1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include <comphelper/string.hxx>
24 #include <rtl/ustring.hxx>
25 #include <svl/svdde.hxx>
26 #include <osl/thread.h>
27 #include <o3tl/sorted_vector.hxx>
28 #include <o3tl/char16_t2wchar_t.hxx>
45 explicit DdeItemImpData( HCONV nH
) : nHCnv( nH
), nCnt( 1 ) {}
48 HDDEDATA CALLBACK
DdeInternal::SvrCallback(
49 UINT nCode
, UINT nCbType
, HCONV hConv
, HSZ hText1
, HSZ hText2
,
50 HDDEDATA hData
, ULONG_PTR
, ULONG_PTR
)
52 DdeServices
& rAll
= DdeService::GetServices();
59 DdeInstData
* pInst
= ImpGetInstData();
64 case XTYP_WILDCONNECT
:
68 WCHAR chTopicBuf
[250];
70 DdeQueryStringW( pInst
->hDdeInstSvr
, hText1
, chTopicBuf
,
71 SAL_N_ELEMENTS(chTopicBuf
), CP_WINUNICODE
);
73 for (auto& rpService
: rAll
)
76 if ( !hText2
|| ( *pService
->pName
== hText2
) )
78 OUString
sTopics( pService
->Topics() );
79 if (!sTopics
.isEmpty())
86 OUString
s( sTopics
.getToken( 0, '\t', n
));
87 if( s
== o3tl::toU(chTopicBuf
) )
92 nTopics
+= comphelper::string::getTokenCount(sTopics
, '\t');
100 auto pPairs
= std::make_unique
<HSZPAIR
[]>(nTopics
+ 1);
102 HSZPAIR
* q
= pPairs
.get();
103 for (auto& rpService
: rAll
)
105 pService
= rpService
;
106 if ( !hText2
|| (*pService
->pName
== hText2
) )
108 OUString
sTopics( pService
->Topics() );
112 OUString
s( sTopics
.getToken( 0, '\t', n
));
113 s
= s
.replaceAll("\n", "").replaceAll("\r", "");
114 if( !hText1
|| s
== o3tl::toU(chTopicBuf
) )
116 DdeString
aDStr( pInst
->hDdeInstSvr
, s
);
117 pTopic
= FindTopic( *pService
, aDStr
.getHSZ() );
120 q
->hszSvc
= pService
->pName
->getHSZ();
121 q
->hszTopic
= pTopic
->pName
->getHSZ();
130 q
->hszTopic
= nullptr;
131 HDDEDATA h
= DdeCreateDataHandle(
133 reinterpret_cast<LPBYTE
>(pPairs
.get()),
134 sizeof(HSZPAIR
) * (nTopics
+1),
135 0, nullptr, nCbType
, 0);
140 pService
= FindService( hText2
);
142 pTopic
= FindTopic( *pService
, hText1
);
146 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
150 case XTYP_CONNECT_CONFIRM
:
151 pService
= FindService( hText2
);
154 pTopic
= FindTopic( *pService
, hText1
);
157 pC
= new Conversation
;
160 pService
->m_vConv
.emplace_back( pC
);
166 for (auto& rpService
: rAll
)
168 pService
= rpService
;
169 for ( size_t i
= 0, n
= pService
->m_vConv
.size(); i
< n
; ++i
)
171 pC
= pService
->m_vConv
[ i
].get();
172 if ( pC
->hConv
== hConv
)
177 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
180 if ( nCode
== XTYP_DISCONNECT
)
182 DisconnectTopic(*pC
->pTopic
, hConv
);
183 auto it
= std::find_if(pService
->m_vConv
.begin(), pService
->m_vConv
.end(),
184 [&pC
](const std::unique_ptr
<Conversation
>& rxConv
) { return rxConv
.get() == pC
; });
185 if (it
!= pService
->m_vConv
.end())
186 pService
->m_vConv
.erase( it
);
190 bool bExec
= nCode
== XTYP_EXECUTE
;
192 if ( pTopic
&& !bExec
)
193 pItem
= FindItem( *pTopic
, hText2
);
197 if ( !bExec
&& !pService
->HasCbFormat( nCbType
) )
199 if ( !pItem
&& !bExec
)
200 return static_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
202 pTopic
->aItem
= pItem
->GetName();
204 pTopic
->aItem
.clear();
212 OUString aRes
; // Must be free not until the end!
213 if ( pTopic
->IsSystemTopic() )
215 if ( pTopic
->aItem
== SZDDESYS_ITEM_TOPICS
)
216 aRes
= pService
->Topics();
217 else if ( pTopic
->aItem
== SZDDESYS_ITEM_SYSITEMS
)
218 aRes
= pService
->SysItems();
219 else if ( pTopic
->aItem
== SZDDESYS_ITEM_STATUS
)
220 aRes
= pService
->Status();
221 else if ( pTopic
->aItem
== SZDDESYS_ITEM_FORMATS
)
222 aRes
= pService
->Formats();
223 else if ( pTopic
->aItem
== SZDDESYS_ITEM_HELP
)
228 if ( !aRes
.isEmpty() )
229 pData
= new DdeData( aRes
);
233 else if( DDEGETPUTITEM
== pItem
->nType
)
235 pData
= static_cast<DdeGetPutItem
*>(pItem
)->Get( DdeData::GetInternalFormat( nCbType
) );
239 pData
= pTopic
->Get( DdeData::GetInternalFormat( nCbType
));
244 return DdeCreateDataHandle( pInst
->hDdeInstSvr
,
245 static_cast<LPBYTE
>(const_cast<void *>(pData
->xImp
->pData
)),
248 DdeData::GetExternalFormat(
256 if ( !pTopic
->IsSystemTopic() )
259 d
.xImp
->hData
= hData
;
260 d
.xImp
->nFmt
= DdeData::GetInternalFormat( nCbType
);
262 if( DDEGETPUTITEM
== pItem
->nType
)
263 bRes
= static_cast<DdeGetPutItem
*>(pItem
)->Put( &d
);
265 bRes
= pTopic
->Put( &d
);
268 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
270 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
274 // Is the Item turning into a HotLink for the first time?
275 if( !pItem
->pImpData
&& pTopic
->StartAdviseLoop() )
277 // Then the Item has been exchanged
278 std::vector
<DdeItem
*>::iterator
it(std::find(pTopic
->aItems
.begin(),
279 pTopic
->aItems
.end(),
281 if (it
!= pTopic
->aItems
.end())
282 pTopic
->aItems
.erase(it
);
284 std::vector
<DdeItem
*>::iterator iter
;
285 iter
= std::find_if(pTopic
->aItems
.begin(), pTopic
->aItems
.end(),
286 [&hText2
](const DdeItem
* pDdeItem
) { return *pDdeItem
->pName
== hText2
; });
287 if (iter
!= pTopic
->aItems
.end())
289 // It was exchanged indeed
295 // It was not exchange, so back in
296 pTopic
->aItems
.push_back(pItem
);
298 pItem
= iter
!= pTopic
->aItems
.end() ? *iter
: nullptr;
303 IncMonitor(pItem
, hConv
);
306 return reinterpret_cast<HDDEDATA
>(TRUE
);
309 DecMonitor(pItem
, hConv
);
310 return reinterpret_cast<HDDEDATA
>(TRUE
);
315 aExec
.xImp
->hData
= hData
;
316 aExec
.xImp
->nFmt
= DdeData::GetInternalFormat( nCbType
);
320 aName
= static_cast<const sal_Unicode
*>(aExec
.xImp
->pData
);
322 if( pTopic
->IsSystemTopic() )
325 bRes
= pTopic
->Execute( &aName
);
328 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
330 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
336 DdeService
* DdeInternal::FindService( HSZ hService
)
338 DdeServices
& rSvc
= DdeService::GetServices();
339 auto aI
= std::find_if(rSvc
.begin(), rSvc
.end(),
340 [&hService
](const DdeService
* s
) { return *s
->pName
== hService
; });
341 if (aI
!= rSvc
.end())
347 DdeTopic
* DdeInternal::FindTopic( DdeService
& rService
, HSZ hTopic
)
349 std::vector
<DdeTopic
*> &rTopics
= rService
.aTopics
;
350 DdeInstData
* pInst
= ImpGetInstData();
353 auto iter
= std::find_if(rTopics
.begin(), rTopics
.end(),
354 [&hTopic
](const DdeTopic
* pTopic
) { return *pTopic
->pName
== hTopic
; });
355 if (iter
!= rTopics
.end())
361 DdeItem
* DdeInternal::FindItem( DdeTopic
& rTopic
, HSZ hItem
)
363 std::vector
<DdeItem
*>::iterator iter
;
364 std::vector
<DdeItem
*> &rItems
= rTopic
.aItems
;
365 DdeInstData
* pInst
= ImpGetInstData();
367 bool bContinue
= false;
370 { // middle check loop
371 iter
= std::find_if(rItems
.begin(), rItems
.end(),
372 [&hItem
](const DdeItem
* pItem
) { return *pItem
->pName
== hItem
; });
373 if (iter
!= rItems
.end())
375 bContinue
= !bContinue
;
379 // Let's query our subclass
381 DdeQueryStringW(pInst
->hDdeInstSvr
,hItem
,chBuf
,SAL_N_ELEMENTS(chBuf
),CP_WINUNICODE
);
382 bContinue
= rTopic
.MakeItem( OUString(o3tl::toU(chBuf
)) );
383 // We need to search again
390 DdeService::DdeService( const OUString
& rService
)
392 DdeInstData
* pInst
= ImpGetInstData();
394 pInst
= ImpInitInstData();
396 pInst
->nInstanceSvr
++;
398 if ( !pInst
->hDdeInstSvr
)
400 nStatus
= sal::static_int_cast
< short >(
401 DdeInitializeW( &pInst
->hDdeInstSvr
,
402 DdeInternal::SvrCallback
,
404 CBF_SKIP_REGISTRATIONS
|
405 CBF_SKIP_UNREGISTRATIONS
, 0L ) );
406 pInst
->pServicesSvr
= new DdeServices
;
409 nStatus
= DMLERR_NO_ERROR
;
411 if ( pInst
->pServicesSvr
)
412 pInst
->pServicesSvr
->push_back( this );
414 pName
= new DdeString( pInst
->hDdeInstSvr
, rService
);
415 if ( nStatus
== DMLERR_NO_ERROR
)
417 if ( !DdeNameService( pInst
->hDdeInstSvr
, pName
->getHSZ(), nullptr,
418 DNS_REGISTER
| DNS_FILTEROFF
) )
420 nStatus
= DMLERR_SYS_ERROR
;
423 AddFormat( SotClipboardFormatId::STRING
);
424 pSysTopic
= new DdeTopic( SZDDESYS_TOPIC
);
425 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_TOPICS
) );
426 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_SYSITEMS
) );
427 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_STATUS
) );
428 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_FORMATS
) );
429 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_HELP
) );
430 AddTopic( *pSysTopic
);
433 DdeService::~DdeService()
435 DdeInstData
* pInst
= ImpGetInstData();
437 if ( pInst
->pServicesSvr
)
438 pInst
->pServicesSvr
->erase(std::remove(pInst
->pServicesSvr
->begin(), pInst
->pServicesSvr
->end(), this), pInst
->pServicesSvr
->end());
443 pInst
->nInstanceSvr
--;
445 if ( !pInst
->nInstanceSvr
&& pInst
->hDdeInstSvr
)
447 if( DdeUninitialize( pInst
->hDdeInstSvr
) )
449 pInst
->hDdeInstSvr
= 0;
450 delete pInst
->pServicesSvr
;
451 pInst
->pServicesSvr
= nullptr;
452 if( pInst
->nRefCount
== 0)
458 OUString
DdeService::GetName() const
460 return pName
->toOUString();
463 DdeServices
& DdeService::GetServices()
465 DdeInstData
* pInst
= ImpGetInstData();
467 return *(pInst
->pServicesSvr
);
470 void DdeService::AddTopic( const DdeTopic
& rTopic
)
472 RemoveTopic( rTopic
);
473 aTopics
.push_back(const_cast<DdeTopic
*>(&rTopic
));
476 void DdeService::RemoveTopic( const DdeTopic
& rTopic
)
478 auto iter
= std::find_if(aTopics
.begin(), aTopics
.end(),
479 [&rTopic
](const DdeTopic
* pTopic
) { return DdeCmpStringHandles(pTopic
->pName
->getHSZ(), rTopic
.pName
->getHSZ()) == 0; });
480 if (iter
!= aTopics
.end())
483 // Delete all conversions!
484 // Or else we work on deleted topics!
485 for( size_t n
= m_vConv
.size(); n
; )
487 auto const& pC
= m_vConv
[ --n
];
488 if( pC
->pTopic
== &rTopic
)
489 m_vConv
.erase( m_vConv
.begin() + n
);
494 bool DdeService::HasCbFormat( sal_uInt32 nFmt
)
496 return std::find(aFormats
.begin(), aFormats
.end(), nFmt
) != aFormats
.end();
499 bool DdeService::HasFormat(SotClipboardFormatId nFmt
)
501 return HasCbFormat( DdeData::GetExternalFormat( nFmt
));
504 void DdeService::AddFormat(SotClipboardFormatId nFmt
)
506 sal_uInt32 nExternalFmt
= DdeData::GetExternalFormat( nFmt
);
507 if (HasCbFormat(nExternalFmt
))
509 aFormats
.push_back( nExternalFmt
);
512 void DdeService::RemoveFormat(SotClipboardFormatId nFmt
)
514 sal_uInt32 nExternalFmt
= DdeData::GetExternalFormat( nFmt
);
515 auto it
= std::find(aFormats
.begin(), aFormats
.end(), nExternalFmt
);
516 if (it
!= aFormats
.end())
517 aFormats
.erase( it
);
520 DdeTopic::DdeTopic( const OUString
& rName
)
522 DdeInstData
* pInst
= ImpGetInstData();
524 pName
= new DdeString( pInst
->hDdeInstSvr
, rName
);
527 DdeTopic::~DdeTopic()
529 for (auto& rpItem
: aItems
)
531 rpItem
->pMyTopic
= nullptr;
538 OUString
DdeTopic::GetName() const
540 return pName
->toOUString();
543 bool DdeTopic::IsSystemTopic()
545 return GetName() == SZDDESYS_TOPIC
;
548 DdeItem
* DdeTopic::AddItem( const DdeItem
& r
)
551 if( DDEGETPUTITEM
== r
.nType
)
552 s
= new DdeGetPutItem( r
);
554 s
= new DdeItem( r
);
556 aItems
.push_back( s
);
561 void DdeTopic::InsertItem( DdeItem
* pNew
)
565 aItems
.push_back( pNew
);
566 pNew
->pMyTopic
= this;
570 void DdeTopic::RemoveItem( const DdeItem
& r
)
572 auto iter
= std::find_if(aItems
.begin(), aItems
.end(),
573 [&r
](const DdeItem
* pItem
) { return DdeCmpStringHandles(pItem
->pName
->getHSZ(), r
.pName
->getHSZ()) == 0; });
575 if ( iter
!= aItems
.end() )
577 (*iter
)->pMyTopic
= nullptr;
583 void DdeTopic::NotifyClient( const OUString
& rItem
)
585 DdeInstData
* pInst
= ImpGetInstData();
587 auto iter
= std::find_if(aItems
.begin(), aItems
.end(),
588 [&rItem
](const DdeItem
* pItem
) { return pItem
->GetName().equals(rItem
) && pItem
->pImpData
; });
589 if (iter
!= aItems
.end())
590 DdePostAdvise( pInst
->hDdeInstSvr
, pName
->getHSZ(), (*iter
)->pName
->getHSZ() );
593 void DdeInternal::DisconnectTopic(DdeTopic
& rTopic
, HCONV nId
)
595 for (const auto& rpItem
: rTopic
.aItems
)
597 DecMonitor(rpItem
, nId
);
601 DdeData
* DdeTopic::Get(SotClipboardFormatId
/*nFmt*/)
606 bool DdeTopic::Put( const DdeData
* )
611 bool DdeTopic::Execute( const OUString
* )
616 bool DdeTopic::StartAdviseLoop()
621 DdeItem::DdeItem( const sal_Unicode
* p
)
623 DdeInstData
* pInst
= ImpGetInstData();
625 pName
= new DdeString( pInst
->hDdeInstSvr
, OUString(p
) );
631 DdeItem::DdeItem( const OUString
& r
)
633 DdeInstData
* pInst
= ImpGetInstData();
635 pName
= new DdeString( pInst
->hDdeInstSvr
, r
);
641 DdeItem::DdeItem( const DdeItem
& r
)
643 DdeInstData
* pInst
= ImpGetInstData();
645 pName
= new DdeString( pInst
->hDdeInstSvr
, r
.pName
->toOUString() );
654 pMyTopic
->aItems
.erase(std::remove(pMyTopic
->aItems
.begin(),
655 pMyTopic
->aItems
.end(),this));
660 OUString
DdeItem::GetName() const
662 return pName
->toOUString();
665 void DdeItem::NotifyClient()
667 if( pMyTopic
&& pImpData
)
669 DdeInstData
* pInst
= ImpGetInstData();
671 DdePostAdvise( pInst
->hDdeInstSvr
, pMyTopic
->pName
->getHSZ(), pName
->getHSZ() );
675 void DdeInternal::IncMonitor(DdeItem
*const pItem
, HCONV nHCnv
)
677 if (!pItem
->pImpData
)
679 pItem
->pImpData
= new std::vector
<DdeItemImpData
>;
680 if (DDEGETPUTITEM
== pItem
->nType
)
682 static_cast<DdeGetPutItem
*>(pItem
)->AdviseLoop( true );
687 for (size_t n
= pItem
->pImpData
->size(); n
; )
689 if ((*pItem
->pImpData
)[ --n
].nHCnv
== nHCnv
)
691 ++(*pItem
->pImpData
)[ n
].nHCnv
;
697 pItem
->pImpData
->push_back( DdeItemImpData( nHCnv
) );
700 void DdeInternal::DecMonitor(DdeItem
*const pItem
, HCONV nHCnv
)
704 for( size_t n
= 0; n
< pItem
->pImpData
->size(); ++n
)
706 DdeItemImpData
* pData
= &(*pItem
->pImpData
)[n
];
707 if( pData
->nHCnv
== nHCnv
)
709 if( !pData
->nCnt
|| !--pData
->nCnt
)
711 if (1 < pItem
->pImpData
->size())
713 pItem
->pImpData
->erase(pItem
->pImpData
->begin() + n
);
717 delete pItem
->pImpData
;
718 pItem
->pImpData
= nullptr;
719 if (DDEGETPUTITEM
== pItem
->nType
)
721 static_cast<DdeGetPutItem
*>(pItem
)->AdviseLoop(false);
731 short DdeItem::GetLinks()
736 for (const auto& rData
: *pImpData
)
744 DdeGetPutItem::DdeGetPutItem( const sal_Unicode
* p
)
747 nType
= DDEGETPUTITEM
;
750 DdeGetPutItem::DdeGetPutItem( const OUString
& rStr
)
753 nType
= DDEGETPUTITEM
;
756 DdeGetPutItem::DdeGetPutItem( const DdeItem
& rItem
)
759 nType
= DDEGETPUTITEM
;
762 DdeData
* DdeGetPutItem::Get(SotClipboardFormatId
)
767 bool DdeGetPutItem::Put( const DdeData
* )
772 void DdeGetPutItem::AdviseLoop( bool )
776 OUString
DdeService::SysItems()
779 for ( const auto& rpTopic
: aTopics
)
781 if ( rpTopic
->GetName() == SZDDESYS_TOPIC
)
784 for ( const auto& rpItem
: rpTopic
->aItems
)
788 s
+= rpItem
->GetName();
798 OUString
DdeService::Topics()
803 for ( const auto& rpTopic
: aTopics
)
807 s
+= rpTopic
->GetName();
815 OUString
DdeService::Formats()
820 for (size_t i
= 0; i
< aFormats
.size(); ++i
, ++n
)
822 sal_uInt32 f
= aFormats
[ i
];
837 GetClipboardFormatNameW( f
, buf
, SAL_N_ELEMENTS(buf
) );
849 OUString
DdeService::Status()
854 bool DdeTopic::MakeItem( const OUString
& )
859 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */