sd: simplify UpdateShellStack
[LibreOffice.git] / svl / source / svdde / ddesvr.cxx
blob3df8b5a570a59843831350dd1e0c89c4f81751fc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include "ddeimp.hxx"
21 #include <algorithm>
22 #include <memory>
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>
29 #include <officecfg/Office/Common.hxx>
31 namespace {
33 enum DdeItemType
35 DDEITEM,
36 DDEGETPUTITEM
41 struct DdeItemImpData
43 HCONV nHCnv;
44 sal_uInt16 nCnt;
46 explicit DdeItemImpData( HCONV nH ) : nHCnv( nH ), nCnt( 1 ) {}
49 HDDEDATA CALLBACK DdeInternal::SvrCallback(
50 UINT nCode, UINT nCbType, HCONV hConv, HSZ hText1, HSZ hText2,
51 HDDEDATA hData, ULONG_PTR, ULONG_PTR )
53 DdeInstData* pInst = ImpGetInstData();
54 assert(pInst);
56 switch( nCode )
58 case XTYP_WILDCONNECT:
60 std::vector<HSZPAIR> aPairs;
62 WCHAR chTopicBuf[256];
63 if( hText1 )
64 DdeQueryStringW( pInst->hDdeInstSvr, hText1, chTopicBuf,
65 SAL_N_ELEMENTS(chTopicBuf), CP_WINUNICODE );
67 for (auto& pService : DdeService::GetServices())
69 if (hText2 && !(*pService->pName == hText2))
70 continue;
72 OUString sTopics(pService->Topics().replaceAll("\n", "").replaceAll("\r", ""));
73 if (sTopics.isEmpty())
74 continue;
76 for (sal_Int32 n = 0; -1 != n;)
78 OUString s(sTopics.getToken(0, '\t', n));
79 if (hText1 && s != o3tl::toU(chTopicBuf))
80 continue;
82 DdeString aDStr(pInst->hDdeInstSvr, s);
83 if (auto pTopic = FindTopic(*pService, aDStr.getHSZ()))
85 auto& pair = aPairs.emplace_back();
86 pair.hszSvc = pService->pName->getHSZ();
87 pair.hszTopic = pTopic->pName->getHSZ();
92 if (aPairs.empty())
93 return nullptr;
94 aPairs.emplace_back(); // trailing zero
96 HDDEDATA h = DdeCreateDataHandle(
97 pInst->hDdeInstSvr,
98 reinterpret_cast<LPBYTE>(aPairs.data()),
99 sizeof(HSZPAIR) * aPairs.size(),
100 0, nullptr, nCbType, 0);
101 return h;
104 case XTYP_CONNECT:
105 if (auto pService = FindService(hText2))
106 if (FindTopic(*pService, hText1))
107 return reinterpret_cast<HDDEDATA>(DDE_FACK);
108 return nullptr;
110 case XTYP_CONNECT_CONFIRM:
111 if (auto pService = FindService(hText2))
113 if (auto pTopic = FindTopic(*pService, hText1))
115 auto pC = new Conversation;
116 pC->hConv = hConv;
117 pC->pTopic = pTopic;
118 pService->m_vConv.emplace_back( pC );
121 return nullptr;
124 DdeService* pService = nullptr;
125 Conversation* pC = nullptr;
126 for (auto& rpService : DdeService::GetServices())
128 for ( size_t i = 0, n = rpService->m_vConv.size(); i < n; ++i )
130 pC = rpService->m_vConv[ i ].get();
131 if ( pC->hConv == hConv )
132 pService = rpService;
136 if (!pService)
137 return reinterpret_cast<HDDEDATA>(DDE_FNOTPROCESSED);
138 assert(pC);
140 if ( nCode == XTYP_DISCONNECT)
142 DisconnectTopic(*pC->pTopic, hConv);
143 auto it = std::find_if(pService->m_vConv.begin(), pService->m_vConv.end(),
144 [&pC](const std::unique_ptr<Conversation>& rxConv) { return rxConv.get() == pC; });
145 if (it != pService->m_vConv.end())
146 pService->m_vConv.erase( it );
147 return nullptr;
150 bool bExec = nCode == XTYP_EXECUTE;
151 DdeTopic* pTopic = pC->pTopic;
152 DdeItem* pItem;
153 if (pTopic && !bExec && pService->HasCbFormat(nCbType))
154 pItem = FindItem( *pTopic, hText2 );
155 else
156 pItem = nullptr;
158 if ( !pItem && !bExec )
159 return static_cast<HDDEDATA>(DDE_FNOTPROCESSED);
160 if ( pItem )
161 pTopic->aItem = pItem->GetName();
162 else
163 pTopic->aItem.clear();
165 bool bRes = false;
166 switch( nCode )
168 case XTYP_REQUEST:
169 case XTYP_ADVREQ:
171 OUString aRes; // Must be free not until the end!
172 DdeData* pData;
173 if ( pTopic->IsSystemTopic() )
175 if ( pTopic->aItem == SZDDESYS_ITEM_TOPICS )
176 aRes = pService->Topics();
177 else if ( pTopic->aItem == SZDDESYS_ITEM_SYSITEMS )
178 aRes = pService->SysItems();
179 else if ( pTopic->aItem == SZDDESYS_ITEM_STATUS )
180 aRes = pService->Status();
181 else if ( pTopic->aItem == SZDDESYS_ITEM_FORMATS )
182 aRes = pService->Formats();
183 else if ( pTopic->aItem == SZDDESYS_ITEM_HELP )
184 aRes = OUString();
185 else
186 aRes = OUString();
188 if ( !aRes.isEmpty() )
189 pData = new DdeData( aRes );
190 else
191 pData = nullptr;
193 else if( DDEGETPUTITEM == pItem->nType )
195 pData = static_cast<DdeGetPutItem*>(pItem)->Get( DdeData::GetInternalFormat( nCbType ) );
197 else
199 pData = pTopic->Get( DdeData::GetInternalFormat( nCbType ));
202 if ( pData )
204 return DdeCreateDataHandle( pInst->hDdeInstSvr,
205 static_cast<LPBYTE>(const_cast<void *>(pData->xImp->pData)),
206 pData->xImp->nData,
207 0, hText2,
208 DdeData::GetExternalFormat(
209 pData->xImp->nFmt ),
210 0 );
213 break;
215 case XTYP_POKE:
216 if ( !pTopic->IsSystemTopic() )
218 DdeData d;
219 d.xImp->hData = hData;
220 d.xImp->nFmt = DdeData::GetInternalFormat( nCbType );
221 d.Lock();
222 if( DDEGETPUTITEM == pItem->nType )
223 bRes = static_cast<DdeGetPutItem*>(pItem)->Put( &d );
224 else
225 bRes = pTopic->Put( &d );
227 if ( bRes )
228 return reinterpret_cast<HDDEDATA>(DDE_FACK);
229 else
230 return reinterpret_cast<HDDEDATA>(DDE_FNOTPROCESSED);
232 case XTYP_ADVSTART:
234 // Is the Item turning into a HotLink for the first time?
235 if( !pItem->pImpData && pTopic->StartAdviseLoop() )
237 // Then the Item has been exchanged
238 std::vector<DdeItem*>::iterator it(std::find(pTopic->aItems.begin(),
239 pTopic->aItems.end(),
240 pItem));
241 if (it != pTopic->aItems.end())
242 pTopic->aItems.erase(it);
244 std::vector<DdeItem*>::iterator iter;
245 iter = std::find_if(pTopic->aItems.begin(), pTopic->aItems.end(),
246 [&hText2](const DdeItem* pDdeItem) { return *pDdeItem->pName == hText2; });
247 if (iter != pTopic->aItems.end())
249 // It was exchanged indeed
250 delete pItem;
251 pItem = nullptr;
254 if( pItem )
255 // It was not exchange, so back in
256 pTopic->aItems.push_back(pItem);
257 else
258 pItem = iter != pTopic->aItems.end() ? *iter : nullptr;
261 if (pItem)
263 IncMonitor(pItem, hConv);
266 return reinterpret_cast<HDDEDATA>(TRUE);
268 case XTYP_ADVSTOP:
269 DecMonitor(pItem, hConv);
270 return reinterpret_cast<HDDEDATA>(TRUE);
272 case XTYP_EXECUTE:
274 DdeData aExec;
275 aExec.xImp->hData = hData;
276 aExec.xImp->nFmt = DdeData::GetInternalFormat( nCbType );
277 aExec.Lock();
278 OUString aName;
280 aName = static_cast<const sal_Unicode *>(aExec.xImp->pData);
282 if( pTopic->IsSystemTopic() )
283 bRes = false;
284 else
285 bRes = pTopic->Execute( &aName );
287 if ( bRes )
288 return reinterpret_cast<HDDEDATA>(DDE_FACK);
289 else
290 return reinterpret_cast<HDDEDATA>(DDE_FNOTPROCESSED);
293 return nullptr;
296 DdeService* DdeInternal::FindService( HSZ hService )
298 DdeServices& rSvc = DdeService::GetServices();
299 auto aI = std::find_if(rSvc.begin(), rSvc.end(),
300 [&hService](const DdeService* s) { return *s->pName == hService; });
301 if (aI != rSvc.end())
302 return *aI;
304 return nullptr;
307 DdeTopic* DdeInternal::FindTopic( DdeService& rService, HSZ hTopic )
309 std::vector<DdeTopic*> &rTopics = rService.aTopics;
311 auto iter = std::find_if(rTopics.begin(), rTopics.end(),
312 [&hTopic](const DdeTopic* pTopic) { return *pTopic->pName == hTopic; });
313 if (iter != rTopics.end())
314 return *iter;
316 return nullptr;
319 DdeItem* DdeInternal::FindItem( DdeTopic& rTopic, HSZ hItem )
321 std::vector<DdeItem*>::iterator iter;
322 std::vector<DdeItem*> &rItems = rTopic.aItems;
323 DdeInstData* pInst = ImpGetInstData();
324 assert(pInst);
325 bool bContinue = false;
328 { // middle check loop
329 iter = std::find_if(rItems.begin(), rItems.end(),
330 [&hItem](const DdeItem* pItem) { return *pItem->pName == hItem; });
331 if (iter != rItems.end())
332 return *iter;
333 bContinue = !bContinue;
334 if( !bContinue )
335 break;
337 // Let's query our subclass
338 WCHAR chBuf[250];
339 DdeQueryStringW(pInst->hDdeInstSvr,hItem,chBuf,SAL_N_ELEMENTS(chBuf),CP_WINUNICODE );
340 bContinue = rTopic.MakeItem( OUString(o3tl::toU(chBuf)) );
341 // We need to search again
343 while( bContinue );
345 return nullptr;
348 DdeService::DdeService( const OUString& rService )
350 DdeInstData* pInst = ImpGetInstData();
351 if( !pInst )
352 pInst = ImpInitInstData();
353 pInst->nRefCount++;
354 pInst->nInstanceSvr++;
356 if ( !pInst->hDdeInstSvr )
358 nStatus = DMLERR_SYS_ERROR;
359 if ( !officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() )
361 nStatus = sal::static_int_cast< short >(
362 DdeInitializeW( &pInst->hDdeInstSvr,
363 DdeInternal::SvrCallback,
364 APPCLASS_STANDARD |
365 CBF_SKIP_REGISTRATIONS |
366 CBF_SKIP_UNREGISTRATIONS, 0 ) );
368 pInst->pServicesSvr = new DdeServices;
370 else
371 nStatus = DMLERR_NO_ERROR;
373 if ( pInst->pServicesSvr )
374 pInst->pServicesSvr->push_back( this );
376 pName = new DdeString( pInst->hDdeInstSvr, rService );
377 if ( nStatus == DMLERR_NO_ERROR )
379 if ( !DdeNameService( pInst->hDdeInstSvr, pName->getHSZ(), nullptr,
380 DNS_REGISTER | DNS_FILTEROFF ) )
382 nStatus = DMLERR_SYS_ERROR;
385 AddFormat( SotClipboardFormatId::STRING );
386 pSysTopic = new DdeTopic( SZDDESYS_TOPIC );
387 pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_TOPICS ) );
388 pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_SYSITEMS ) );
389 pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_STATUS ) );
390 pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_FORMATS ) );
391 pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_HELP ) );
392 AddTopic( *pSysTopic );
395 DdeService::~DdeService()
397 DdeInstData* pInst = ImpGetInstData();
398 assert(pInst);
399 if ( pInst->pServicesSvr )
400 std::erase(*pInst->pServicesSvr, this);
402 delete pSysTopic;
403 delete pName;
405 pInst->nInstanceSvr--;
406 pInst->nRefCount--;
407 if ( !pInst->nInstanceSvr && pInst->hDdeInstSvr )
409 if( DdeUninitialize( pInst->hDdeInstSvr ) )
411 pInst->hDdeInstSvr = 0;
412 delete pInst->pServicesSvr;
413 pInst->pServicesSvr = nullptr;
414 if( pInst->nRefCount == 0)
415 ImpDeinitInstData();
420 OUString DdeService::GetName() const
422 return pName->toOUString();
425 DdeServices& DdeService::GetServices()
427 DdeInstData* pInst = ImpGetInstData();
428 assert(pInst);
429 return *(pInst->pServicesSvr);
432 void DdeService::AddTopic( const DdeTopic& rTopic )
434 RemoveTopic( rTopic );
435 aTopics.push_back(const_cast<DdeTopic *>(&rTopic));
438 void DdeService::RemoveTopic( const DdeTopic& rTopic )
440 auto iter = std::find_if(aTopics.begin(), aTopics.end(),
441 [&rTopic](const DdeTopic* pTopic) { return DdeCmpStringHandles(pTopic->pName->getHSZ(), rTopic.pName->getHSZ()) == 0; });
442 if (iter != aTopics.end())
444 aTopics.erase(iter);
445 // Delete all conversions!
446 // Or else we work on deleted topics!
447 for( size_t n = m_vConv.size(); n; )
449 auto const& pC = m_vConv[ --n ];
450 if( pC->pTopic == &rTopic )
451 m_vConv.erase( m_vConv.begin() + n );
456 bool DdeService::HasCbFormat( sal_uInt32 nFmt )
458 return std::find(aFormats.begin(), aFormats.end(), nFmt) != aFormats.end();
461 bool DdeService::HasFormat(SotClipboardFormatId nFmt)
463 return HasCbFormat( DdeData::GetExternalFormat( nFmt ));
466 void DdeService::AddFormat(SotClipboardFormatId nFmt)
468 sal_uInt32 nExternalFmt = DdeData::GetExternalFormat( nFmt );
469 if (HasCbFormat(nExternalFmt))
470 return;
471 aFormats.push_back( nExternalFmt );
474 void DdeService::RemoveFormat(SotClipboardFormatId nFmt)
476 sal_uInt32 nExternalFmt = DdeData::GetExternalFormat( nFmt );
477 auto it = std::find(aFormats.begin(), aFormats.end(), nExternalFmt);
478 if (it != aFormats.end())
479 aFormats.erase( it );
482 DdeTopic::DdeTopic( const OUString& rName )
484 DdeInstData* pInst = ImpGetInstData();
485 assert(pInst);
486 pName = new DdeString( pInst->hDdeInstSvr, rName );
489 DdeTopic::~DdeTopic()
491 for (auto& rpItem : aItems)
493 rpItem->pMyTopic = nullptr;
494 delete rpItem;
497 delete pName;
500 OUString DdeTopic::GetName() const
502 return pName->toOUString();
505 bool DdeTopic::IsSystemTopic()
507 return GetName() == SZDDESYS_TOPIC;
510 DdeItem* DdeTopic::AddItem( const DdeItem& r )
512 DdeItem* s;
513 if( DDEGETPUTITEM == r.nType )
514 s = new DdeGetPutItem( r );
515 else
516 s = new DdeItem( r );
518 aItems.push_back( s );
519 s->pMyTopic = this;
520 return s;
523 void DdeTopic::InsertItem( DdeItem* pNew )
525 if( pNew )
527 aItems.push_back( pNew );
528 pNew->pMyTopic = this;
532 void DdeTopic::RemoveItem( const DdeItem& r )
534 auto iter = std::find_if(aItems.begin(), aItems.end(),
535 [&r](const DdeItem* pItem) { return DdeCmpStringHandles(pItem->pName->getHSZ(), r.pName->getHSZ()) == 0; });
537 if ( iter != aItems.end() )
539 (*iter)->pMyTopic = nullptr;
540 delete *iter;
541 aItems.erase(iter);
545 void DdeTopic::NotifyClient( const OUString& rItem )
547 DdeInstData* pInst = ImpGetInstData();
548 assert(pInst);
549 auto iter = std::find_if(aItems.begin(), aItems.end(),
550 [&rItem](const DdeItem* pItem) { return pItem->GetName().equals(rItem) && pItem->pImpData; });
551 if (iter != aItems.end())
552 DdePostAdvise( pInst->hDdeInstSvr, pName->getHSZ(), (*iter)->pName->getHSZ() );
555 void DdeInternal::DisconnectTopic(DdeTopic & rTopic, HCONV nId)
557 for (const auto& rpItem : rTopic.aItems)
559 DecMonitor(rpItem, nId);
563 DdeData* DdeTopic::Get(SotClipboardFormatId /*nFmt*/)
565 return nullptr;
568 bool DdeTopic::Put( const DdeData* )
570 return false;
573 bool DdeTopic::Execute( const OUString* )
575 return false;
578 bool DdeTopic::StartAdviseLoop()
580 return false;
583 DdeItem::DdeItem( const sal_Unicode* p )
585 DdeInstData* pInst = ImpGetInstData();
586 assert(pInst);
587 pName = new DdeString( pInst->hDdeInstSvr, OUString(p) );
588 nType = DDEITEM;
589 pMyTopic = nullptr;
590 pImpData = nullptr;
593 DdeItem::DdeItem( const OUString& r)
595 DdeInstData* pInst = ImpGetInstData();
596 assert(pInst);
597 pName = new DdeString( pInst->hDdeInstSvr, r );
598 nType = DDEITEM;
599 pMyTopic = nullptr;
600 pImpData = nullptr;
603 DdeItem::DdeItem( const DdeItem& r)
605 DdeInstData* pInst = ImpGetInstData();
606 assert(pInst);
607 pName = new DdeString( pInst->hDdeInstSvr, r.pName->toOUString() );
608 nType = DDEITEM;
609 pMyTopic = nullptr;
610 pImpData = nullptr;
613 DdeItem::~DdeItem()
615 if( pMyTopic )
616 std::erase(pMyTopic->aItems, this);
617 delete pName;
618 delete pImpData;
621 OUString DdeItem::GetName() const
623 return pName->toOUString();
626 void DdeItem::NotifyClient()
628 if( pMyTopic && pImpData )
630 DdeInstData* pInst = ImpGetInstData();
631 assert(pInst);
632 DdePostAdvise( pInst->hDdeInstSvr, pMyTopic->pName->getHSZ(), pName->getHSZ() );
636 void DdeInternal::IncMonitor(DdeItem *const pItem, HCONV nHCnv)
638 if (!pItem->pImpData)
640 pItem->pImpData = new std::vector<DdeItemImpData>;
641 if (DDEGETPUTITEM == pItem->nType)
643 static_cast<DdeGetPutItem*>(pItem)->AdviseLoop( true );
646 else
648 for (size_t n = pItem->pImpData->size(); n; )
650 if ((*pItem->pImpData)[ --n ].nHCnv == nHCnv)
652 ++(*pItem->pImpData)[ n ].nHCnv;
653 return ;
658 pItem->pImpData->push_back( DdeItemImpData( nHCnv ) );
661 void DdeInternal::DecMonitor(DdeItem *const pItem, HCONV nHCnv)
663 if (pItem->pImpData)
665 for( size_t n = 0; n < pItem->pImpData->size(); ++n )
667 DdeItemImpData* pData = &(*pItem->pImpData)[n];
668 if( pData->nHCnv == nHCnv )
670 if( !pData->nCnt || !--pData->nCnt )
672 if (1 < pItem->pImpData->size())
674 pItem->pImpData->erase(pItem->pImpData->begin() + n);
676 else
678 delete pItem->pImpData;
679 pItem->pImpData = nullptr;
680 if (DDEGETPUTITEM == pItem->nType)
682 static_cast<DdeGetPutItem*>(pItem)->AdviseLoop(false);
686 return ;
692 short DdeItem::GetLinks()
694 short nCnt = 0;
695 if( pImpData )
697 for (const auto& rData : *pImpData)
699 nCnt += rData.nCnt;
702 return nCnt;
705 DdeGetPutItem::DdeGetPutItem( const sal_Unicode* p )
706 : DdeItem( p )
708 nType = DDEGETPUTITEM;
711 DdeGetPutItem::DdeGetPutItem( const OUString& rStr )
712 : DdeItem( rStr )
714 nType = DDEGETPUTITEM;
717 DdeGetPutItem::DdeGetPutItem( const DdeItem& rItem )
718 : DdeItem( rItem )
720 nType = DDEGETPUTITEM;
723 DdeData* DdeGetPutItem::Get(SotClipboardFormatId)
725 return nullptr;
728 bool DdeGetPutItem::Put( const DdeData* )
730 return false;
733 void DdeGetPutItem::AdviseLoop( bool )
737 OUString DdeService::SysItems()
739 OUString s;
740 for ( const auto& rpTopic : aTopics )
742 if ( rpTopic->GetName() == SZDDESYS_TOPIC )
744 short n = 0;
745 for ( const auto& rpItem : rpTopic->aItems )
747 if ( n )
748 s += "\t";
749 s += rpItem->GetName();
750 n++;
752 s += "\r\n";
756 return s;
759 OUString DdeService::Topics()
761 OUString s;
762 short n = 0;
764 for ( const auto& rpTopic : aTopics )
766 if ( n )
767 s += "\t";
768 s += rpTopic->GetName();
769 n++;
771 s += "\r\n";
773 return s;
776 OUString DdeService::Formats()
778 OUString s;
779 short n = 0;
781 for (size_t i = 0; i < aFormats.size(); ++i, ++n)
783 sal_uInt32 f = aFormats[ i ];
784 if ( n )
785 s += "\t";
787 switch( f )
789 case CF_TEXT:
790 s += "TEXT";
791 break;
792 case CF_BITMAP:
793 s += "BITMAP";
794 break;
795 default:
797 WCHAR buf[128];
798 GetClipboardFormatNameW( f, buf, SAL_N_ELEMENTS(buf) );
799 s += o3tl::toU(buf);
801 break;
805 s += "\r\n";
807 return s;
810 OUString DdeService::Status()
812 return "Ready\r\n";
815 bool DdeTopic::MakeItem( const OUString& )
817 return false;
820 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */