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 DdeInstData
* pInst
= ImpGetInstData();
57 case XTYP_WILDCONNECT
:
59 std::vector
<HSZPAIR
> aPairs
;
61 WCHAR chTopicBuf
[256];
63 DdeQueryStringW( pInst
->hDdeInstSvr
, hText1
, chTopicBuf
,
64 SAL_N_ELEMENTS(chTopicBuf
), CP_WINUNICODE
);
66 for (auto& pService
: DdeService::GetServices())
68 if (hText2
&& !(*pService
->pName
== hText2
))
71 OUString
sTopics(pService
->Topics().replaceAll("\n", "").replaceAll("\r", ""));
72 if (sTopics
.isEmpty())
75 for (sal_Int32 n
= 0; -1 != n
;)
77 OUString
s(sTopics
.getToken(0, '\t', n
));
78 if (hText1
&& s
!= o3tl::toU(chTopicBuf
))
81 DdeString
aDStr(pInst
->hDdeInstSvr
, s
);
82 if (auto pTopic
= FindTopic(*pService
, aDStr
.getHSZ()))
84 auto& pair
= aPairs
.emplace_back();
85 pair
.hszSvc
= pService
->pName
->getHSZ();
86 pair
.hszTopic
= pTopic
->pName
->getHSZ();
93 aPairs
.emplace_back(); // trailing zero
95 HDDEDATA h
= DdeCreateDataHandle(
97 reinterpret_cast<LPBYTE
>(aPairs
.data()),
98 sizeof(HSZPAIR
) * aPairs
.size(),
99 0, nullptr, nCbType
, 0);
104 if (auto pService
= FindService(hText2
))
105 if (FindTopic(*pService
, hText1
))
106 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
109 case XTYP_CONNECT_CONFIRM
:
110 if (auto pService
= FindService(hText2
))
112 if (auto pTopic
= FindTopic(*pService
, hText1
))
114 auto pC
= new Conversation
;
117 pService
->m_vConv
.emplace_back( pC
);
123 DdeService
* pService
= nullptr;
124 Conversation
* pC
= nullptr;
125 for (auto& rpService
: DdeService::GetServices())
127 for ( size_t i
= 0, n
= rpService
->m_vConv
.size(); i
< n
; ++i
)
129 pC
= rpService
->m_vConv
[ i
].get();
130 if ( pC
->hConv
== hConv
)
131 pService
= rpService
;
136 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
139 if ( nCode
== XTYP_DISCONNECT
)
141 DisconnectTopic(*pC
->pTopic
, hConv
);
142 auto it
= std::find_if(pService
->m_vConv
.begin(), pService
->m_vConv
.end(),
143 [&pC
](const std::unique_ptr
<Conversation
>& rxConv
) { return rxConv
.get() == pC
; });
144 if (it
!= pService
->m_vConv
.end())
145 pService
->m_vConv
.erase( it
);
149 bool bExec
= nCode
== XTYP_EXECUTE
;
150 DdeTopic
* pTopic
= pC
->pTopic
;
152 if (pTopic
&& !bExec
&& pService
->HasCbFormat(nCbType
))
153 pItem
= FindItem( *pTopic
, hText2
);
157 if ( !pItem
&& !bExec
)
158 return static_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
160 pTopic
->aItem
= pItem
->GetName();
162 pTopic
->aItem
.clear();
170 OUString aRes
; // Must be free not until the end!
172 if ( pTopic
->IsSystemTopic() )
174 if ( pTopic
->aItem
== SZDDESYS_ITEM_TOPICS
)
175 aRes
= pService
->Topics();
176 else if ( pTopic
->aItem
== SZDDESYS_ITEM_SYSITEMS
)
177 aRes
= pService
->SysItems();
178 else if ( pTopic
->aItem
== SZDDESYS_ITEM_STATUS
)
179 aRes
= pService
->Status();
180 else if ( pTopic
->aItem
== SZDDESYS_ITEM_FORMATS
)
181 aRes
= pService
->Formats();
182 else if ( pTopic
->aItem
== SZDDESYS_ITEM_HELP
)
187 if ( !aRes
.isEmpty() )
188 pData
= new DdeData( aRes
);
192 else if( DDEGETPUTITEM
== pItem
->nType
)
194 pData
= static_cast<DdeGetPutItem
*>(pItem
)->Get( DdeData::GetInternalFormat( nCbType
) );
198 pData
= pTopic
->Get( DdeData::GetInternalFormat( nCbType
));
203 return DdeCreateDataHandle( pInst
->hDdeInstSvr
,
204 static_cast<LPBYTE
>(const_cast<void *>(pData
->xImp
->pData
)),
207 DdeData::GetExternalFormat(
215 if ( !pTopic
->IsSystemTopic() )
218 d
.xImp
->hData
= hData
;
219 d
.xImp
->nFmt
= DdeData::GetInternalFormat( nCbType
);
221 if( DDEGETPUTITEM
== pItem
->nType
)
222 bRes
= static_cast<DdeGetPutItem
*>(pItem
)->Put( &d
);
224 bRes
= pTopic
->Put( &d
);
227 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
229 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
233 // Is the Item turning into a HotLink for the first time?
234 if( !pItem
->pImpData
&& pTopic
->StartAdviseLoop() )
236 // Then the Item has been exchanged
237 std::vector
<DdeItem
*>::iterator
it(std::find(pTopic
->aItems
.begin(),
238 pTopic
->aItems
.end(),
240 if (it
!= pTopic
->aItems
.end())
241 pTopic
->aItems
.erase(it
);
243 std::vector
<DdeItem
*>::iterator iter
;
244 iter
= std::find_if(pTopic
->aItems
.begin(), pTopic
->aItems
.end(),
245 [&hText2
](const DdeItem
* pDdeItem
) { return *pDdeItem
->pName
== hText2
; });
246 if (iter
!= pTopic
->aItems
.end())
248 // It was exchanged indeed
254 // It was not exchange, so back in
255 pTopic
->aItems
.push_back(pItem
);
257 pItem
= iter
!= pTopic
->aItems
.end() ? *iter
: nullptr;
262 IncMonitor(pItem
, hConv
);
265 return reinterpret_cast<HDDEDATA
>(TRUE
);
268 DecMonitor(pItem
, hConv
);
269 return reinterpret_cast<HDDEDATA
>(TRUE
);
274 aExec
.xImp
->hData
= hData
;
275 aExec
.xImp
->nFmt
= DdeData::GetInternalFormat( nCbType
);
279 aName
= static_cast<const sal_Unicode
*>(aExec
.xImp
->pData
);
281 if( pTopic
->IsSystemTopic() )
284 bRes
= pTopic
->Execute( &aName
);
287 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
289 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
295 DdeService
* DdeInternal::FindService( HSZ hService
)
297 DdeServices
& rSvc
= DdeService::GetServices();
298 auto aI
= std::find_if(rSvc
.begin(), rSvc
.end(),
299 [&hService
](const DdeService
* s
) { return *s
->pName
== hService
; });
300 if (aI
!= rSvc
.end())
306 DdeTopic
* DdeInternal::FindTopic( DdeService
& rService
, HSZ hTopic
)
308 std::vector
<DdeTopic
*> &rTopics
= rService
.aTopics
;
310 auto iter
= std::find_if(rTopics
.begin(), rTopics
.end(),
311 [&hTopic
](const DdeTopic
* pTopic
) { return *pTopic
->pName
== hTopic
; });
312 if (iter
!= rTopics
.end())
318 DdeItem
* DdeInternal::FindItem( DdeTopic
& rTopic
, HSZ hItem
)
320 std::vector
<DdeItem
*>::iterator iter
;
321 std::vector
<DdeItem
*> &rItems
= rTopic
.aItems
;
322 DdeInstData
* pInst
= ImpGetInstData();
324 bool bContinue
= false;
327 { // middle check loop
328 iter
= std::find_if(rItems
.begin(), rItems
.end(),
329 [&hItem
](const DdeItem
* pItem
) { return *pItem
->pName
== hItem
; });
330 if (iter
!= rItems
.end())
332 bContinue
= !bContinue
;
336 // Let's query our subclass
338 DdeQueryStringW(pInst
->hDdeInstSvr
,hItem
,chBuf
,SAL_N_ELEMENTS(chBuf
),CP_WINUNICODE
);
339 bContinue
= rTopic
.MakeItem( OUString(o3tl::toU(chBuf
)) );
340 // We need to search again
347 DdeService::DdeService( const OUString
& rService
)
349 DdeInstData
* pInst
= ImpGetInstData();
351 pInst
= ImpInitInstData();
353 pInst
->nInstanceSvr
++;
355 if ( !pInst
->hDdeInstSvr
)
357 nStatus
= sal::static_int_cast
< short >(
358 DdeInitializeW( &pInst
->hDdeInstSvr
,
359 DdeInternal::SvrCallback
,
361 CBF_SKIP_REGISTRATIONS
|
362 CBF_SKIP_UNREGISTRATIONS
, 0 ) );
363 pInst
->pServicesSvr
= new DdeServices
;
366 nStatus
= DMLERR_NO_ERROR
;
368 if ( pInst
->pServicesSvr
)
369 pInst
->pServicesSvr
->push_back( this );
371 pName
= new DdeString( pInst
->hDdeInstSvr
, rService
);
372 if ( nStatus
== DMLERR_NO_ERROR
)
374 if ( !DdeNameService( pInst
->hDdeInstSvr
, pName
->getHSZ(), nullptr,
375 DNS_REGISTER
| DNS_FILTEROFF
) )
377 nStatus
= DMLERR_SYS_ERROR
;
380 AddFormat( SotClipboardFormatId::STRING
);
381 pSysTopic
= new DdeTopic( SZDDESYS_TOPIC
);
382 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_TOPICS
) );
383 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_SYSITEMS
) );
384 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_STATUS
) );
385 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_FORMATS
) );
386 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_HELP
) );
387 AddTopic( *pSysTopic
);
390 DdeService::~DdeService()
392 DdeInstData
* pInst
= ImpGetInstData();
394 if ( pInst
->pServicesSvr
)
395 pInst
->pServicesSvr
->erase(std::remove(pInst
->pServicesSvr
->begin(), pInst
->pServicesSvr
->end(), this), pInst
->pServicesSvr
->end());
400 pInst
->nInstanceSvr
--;
402 if ( !pInst
->nInstanceSvr
&& pInst
->hDdeInstSvr
)
404 if( DdeUninitialize( pInst
->hDdeInstSvr
) )
406 pInst
->hDdeInstSvr
= 0;
407 delete pInst
->pServicesSvr
;
408 pInst
->pServicesSvr
= nullptr;
409 if( pInst
->nRefCount
== 0)
415 OUString
DdeService::GetName() const
417 return pName
->toOUString();
420 DdeServices
& DdeService::GetServices()
422 DdeInstData
* pInst
= ImpGetInstData();
424 return *(pInst
->pServicesSvr
);
427 void DdeService::AddTopic( const DdeTopic
& rTopic
)
429 RemoveTopic( rTopic
);
430 aTopics
.push_back(const_cast<DdeTopic
*>(&rTopic
));
433 void DdeService::RemoveTopic( const DdeTopic
& rTopic
)
435 auto iter
= std::find_if(aTopics
.begin(), aTopics
.end(),
436 [&rTopic
](const DdeTopic
* pTopic
) { return DdeCmpStringHandles(pTopic
->pName
->getHSZ(), rTopic
.pName
->getHSZ()) == 0; });
437 if (iter
!= aTopics
.end())
440 // Delete all conversions!
441 // Or else we work on deleted topics!
442 for( size_t n
= m_vConv
.size(); n
; )
444 auto const& pC
= m_vConv
[ --n
];
445 if( pC
->pTopic
== &rTopic
)
446 m_vConv
.erase( m_vConv
.begin() + n
);
451 bool DdeService::HasCbFormat( sal_uInt32 nFmt
)
453 return std::find(aFormats
.begin(), aFormats
.end(), nFmt
) != aFormats
.end();
456 bool DdeService::HasFormat(SotClipboardFormatId nFmt
)
458 return HasCbFormat( DdeData::GetExternalFormat( nFmt
));
461 void DdeService::AddFormat(SotClipboardFormatId nFmt
)
463 sal_uInt32 nExternalFmt
= DdeData::GetExternalFormat( nFmt
);
464 if (HasCbFormat(nExternalFmt
))
466 aFormats
.push_back( nExternalFmt
);
469 void DdeService::RemoveFormat(SotClipboardFormatId nFmt
)
471 sal_uInt32 nExternalFmt
= DdeData::GetExternalFormat( nFmt
);
472 auto it
= std::find(aFormats
.begin(), aFormats
.end(), nExternalFmt
);
473 if (it
!= aFormats
.end())
474 aFormats
.erase( it
);
477 DdeTopic::DdeTopic( const OUString
& rName
)
479 DdeInstData
* pInst
= ImpGetInstData();
481 pName
= new DdeString( pInst
->hDdeInstSvr
, rName
);
484 DdeTopic::~DdeTopic()
486 for (auto& rpItem
: aItems
)
488 rpItem
->pMyTopic
= nullptr;
495 OUString
DdeTopic::GetName() const
497 return pName
->toOUString();
500 bool DdeTopic::IsSystemTopic()
502 return GetName() == SZDDESYS_TOPIC
;
505 DdeItem
* DdeTopic::AddItem( const DdeItem
& r
)
508 if( DDEGETPUTITEM
== r
.nType
)
509 s
= new DdeGetPutItem( r
);
511 s
= new DdeItem( r
);
513 aItems
.push_back( s
);
518 void DdeTopic::InsertItem( DdeItem
* pNew
)
522 aItems
.push_back( pNew
);
523 pNew
->pMyTopic
= this;
527 void DdeTopic::RemoveItem( const DdeItem
& r
)
529 auto iter
= std::find_if(aItems
.begin(), aItems
.end(),
530 [&r
](const DdeItem
* pItem
) { return DdeCmpStringHandles(pItem
->pName
->getHSZ(), r
.pName
->getHSZ()) == 0; });
532 if ( iter
!= aItems
.end() )
534 (*iter
)->pMyTopic
= nullptr;
540 void DdeTopic::NotifyClient( const OUString
& rItem
)
542 DdeInstData
* pInst
= ImpGetInstData();
544 auto iter
= std::find_if(aItems
.begin(), aItems
.end(),
545 [&rItem
](const DdeItem
* pItem
) { return pItem
->GetName().equals(rItem
) && pItem
->pImpData
; });
546 if (iter
!= aItems
.end())
547 DdePostAdvise( pInst
->hDdeInstSvr
, pName
->getHSZ(), (*iter
)->pName
->getHSZ() );
550 void DdeInternal::DisconnectTopic(DdeTopic
& rTopic
, HCONV nId
)
552 for (const auto& rpItem
: rTopic
.aItems
)
554 DecMonitor(rpItem
, nId
);
558 DdeData
* DdeTopic::Get(SotClipboardFormatId
/*nFmt*/)
563 bool DdeTopic::Put( const DdeData
* )
568 bool DdeTopic::Execute( const OUString
* )
573 bool DdeTopic::StartAdviseLoop()
578 DdeItem::DdeItem( const sal_Unicode
* p
)
580 DdeInstData
* pInst
= ImpGetInstData();
582 pName
= new DdeString( pInst
->hDdeInstSvr
, OUString(p
) );
588 DdeItem::DdeItem( const OUString
& r
)
590 DdeInstData
* pInst
= ImpGetInstData();
592 pName
= new DdeString( pInst
->hDdeInstSvr
, r
);
598 DdeItem::DdeItem( const DdeItem
& r
)
600 DdeInstData
* pInst
= ImpGetInstData();
602 pName
= new DdeString( pInst
->hDdeInstSvr
, r
.pName
->toOUString() );
611 pMyTopic
->aItems
.erase(std::remove(pMyTopic
->aItems
.begin(),
612 pMyTopic
->aItems
.end(),this));
617 OUString
DdeItem::GetName() const
619 return pName
->toOUString();
622 void DdeItem::NotifyClient()
624 if( pMyTopic
&& pImpData
)
626 DdeInstData
* pInst
= ImpGetInstData();
628 DdePostAdvise( pInst
->hDdeInstSvr
, pMyTopic
->pName
->getHSZ(), pName
->getHSZ() );
632 void DdeInternal::IncMonitor(DdeItem
*const pItem
, HCONV nHCnv
)
634 if (!pItem
->pImpData
)
636 pItem
->pImpData
= new std::vector
<DdeItemImpData
>;
637 if (DDEGETPUTITEM
== pItem
->nType
)
639 static_cast<DdeGetPutItem
*>(pItem
)->AdviseLoop( true );
644 for (size_t n
= pItem
->pImpData
->size(); n
; )
646 if ((*pItem
->pImpData
)[ --n
].nHCnv
== nHCnv
)
648 ++(*pItem
->pImpData
)[ n
].nHCnv
;
654 pItem
->pImpData
->push_back( DdeItemImpData( nHCnv
) );
657 void DdeInternal::DecMonitor(DdeItem
*const pItem
, HCONV nHCnv
)
661 for( size_t n
= 0; n
< pItem
->pImpData
->size(); ++n
)
663 DdeItemImpData
* pData
= &(*pItem
->pImpData
)[n
];
664 if( pData
->nHCnv
== nHCnv
)
666 if( !pData
->nCnt
|| !--pData
->nCnt
)
668 if (1 < pItem
->pImpData
->size())
670 pItem
->pImpData
->erase(pItem
->pImpData
->begin() + n
);
674 delete pItem
->pImpData
;
675 pItem
->pImpData
= nullptr;
676 if (DDEGETPUTITEM
== pItem
->nType
)
678 static_cast<DdeGetPutItem
*>(pItem
)->AdviseLoop(false);
688 short DdeItem::GetLinks()
693 for (const auto& rData
: *pImpData
)
701 DdeGetPutItem::DdeGetPutItem( const sal_Unicode
* p
)
704 nType
= DDEGETPUTITEM
;
707 DdeGetPutItem::DdeGetPutItem( const OUString
& rStr
)
710 nType
= DDEGETPUTITEM
;
713 DdeGetPutItem::DdeGetPutItem( const DdeItem
& rItem
)
716 nType
= DDEGETPUTITEM
;
719 DdeData
* DdeGetPutItem::Get(SotClipboardFormatId
)
724 bool DdeGetPutItem::Put( const DdeData
* )
729 void DdeGetPutItem::AdviseLoop( bool )
733 OUString
DdeService::SysItems()
736 for ( const auto& rpTopic
: aTopics
)
738 if ( rpTopic
->GetName() == SZDDESYS_TOPIC
)
741 for ( const auto& rpItem
: rpTopic
->aItems
)
745 s
+= rpItem
->GetName();
755 OUString
DdeService::Topics()
760 for ( const auto& rpTopic
: aTopics
)
764 s
+= rpTopic
->GetName();
772 OUString
DdeService::Formats()
777 for (size_t i
= 0; i
< aFormats
.size(); ++i
, ++n
)
779 sal_uInt32 f
= aFormats
[ i
];
794 GetClipboardFormatNameW( f
, buf
, SAL_N_ELEMENTS(buf
) );
806 OUString
DdeService::Status()
811 bool DdeTopic::MakeItem( const OUString
& )
816 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */