diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svl/source/svdde | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svl/source/svdde')
-rw-r--r-- | svl/source/svdde/ddecli.cxx | 387 | ||||
-rw-r--r-- | svl/source/svdde/ddedata.cxx | 167 | ||||
-rw-r--r-- | svl/source/svdde/ddeimp.hxx | 117 | ||||
-rw-r--r-- | svl/source/svdde/ddestrg.cxx | 47 | ||||
-rw-r--r-- | svl/source/svdde/ddesvr.cxx | 857 |
5 files changed, 1575 insertions, 0 deletions
diff --git a/svl/source/svdde/ddecli.cxx b/svl/source/svdde/ddecli.cxx new file mode 100644 index 000000000..852d7db9e --- /dev/null +++ b/svl/source/svdde/ddecli.cxx @@ -0,0 +1,387 @@ +/* -*- 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 <string.h> +#include <algorithm> +#include "ddeimp.hxx" +#include <svl/svdde.hxx> +#include <osl/thread.h> +#include <comphelper/solarmutex.hxx> + +namespace { + +DdeInstData * theDdeInstData; + +} + +DdeInstData* ImpGetInstData() +{ + return theDdeInstData; +} + +DdeInstData* ImpInitInstData() +{ + theDdeInstData = new DdeInstData; + return theDdeInstData; +} + +void ImpDeinitInstData() +{ + delete theDdeInstData; + theDdeInstData = nullptr; +} + + +struct DdeImp +{ + HCONV hConv; + UINT nStatus; +}; + +HDDEDATA CALLBACK DdeInternal::CliCallback( UINT nCode, UINT nCbType, + HCONV hConv, HSZ, HSZ hText2, + HDDEDATA hData, ULONG_PTR nInfo1, ULONG_PTR ) +{ + HDDEDATA nRet = DDE_FNOTPROCESSED; + const std::vector<DdeConnection*> &rAll = DdeConnection::GetConnections(); + DdeConnection* self = nullptr; + + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + + for ( size_t i = 0; i < rAll.size(); ++i) + { + self = rAll[i]; + + if ( self->pImp->hConv == hConv ) + break; + } + + if( self ) + { + bool bFound = false; + std::vector<DdeTransaction*>::iterator iter; + for( iter = self->aTransactions.begin(); iter != self->aTransactions.end(); ++iter ) + { + switch( nCode ) + { + case XTYP_XACT_COMPLETE: + if( static_cast<DWORD>((*iter)->nId) == nInfo1 ) + { + nCode = (*iter)->nType & (XCLASS_MASK | XTYP_MASK); + (*iter)->bBusy = false; + (*iter)->Done( nullptr != hData ); + bFound = true; + } + break; + + case XTYP_DISCONNECT: + self->pImp->hConv = DdeReconnect( hConv ); + self->pImp->nStatus = self->pImp->hConv + ? DMLERR_NO_ERROR + : DdeGetLastError( pInst->hDdeInstCli ); + iter = self->aTransactions.end(); + nRet = nullptr; + bFound = true; + break; + + case XTYP_ADVDATA: + bFound = *(*iter)->pName == hText2; + break; + } + if( bFound ) + break; + } + + if( iter != self->aTransactions.end() ) + { + switch( nCode ) + { + case XTYP_ADVDATA: + if( !hData ) + { + static_cast<DdeLink*>(*iter)->Notify(); + nRet = reinterpret_cast<HDDEDATA>(DDE_FACK); + break; + } + [[fallthrough]]; + + case XTYP_REQUEST: + DdeData d; + d.xImp->hData = hData; + d.xImp->nFmt = DdeData::GetInternalFormat( nCbType ); + d.Lock(); + (*iter)->Data( &d ); + nRet = reinterpret_cast<HDDEDATA>(DDE_FACK); + break; + } + } + } + return nRet; +} + +DdeConnection::DdeConnection( const OUString& rService, const OUString& rTopic ): + pImp(std::make_unique<DdeImp>()) +{ + pImp->nStatus = DMLERR_NO_ERROR; + pImp->hConv = nullptr; + + DdeInstData* pInst = ImpGetInstData(); + if( !pInst ) + pInst = ImpInitInstData(); + pInst->nRefCount++; + pInst->nInstanceCli++; + if ( !pInst->hDdeInstCli ) + { + pImp->nStatus = DdeInitializeW( &pInst->hDdeInstCli, + DdeInternal::CliCallback, + APPCLASS_STANDARD | APPCMD_CLIENTONLY | + CBF_FAIL_ALLSVRXACTIONS | + CBF_SKIP_REGISTRATIONS | + CBF_SKIP_UNREGISTRATIONS, 0L ); + } + + pService = new DdeString( pInst->hDdeInstCli, rService ); + pTopic = new DdeString( pInst->hDdeInstCli, rTopic ); + + if ( pImp->nStatus == DMLERR_NO_ERROR ) + { + pImp->hConv = DdeConnect( pInst->hDdeInstCli,pService->getHSZ(),pTopic->getHSZ(), nullptr); + if( !pImp->hConv ) + pImp->nStatus = DdeGetLastError( pInst->hDdeInstCli ); + } + + pInst->aConnections.push_back( this ); +} + +DdeConnection::~DdeConnection() +{ + if ( pImp->hConv ) + DdeDisconnect( pImp->hConv ); + + delete pService; + delete pTopic; + + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + + std::vector<DdeConnection*>::iterator it(std::find(pInst->aConnections.begin(), + pInst->aConnections.end(), + this)); + if (it != pInst->aConnections.end()) + pInst->aConnections.erase(it); + + pInst->nInstanceCli--; + pInst->nRefCount--; + if ( !pInst->nInstanceCli && pInst->hDdeInstCli ) + { + if( DdeUninitialize( pInst->hDdeInstCli ) ) + { + pInst->hDdeInstCli = 0; + if( pInst->nRefCount == 0 ) + ImpDeinitInstData(); + } + } +} + +bool DdeConnection::IsConnected() +{ + CONVINFO c; + c.cb = sizeof( c ); + if ( DdeQueryConvInfo( pImp->hConv, QID_SYNC, &c ) ) + return true; + else + { + DdeInstData* pInst = ImpGetInstData(); + pImp->hConv = DdeReconnect( pImp->hConv ); + pImp->nStatus = pImp->hConv ? DMLERR_NO_ERROR : DdeGetLastError( pInst->hDdeInstCli ); + return pImp->nStatus == DMLERR_NO_ERROR; + } +} + +OUString DdeConnection::GetServiceName() const +{ + return pService->toOUString(); +} + + OUString DdeConnection::GetTopicName() const +{ + return pTopic->toOUString(); +} + +const std::vector<DdeConnection*>& DdeConnection::GetConnections() +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + return pInst->aConnections; +} + +DdeTransaction::DdeTransaction( DdeConnection& d, const OUString& rItemName, + tools::Long n ) + : rDde( d ) +{ + DdeInstData* pInst = ImpGetInstData(); + pName = new DdeString( pInst->hDdeInstCli, rItemName ); + nTime = n; + nId = 0; + nType = 0; + bBusy = false; + + rDde.aTransactions.push_back( this ); +} + +DdeTransaction::~DdeTransaction() +{ + if ( nId && rDde.pImp->hConv ) + { + DdeInstData* pInst = ImpGetInstData(); + DdeAbandonTransaction( pInst->hDdeInstCli, rDde.pImp->hConv, nId ); + } + + delete pName; + rDde.aTransactions.erase(std::remove(rDde.aTransactions.begin(), + rDde.aTransactions.end(),this)); +} + +void DdeTransaction::Execute() +{ + HSZ hItem = pName->getHSZ(); + void const * pData = aDdeData.getData(); + DWORD nData = static_cast<DWORD>(aDdeData.getSize()); + SotClipboardFormatId nIntFmt = aDdeData.xImp->nFmt; + UINT nExtFmt = DdeData::GetExternalFormat( nIntFmt ); + DdeInstData* pInst = ImpGetInstData(); + + if ( nType == XTYP_EXECUTE ) + hItem = nullptr; + if ( nType != XTYP_EXECUTE && nType != XTYP_POKE ) + { + pData = nullptr; + nData = 0; + } + if ( nTime ) + { + HDDEDATA hData = DdeClientTransaction( static_cast<LPBYTE>(const_cast<void *>(pData)), + nData, rDde.pImp->hConv, + hItem, nExtFmt, static_cast<UINT>(nType), + static_cast<DWORD>(nTime), nullptr ); + + rDde.pImp->nStatus = DdeGetLastError( pInst->hDdeInstCli ); + if( hData && nType == XTYP_REQUEST ) + { + { + DdeData d; + d.xImp->hData = hData; + d.xImp->nFmt = nIntFmt; + d.Lock(); + Data( &d ); + } + DdeFreeDataHandle( hData ); + } + } + else + { + if ( nId && rDde.pImp->hConv ) + DdeAbandonTransaction( pInst->hDdeInstCli, rDde.pImp->hConv, nId); + nId = 0; + bBusy = true; + DWORD result; + HDDEDATA hRet = DdeClientTransaction( static_cast<LPBYTE>(const_cast<void *>(pData)), nData, + rDde.pImp->hConv, hItem, nExtFmt, + static_cast<UINT>(nType), TIMEOUT_ASYNC, + &result ); + nId = result; + rDde.pImp->nStatus = hRet ? DMLERR_NO_ERROR + : DdeGetLastError( pInst->hDdeInstCli ); + } +} + +OUString DdeTransaction::GetName() const +{ + return pName->toOUString(); +} + +void DdeTransaction::Data( const DdeData* p ) +{ + comphelper::SolarMutex *pSolarMutex = comphelper::SolarMutex::get(); + if ( pSolarMutex ) + { + pSolarMutex->acquire(); + aData.Call( p ); + pSolarMutex = comphelper::SolarMutex::get(); + if ( pSolarMutex ) + pSolarMutex->release(); + } +} + +void DdeTransaction::Done( bool bDataValid ) +{ + aDone.Call( bDataValid ); +} + +DdeLink::DdeLink( DdeConnection& d, const OUString& aItemName, tools::Long n ) + : DdeTransaction (d, aItemName, n) +{ +} + +DdeLink::~DdeLink() +{ + nType = sal_uInt16(XTYP_ADVSTOP); + nTime = 0; +} + +void DdeLink::Notify() +{ + aNotify.Call( nullptr ); +} + +DdeRequest::DdeRequest( DdeConnection& d, const OUString& i, tools::Long n ) + : DdeTransaction( d, i, n ) +{ + nType = XTYP_REQUEST; +} + +DdeHotLink::DdeHotLink( DdeConnection& d, const OUString& i ) + : DdeLink( d, i, 0 ) +{ + nType = XTYP_ADVSTART; +} + +DdePoke::DdePoke( DdeConnection& d, const OUString& i, const DdeData& rData, + tools::Long n ) + : DdeTransaction( d, i, n ) +{ + aDdeData = rData; + nType = XTYP_POKE; +} + +DdeExecute::DdeExecute( DdeConnection& d, const OUString& rData, tools::Long n ) + : DdeTransaction( d, OUString(), n ) +{ + aDdeData = DdeData( rData.getStr(), sizeof(sal_Unicode) * (rData.getLength() + 1), SotClipboardFormatId::STRING ); + nType = XTYP_EXECUTE; +} + +tools::Long DdeConnection::GetError() const +{ + return pImp->nStatus; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svl/source/svdde/ddedata.cxx b/svl/source/svdde/ddedata.cxx new file mode 100644 index 000000000..d8e1e1579 --- /dev/null +++ b/svl/source/svdde/ddedata.cxx @@ -0,0 +1,167 @@ +/* -*- 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 . + */ + + +// ATTENTION: We assume StarView Clipboard format numbers and Windows +// Format numbers to be the same! If that's not the case, we need to +// adapt the code here. The implementation uses the conversions here. + +#include <string.h> +#include "ddeimp.hxx" +#include <svl/svdde.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <osl/thread.h> +#include <sot/exchange.hxx> + +DdeData::DdeData() +{ + xImp.reset(new DdeDataImp); + xImp->hData = nullptr; + xImp->nData = 0; + xImp->pData = nullptr; + xImp->nFmt = SotClipboardFormatId::STRING; +} + +DdeData::DdeData(const void* p, tools::Long n, SotClipboardFormatId f) +{ + xImp.reset(new DdeDataImp); + xImp->hData = nullptr; + xImp->pData = p; + xImp->nData = n; + xImp->nFmt = f; +} + +DdeData::DdeData( const OUString& s ) +{ + xImp.reset(new DdeDataImp); + xImp->hData = nullptr; + xImp->pData = s.getStr(); + xImp->nData = s.getLength()+1; + xImp->nFmt = SotClipboardFormatId::STRING; +} + +DdeData::DdeData(const DdeData& rData) +{ + xImp.reset(new DdeDataImp); + xImp->hData = rData.xImp->hData; + xImp->nData = rData.xImp->nData; + xImp->pData = rData.xImp->pData; + xImp->nFmt = rData.xImp->nFmt; + Lock(); +} + +DdeData::DdeData(DdeData&& rData) noexcept + : xImp(std::move(rData.xImp)) +{ +} + +DdeData::~DdeData() +{ + if (xImp && xImp->hData) + DdeUnaccessData(xImp->hData); +} + +void DdeData::Lock() +{ + if (xImp->hData) + xImp->pData = DdeAccessData(xImp->hData, &xImp->nData); +} + +SotClipboardFormatId DdeData::GetFormat() const +{ + return xImp->nFmt; +} + +void DdeData::SetFormat(SotClipboardFormatId nFmt) +{ + xImp->nFmt = nFmt; +} + +void const * DdeData::getData() const +{ + return xImp->pData; +} + +tools::Long DdeData::getSize() const +{ + return xImp->nData; +} + +DdeData& DdeData::operator=(const DdeData& rData) +{ + if ( &rData != this ) + { + DdeData tmp(rData); + xImp = std::move(tmp.xImp); + } + + return *this; +} + +DdeData& DdeData::operator=(DdeData&& rData) noexcept +{ + xImp = std::move(rData.xImp); + return *this; +} + +sal_uInt32 DdeData::GetExternalFormat(SotClipboardFormatId nFmt) +{ + switch( nFmt ) + { + case SotClipboardFormatId::STRING: + return CF_TEXT; + case SotClipboardFormatId::BITMAP: + return CF_BITMAP; + case SotClipboardFormatId::GDIMETAFILE: + return CF_METAFILEPICT; + default: + { + OUString aName( SotExchange::GetFormatName( nFmt ) ); + if( !aName.isEmpty() ) + return RegisterClipboardFormatW( o3tl::toW(aName.getStr()) ); + } + } + return static_cast<sal_uInt32>(nFmt); +} + +SotClipboardFormatId DdeData::GetInternalFormat(sal_uLong nFmt) +{ + switch( nFmt ) + { + case CF_TEXT: + return SotClipboardFormatId::STRING; + case CF_BITMAP: + return SotClipboardFormatId::BITMAP; + case CF_METAFILEPICT: + return SotClipboardFormatId::GDIMETAFILE; + default: + if( nFmt >= CF_MAX ) + { + WCHAR szName[ 256 ]; + + if(GetClipboardFormatNameW( nFmt, szName, SAL_N_ELEMENTS(szName) )) + return SotExchange::RegisterFormatName( OUString(o3tl::toU(szName)) ); + } + break; + } + return static_cast<SotClipboardFormatId>(nFmt); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svl/source/svdde/ddeimp.hxx b/svl/source/svdde/ddeimp.hxx new file mode 100644 index 000000000..060711bbf --- /dev/null +++ b/svl/source/svdde/ddeimp.hxx @@ -0,0 +1,117 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SVL_SOURCE_SVDDE_DDEIMP_HXX +#define INCLUDED_SVL_SOURCE_SVDDE_DDEIMP_HXX + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ddeml.h> + +#include <rtl/ustring.hxx> +#include <svl/svdde.hxx> +#include <vector> + + +struct Conversation +{ + HCONV hConv; + DdeTopic* pTopic; +}; + + +class DdeInternal +{ +public: + static HDDEDATA CALLBACK CliCallback + ( UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, ULONG_PTR, ULONG_PTR ); + static HDDEDATA CALLBACK SvrCallback + ( UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, ULONG_PTR, ULONG_PTR ); + static DdeService* FindService( HSZ ); + static DdeTopic* FindTopic( DdeService&, HSZ ); + static DdeItem* FindItem( DdeTopic&, HSZ ); + static void DisconnectTopic(DdeTopic &, HCONV); + static void IncMonitor(DdeItem *pItem, HCONV); + static void DecMonitor(DdeItem *pItem, HCONV); +}; + + +class DdeString +{ +private: + OUString m_aString; +protected: + HSZ hString; + DWORD hInst; + +public: + DdeString( DWORD, const OUString& ); + ~DdeString(); + + bool operator==( HSZ ) const; + HSZ getHSZ(); + const OUString & toOUString() const { return m_aString; } +}; + + +struct DdeDataImp +{ + HDDEDATA hData; + void const * pData; + DWORD nData; + SotClipboardFormatId nFmt; +}; + +class DdeConnection; + +class DdeInstData +{ +public: + sal_uInt16 nRefCount; + std::vector<DdeConnection*> aConnections; + // Server + DWORD hDdeInstSvr; + short nInstanceSvr; + DdeServices* pServicesSvr; + // Client + DWORD hDdeInstCli; + short nInstanceCli; + + DdeInstData() + : nRefCount(0) + , hDdeInstSvr(0) + , nInstanceSvr(0) + , pServicesSvr(nullptr) + , hDdeInstCli(0) + , nInstanceCli(0) + { + } + DdeInstData(const DdeInstData&) = delete; + DdeInstData& operator=(const DdeInstData&) = delete; +}; + +DdeInstData* ImpGetInstData(); +DdeInstData* ImpInitInstData(); +void ImpDeinitInstData(); + +#endif // INCLUDED_SVL_SOURCE_SVDDE_DDEIMP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svl/source/svdde/ddestrg.cxx b/svl/source/svdde/ddestrg.cxx new file mode 100644 index 000000000..6ddade45b --- /dev/null +++ b/svl/source/svdde/ddestrg.cxx @@ -0,0 +1,47 @@ +/* -*- 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 "ddeimp.hxx" +#include <svl/svdde.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +DdeString::DdeString( DWORD hDdeInst, const OUString& r) + : m_aString(r), hString(DdeCreateStringHandleW( hDdeInst, o3tl::toW(r.getStr()), CP_WINUNICODE )), + hInst(hDdeInst) +{ +} + +DdeString::~DdeString() +{ + if ( hString ) + DdeFreeStringHandle( hInst, hString ); +} + +bool DdeString::operator==( HSZ h ) const +{ + return( !DdeCmpStringHandles( hString, h ) ); +} + +HSZ DdeString::getHSZ() +{ + return hString; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svl/source/svdde/ddesvr.cxx b/svl/source/svdde/ddesvr.cxx new file mode 100644 index 000000000..cff09ed68 --- /dev/null +++ b/svl/source/svdde/ddesvr.cxx @@ -0,0 +1,857 @@ +/* -*- 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 "ddeimp.hxx" +#include <algorithm> +#include <memory> +#include <comphelper/string.hxx> +#include <rtl/ustring.hxx> +#include <svl/svdde.hxx> +#include <osl/thread.h> +#include <o3tl/sorted_vector.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +namespace { + +enum DdeItemType +{ + DDEITEM, + DDEGETPUTITEM +}; + +} + +struct DdeItemImpData +{ + HCONV nHCnv; + sal_uInt16 nCnt; + + explicit DdeItemImpData( HCONV nH ) : nHCnv( nH ), nCnt( 1 ) {} +}; + +HDDEDATA CALLBACK DdeInternal::SvrCallback( + UINT nCode, UINT nCbType, HCONV hConv, HSZ hText1, HSZ hText2, + HDDEDATA hData, ULONG_PTR, ULONG_PTR ) +{ + DdeServices& rAll = DdeService::GetServices(); + DdeService* pService; + DdeTopic* pTopic; + DdeItem* pItem; + DdeData* pData; + Conversation* pC; + + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + + switch( nCode ) + { + case XTYP_WILDCONNECT: + { + int nTopics = 0; + + WCHAR chTopicBuf[250]; + if( hText1 ) + DdeQueryStringW( pInst->hDdeInstSvr, hText1, chTopicBuf, + SAL_N_ELEMENTS(chTopicBuf), CP_WINUNICODE ); + + for (auto& rpService : rAll) + { + pService = rpService; + if ( !hText2 || ( *pService->pName == hText2 ) ) + { + OUString sTopics( pService->Topics() ); + if (!sTopics.isEmpty()) + { + if( hText1 ) + { + sal_Int32 n = 0; + while( -1 != n ) + { + OUString s( sTopics.getToken( 0, '\t', n )); + if( s == o3tl::toU(chTopicBuf) ) + ++nTopics; + } + } + else + nTopics += comphelper::string::getTokenCount(sTopics, '\t'); + } + } + } + + if( !nTopics ) + return nullptr; + + auto pPairs = std::make_unique<HSZPAIR[]>(nTopics + 1); + + HSZPAIR* q = pPairs.get(); + for (auto& rpService : rAll) + { + pService = rpService; + if ( !hText2 || (*pService->pName == hText2 ) ) + { + OUString sTopics( pService->Topics() ); + sal_Int32 n = 0; + while( -1 != n ) + { + OUString s( sTopics.getToken( 0, '\t', n )); + s = s.replaceAll("\n", "").replaceAll("\r", ""); + if( !hText1 || s == o3tl::toU(chTopicBuf) ) + { + DdeString aDStr( pInst->hDdeInstSvr, s ); + pTopic = FindTopic( *pService, aDStr.getHSZ() ); + if( pTopic ) + { + q->hszSvc = pService->pName->getHSZ(); + q->hszTopic = pTopic->pName->getHSZ(); + q++; + } + } + } + } + } + + q->hszSvc = nullptr; + q->hszTopic = nullptr; + HDDEDATA h = DdeCreateDataHandle( + pInst->hDdeInstSvr, + reinterpret_cast<LPBYTE>(pPairs.get()), + sizeof(HSZPAIR) * (nTopics+1), + 0, nullptr, nCbType, 0); + return h; + } + + case XTYP_CONNECT: + pService = FindService( hText2 ); + if ( pService) + pTopic = FindTopic( *pService, hText1 ); + else + pTopic = nullptr; + if ( pTopic ) + return reinterpret_cast<HDDEDATA>(DDE_FACK); + else + return nullptr; + + case XTYP_CONNECT_CONFIRM: + pService = FindService( hText2 ); + if ( pService ) + { + pTopic = FindTopic( *pService, hText1 ); + if ( pTopic ) + { + pC = new Conversation; + pC->hConv = hConv; + pC->pTopic = pTopic; + pService->m_vConv.emplace_back( pC ); + } + } + return nullptr; + } + + for (auto& rpService : rAll) + { + pService = rpService; + for ( size_t i = 0, n = pService->m_vConv.size(); i < n; ++i ) + { + pC = pService->m_vConv[ i ].get(); + if ( pC->hConv == hConv ) + goto found; + } + } + + return reinterpret_cast<HDDEDATA>(DDE_FNOTPROCESSED); + +found: + if ( nCode == XTYP_DISCONNECT) + { + DisconnectTopic(*pC->pTopic, hConv); + auto it = std::find_if(pService->m_vConv.begin(), pService->m_vConv.end(), + [&pC](const std::unique_ptr<Conversation>& rxConv) { return rxConv.get() == pC; }); + if (it != pService->m_vConv.end()) + pService->m_vConv.erase( it ); + return nullptr; + } + + bool bExec = nCode == XTYP_EXECUTE; + pTopic = pC->pTopic; + if ( pTopic && !bExec ) + pItem = FindItem( *pTopic, hText2 ); + else + pItem = nullptr; + + if ( !bExec && !pService->HasCbFormat( nCbType ) ) + pItem = nullptr; + if ( !pItem && !bExec ) + return static_cast<HDDEDATA>(DDE_FNOTPROCESSED); + if ( pItem ) + pTopic->aItem = pItem->GetName(); + else + pTopic->aItem.clear(); + + bool bRes = false; + switch( nCode ) + { + case XTYP_REQUEST: + case XTYP_ADVREQ: + { + OUString aRes; // Must be free not until the end! + if ( pTopic->IsSystemTopic() ) + { + if ( pTopic->aItem == SZDDESYS_ITEM_TOPICS ) + aRes = pService->Topics(); + else if ( pTopic->aItem == SZDDESYS_ITEM_SYSITEMS ) + aRes = pService->SysItems(); + else if ( pTopic->aItem == SZDDESYS_ITEM_STATUS ) + aRes = pService->Status(); + else if ( pTopic->aItem == SZDDESYS_ITEM_FORMATS ) + aRes = pService->Formats(); + else if ( pTopic->aItem == SZDDESYS_ITEM_HELP ) + aRes = OUString(); + else + aRes = OUString(); + + if ( !aRes.isEmpty() ) + pData = new DdeData( aRes ); + else + pData = nullptr; + } + else if( DDEGETPUTITEM == pItem->nType ) + { + pData = static_cast<DdeGetPutItem*>(pItem)->Get( DdeData::GetInternalFormat( nCbType ) ); + } + else + { + pData = pTopic->Get( DdeData::GetInternalFormat( nCbType )); + } + + if ( pData ) + { + return DdeCreateDataHandle( pInst->hDdeInstSvr, + static_cast<LPBYTE>(const_cast<void *>(pData->xImp->pData)), + pData->xImp->nData, + 0, hText2, + DdeData::GetExternalFormat( + pData->xImp->nFmt ), + 0 ); + } + } + break; + + case XTYP_POKE: + if ( !pTopic->IsSystemTopic() ) + { + DdeData d; + d.xImp->hData = hData; + d.xImp->nFmt = DdeData::GetInternalFormat( nCbType ); + d.Lock(); + if( DDEGETPUTITEM == pItem->nType ) + bRes = static_cast<DdeGetPutItem*>(pItem)->Put( &d ); + else + bRes = pTopic->Put( &d ); + } + if ( bRes ) + return reinterpret_cast<HDDEDATA>(DDE_FACK); + else + return reinterpret_cast<HDDEDATA>(DDE_FNOTPROCESSED); + + case XTYP_ADVSTART: + { + // Is the Item turning into a HotLink for the first time? + if( !pItem->pImpData && pTopic->StartAdviseLoop() ) + { + // Then the Item has been exchanged + std::vector<DdeItem*>::iterator it(std::find(pTopic->aItems.begin(), + pTopic->aItems.end(), + pItem)); + if (it != pTopic->aItems.end()) + pTopic->aItems.erase(it); + + std::vector<DdeItem*>::iterator iter; + iter = std::find_if(pTopic->aItems.begin(), pTopic->aItems.end(), + [&hText2](const DdeItem* pDdeItem) { return *pDdeItem->pName == hText2; }); + if (iter != pTopic->aItems.end()) + { + // It was exchanged indeed + delete pItem; + pItem = nullptr; + } + + if( pItem ) + // It was not exchange, so back in + pTopic->aItems.push_back(pItem); + else + pItem = iter != pTopic->aItems.end() ? *iter : nullptr; + } + + if (pItem) + { + IncMonitor(pItem, hConv); + } + } + return reinterpret_cast<HDDEDATA>(TRUE); + + case XTYP_ADVSTOP: + DecMonitor(pItem, hConv); + return reinterpret_cast<HDDEDATA>(TRUE); + + case XTYP_EXECUTE: + { + DdeData aExec; + aExec.xImp->hData = hData; + aExec.xImp->nFmt = DdeData::GetInternalFormat( nCbType ); + aExec.Lock(); + OUString aName; + + aName = static_cast<const sal_Unicode *>(aExec.xImp->pData); + + if( pTopic->IsSystemTopic() ) + bRes = false; + else + bRes = pTopic->Execute( &aName ); + } + if ( bRes ) + return reinterpret_cast<HDDEDATA>(DDE_FACK); + else + return reinterpret_cast<HDDEDATA>(DDE_FNOTPROCESSED); + } + + return nullptr; +} + +DdeService* DdeInternal::FindService( HSZ hService ) +{ + DdeServices& rSvc = DdeService::GetServices(); + auto aI = std::find_if(rSvc.begin(), rSvc.end(), + [&hService](const DdeService* s) { return *s->pName == hService; }); + if (aI != rSvc.end()) + return *aI; + + return nullptr; +} + +DdeTopic* DdeInternal::FindTopic( DdeService& rService, HSZ hTopic ) +{ + std::vector<DdeTopic*> &rTopics = rService.aTopics; + + auto iter = std::find_if(rTopics.begin(), rTopics.end(), + [&hTopic](const DdeTopic* pTopic) { return *pTopic->pName == hTopic; }); + if (iter != rTopics.end()) + return *iter; + + return nullptr; +} + +DdeItem* DdeInternal::FindItem( DdeTopic& rTopic, HSZ hItem ) +{ + std::vector<DdeItem*>::iterator iter; + std::vector<DdeItem*> &rItems = rTopic.aItems; + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + bool bContinue = false; + + do + { // middle check loop + iter = std::find_if(rItems.begin(), rItems.end(), + [&hItem](const DdeItem* pItem) { return *pItem->pName == hItem; }); + if (iter != rItems.end()) + return *iter; + bContinue = !bContinue; + if( !bContinue ) + break; + + // Let's query our subclass + WCHAR chBuf[250]; + DdeQueryStringW(pInst->hDdeInstSvr,hItem,chBuf,SAL_N_ELEMENTS(chBuf),CP_WINUNICODE ); + bContinue = rTopic.MakeItem( OUString(o3tl::toU(chBuf)) ); + // We need to search again + } + while( bContinue ); + + return nullptr; +} + +DdeService::DdeService( const OUString& rService ) +{ + DdeInstData* pInst = ImpGetInstData(); + if( !pInst ) + pInst = ImpInitInstData(); + pInst->nRefCount++; + pInst->nInstanceSvr++; + + if ( !pInst->hDdeInstSvr ) + { + nStatus = sal::static_int_cast< short >( + DdeInitializeW( &pInst->hDdeInstSvr, + DdeInternal::SvrCallback, + APPCLASS_STANDARD | + CBF_SKIP_REGISTRATIONS | + CBF_SKIP_UNREGISTRATIONS, 0 ) ); + pInst->pServicesSvr = new DdeServices; + } + else + nStatus = DMLERR_NO_ERROR; + + if ( pInst->pServicesSvr ) + pInst->pServicesSvr->push_back( this ); + + pName = new DdeString( pInst->hDdeInstSvr, rService ); + if ( nStatus == DMLERR_NO_ERROR ) + { + if ( !DdeNameService( pInst->hDdeInstSvr, pName->getHSZ(), nullptr, + DNS_REGISTER | DNS_FILTEROFF ) ) + { + nStatus = DMLERR_SYS_ERROR; + } + } + AddFormat( SotClipboardFormatId::STRING ); + pSysTopic = new DdeTopic( SZDDESYS_TOPIC ); + pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_TOPICS ) ); + pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_SYSITEMS ) ); + pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_STATUS ) ); + pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_FORMATS ) ); + pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_HELP ) ); + AddTopic( *pSysTopic ); +} + +DdeService::~DdeService() +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + if ( pInst->pServicesSvr ) + pInst->pServicesSvr->erase(std::remove(pInst->pServicesSvr->begin(), pInst->pServicesSvr->end(), this), pInst->pServicesSvr->end()); + + delete pSysTopic; + delete pName; + + pInst->nInstanceSvr--; + pInst->nRefCount--; + if ( !pInst->nInstanceSvr && pInst->hDdeInstSvr ) + { + if( DdeUninitialize( pInst->hDdeInstSvr ) ) + { + pInst->hDdeInstSvr = 0; + delete pInst->pServicesSvr; + pInst->pServicesSvr = nullptr; + if( pInst->nRefCount == 0) + ImpDeinitInstData(); + } + } +} + +OUString DdeService::GetName() const +{ + return pName->toOUString(); +} + +DdeServices& DdeService::GetServices() +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + return *(pInst->pServicesSvr); +} + +void DdeService::AddTopic( const DdeTopic& rTopic ) +{ + RemoveTopic( rTopic ); + aTopics.push_back(const_cast<DdeTopic *>(&rTopic)); +} + +void DdeService::RemoveTopic( const DdeTopic& rTopic ) +{ + auto iter = std::find_if(aTopics.begin(), aTopics.end(), + [&rTopic](const DdeTopic* pTopic) { return DdeCmpStringHandles(pTopic->pName->getHSZ(), rTopic.pName->getHSZ()) == 0; }); + if (iter != aTopics.end()) + { + aTopics.erase(iter); + // Delete all conversions! + // Or else we work on deleted topics! + for( size_t n = m_vConv.size(); n; ) + { + auto const& pC = m_vConv[ --n ]; + if( pC->pTopic == &rTopic ) + m_vConv.erase( m_vConv.begin() + n ); + } + } +} + +bool DdeService::HasCbFormat( sal_uInt32 nFmt ) +{ + return std::find(aFormats.begin(), aFormats.end(), nFmt) != aFormats.end(); +} + +bool DdeService::HasFormat(SotClipboardFormatId nFmt) +{ + return HasCbFormat( DdeData::GetExternalFormat( nFmt )); +} + +void DdeService::AddFormat(SotClipboardFormatId nFmt) +{ + sal_uInt32 nExternalFmt = DdeData::GetExternalFormat( nFmt ); + if (HasCbFormat(nExternalFmt)) + return; + aFormats.push_back( nExternalFmt ); +} + +void DdeService::RemoveFormat(SotClipboardFormatId nFmt) +{ + sal_uInt32 nExternalFmt = DdeData::GetExternalFormat( nFmt ); + auto it = std::find(aFormats.begin(), aFormats.end(), nExternalFmt); + if (it != aFormats.end()) + aFormats.erase( it ); +} + +DdeTopic::DdeTopic( const OUString& rName ) +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + pName = new DdeString( pInst->hDdeInstSvr, rName ); +} + +DdeTopic::~DdeTopic() +{ + for (auto& rpItem : aItems) + { + rpItem->pMyTopic = nullptr; + delete rpItem; + } + + delete pName; +} + +OUString DdeTopic::GetName() const +{ + return pName->toOUString(); +} + +bool DdeTopic::IsSystemTopic() +{ + return GetName() == SZDDESYS_TOPIC; +} + +DdeItem* DdeTopic::AddItem( const DdeItem& r ) +{ + DdeItem* s; + if( DDEGETPUTITEM == r.nType ) + s = new DdeGetPutItem( r ); + else + s = new DdeItem( r ); + + aItems.push_back( s ); + s->pMyTopic = this; + return s; +} + +void DdeTopic::InsertItem( DdeItem* pNew ) +{ + if( pNew ) + { + aItems.push_back( pNew ); + pNew->pMyTopic = this; + } +} + +void DdeTopic::RemoveItem( const DdeItem& r ) +{ + auto iter = std::find_if(aItems.begin(), aItems.end(), + [&r](const DdeItem* pItem) { return DdeCmpStringHandles(pItem->pName->getHSZ(), r.pName->getHSZ()) == 0; }); + + if ( iter != aItems.end() ) + { + (*iter)->pMyTopic = nullptr; + delete *iter; + aItems.erase(iter); + } +} + +void DdeTopic::NotifyClient( const OUString& rItem ) +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + auto iter = std::find_if(aItems.begin(), aItems.end(), + [&rItem](const DdeItem* pItem) { return pItem->GetName().equals(rItem) && pItem->pImpData; }); + if (iter != aItems.end()) + DdePostAdvise( pInst->hDdeInstSvr, pName->getHSZ(), (*iter)->pName->getHSZ() ); +} + +void DdeInternal::DisconnectTopic(DdeTopic & rTopic, HCONV nId) +{ + for (const auto& rpItem : rTopic.aItems) + { + DecMonitor(rpItem, nId); + } +} + +DdeData* DdeTopic::Get(SotClipboardFormatId /*nFmt*/) +{ + return nullptr; +} + +bool DdeTopic::Put( const DdeData* ) +{ + return false; +} + +bool DdeTopic::Execute( const OUString* ) +{ + return false; +} + +bool DdeTopic::StartAdviseLoop() +{ + return false; +} + +DdeItem::DdeItem( const sal_Unicode* p ) +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + pName = new DdeString( pInst->hDdeInstSvr, OUString(p) ); + nType = DDEITEM; + pMyTopic = nullptr; + pImpData = nullptr; +} + +DdeItem::DdeItem( const OUString& r) +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + pName = new DdeString( pInst->hDdeInstSvr, r ); + nType = DDEITEM; + pMyTopic = nullptr; + pImpData = nullptr; +} + +DdeItem::DdeItem( const DdeItem& r) +{ + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + pName = new DdeString( pInst->hDdeInstSvr, r.pName->toOUString() ); + nType = DDEITEM; + pMyTopic = nullptr; + pImpData = nullptr; +} + +DdeItem::~DdeItem() +{ + if( pMyTopic ) + pMyTopic->aItems.erase(std::remove(pMyTopic->aItems.begin(), + pMyTopic->aItems.end(),this)); + delete pName; + delete pImpData; +} + +OUString DdeItem::GetName() const +{ + return pName->toOUString(); +} + +void DdeItem::NotifyClient() +{ + if( pMyTopic && pImpData ) + { + DdeInstData* pInst = ImpGetInstData(); + assert(pInst); + DdePostAdvise( pInst->hDdeInstSvr, pMyTopic->pName->getHSZ(), pName->getHSZ() ); + } +} + +void DdeInternal::IncMonitor(DdeItem *const pItem, HCONV nHCnv) +{ + if (!pItem->pImpData) + { + pItem->pImpData = new std::vector<DdeItemImpData>; + if (DDEGETPUTITEM == pItem->nType) + { + static_cast<DdeGetPutItem*>(pItem)->AdviseLoop( true ); + } + } + else + { + for (size_t n = pItem->pImpData->size(); n; ) + { + if ((*pItem->pImpData)[ --n ].nHCnv == nHCnv) + { + ++(*pItem->pImpData)[ n ].nHCnv; + return ; + } + } + } + + pItem->pImpData->push_back( DdeItemImpData( nHCnv ) ); +} + +void DdeInternal::DecMonitor(DdeItem *const pItem, HCONV nHCnv) +{ + if (pItem->pImpData) + { + for( size_t n = 0; n < pItem->pImpData->size(); ++n ) + { + DdeItemImpData* pData = &(*pItem->pImpData)[n]; + if( pData->nHCnv == nHCnv ) + { + if( !pData->nCnt || !--pData->nCnt ) + { + if (1 < pItem->pImpData->size()) + { + pItem->pImpData->erase(pItem->pImpData->begin() + n); + } + else + { + delete pItem->pImpData; + pItem->pImpData = nullptr; + if (DDEGETPUTITEM == pItem->nType) + { + static_cast<DdeGetPutItem*>(pItem)->AdviseLoop(false); + } + } + } + return ; + } + } + } +} + +short DdeItem::GetLinks() +{ + short nCnt = 0; + if( pImpData ) + { + for (const auto& rData : *pImpData) + { + nCnt += rData.nCnt; + } + } + return nCnt; +} + +DdeGetPutItem::DdeGetPutItem( const sal_Unicode* p ) + : DdeItem( p ) +{ + nType = DDEGETPUTITEM; +} + +DdeGetPutItem::DdeGetPutItem( const OUString& rStr ) + : DdeItem( rStr ) +{ + nType = DDEGETPUTITEM; +} + +DdeGetPutItem::DdeGetPutItem( const DdeItem& rItem ) + : DdeItem( rItem ) +{ + nType = DDEGETPUTITEM; +} + +DdeData* DdeGetPutItem::Get(SotClipboardFormatId) +{ + return nullptr; +} + +bool DdeGetPutItem::Put( const DdeData* ) +{ + return false; +} + +void DdeGetPutItem::AdviseLoop( bool ) +{ +} + +OUString DdeService::SysItems() +{ + OUString s; + for ( const auto& rpTopic : aTopics ) + { + if ( rpTopic->GetName() == SZDDESYS_TOPIC ) + { + short n = 0; + for ( const auto& rpItem : rpTopic->aItems ) + { + if ( n ) + s += "\t"; + s += rpItem->GetName(); + n++; + } + s += "\r\n"; + } + } + + return s; +} + +OUString DdeService::Topics() +{ + OUString s; + short n = 0; + + for ( const auto& rpTopic : aTopics ) + { + if ( n ) + s += "\t"; + s += rpTopic->GetName(); + n++; + } + s += "\r\n"; + + return s; +} + +OUString DdeService::Formats() +{ + OUString s; + short n = 0; + + for (size_t i = 0; i < aFormats.size(); ++i, ++n) + { + sal_uInt32 f = aFormats[ i ]; + if ( n ) + s += "\t"; + + switch( f ) + { + case CF_TEXT: + s += "TEXT"; + break; + case CF_BITMAP: + s += "BITMAP"; + break; + default: + { + WCHAR buf[128]; + GetClipboardFormatNameW( f, buf, SAL_N_ELEMENTS(buf) ); + s += o3tl::toU(buf); + } + break; + } + + } + s += "\r\n"; + + return s; +} + +OUString DdeService::Status() +{ + return "Ready\r\n"; +} + +bool DdeTopic::MakeItem( const OUString& ) +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |