tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / sfx2 / source / appl / appdde.cxx
blob2f013cfc63426d652f94475ab396bec1fc8afba5
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 <sal/config.h>
22 #include <string_view>
24 #include <config_features.h>
25 #include <rtl/character.hxx>
26 #include <rtl/malformeduriexception.hxx>
27 #include <rtl/uri.hxx>
28 #include <sot/exchange.hxx>
29 #include <svl/eitem.hxx>
30 #include <basic/sbstar.hxx>
31 #include <svl/stritem.hxx>
32 #include <svl/svdde.hxx>
33 #include <sfx2/lnkbase.hxx>
34 #include <sfx2/linkmgr.hxx>
36 #include <tools/debug.hxx>
37 #include <tools/urlobj.hxx>
38 #include <comphelper/diagnose_ex.hxx>
39 #include <unotools/pathoptions.hxx>
40 #include <vcl/svapp.hxx>
42 #include <sfx2/app.hxx>
43 #include <appdata.hxx>
44 #include <sfx2/objsh.hxx>
45 #include <sfx2/viewfrm.hxx>
46 #include <sfx2/dispatch.hxx>
47 #include <sfx2/sfxsids.hrc>
48 #include <sfx2/docfile.hxx>
49 #include <ucbhelper/content.hxx>
50 #include <comphelper/processfactory.hxx>
52 #if defined(_WIN32)
54 static OUString SfxDdeServiceName_Impl( const OUString& sIn )
56 OUStringBuffer sReturn(sIn.getLength());
58 for ( sal_uInt16 n = sIn.getLength(); n; --n )
60 sal_Unicode cChar = sIn[n-1];
61 if (rtl::isAsciiAlphanumeric(cChar))
62 sReturn.append(cChar);
65 return sReturn.makeStringAndClear();
68 namespace {
70 class ImplDdeService : public DdeService
72 public:
73 explicit ImplDdeService( const OUString& rNm )
74 : DdeService( rNm )
76 virtual bool MakeTopic( const OUString& );
78 virtual OUString Topics();
80 virtual bool SysTopicExecute( const OUString* pStr );
83 bool lcl_IsDocument( std::u16string_view rContent )
85 using namespace com::sun::star;
87 bool bRet = false;
88 INetURLObject aObj( rContent );
89 DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
91 try
93 ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
94 bRet = aCnt.isDocument();
96 catch( const uno::Exception& )
98 TOOLS_WARN_EXCEPTION( "sfx.appl", "" );
101 return bRet;
105 bool ImplDdeService::MakeTopic( const OUString& rNm )
107 // Workaround for Event after Main() under OS/2
108 // happens when exiting starts the App again
109 if ( !Application::IsInExecute() )
110 return false;
112 // The Topic rNm is sought, do we have it?
113 // First only loop over the ObjectShells to find those
114 // with the specific name:
115 bool bRet = false;
116 OUString sNm( rNm.toAsciiLowerCase() );
117 SfxObjectShell* pShell = SfxObjectShell::GetFirst();
118 while( pShell )
120 OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
121 if( sNm == sTmp.toAsciiLowerCase() )
123 SfxGetpApp()->AddDdeTopic( pShell );
124 bRet = true;
125 break;
127 pShell = SfxObjectShell::GetNext( *pShell );
130 if( !bRet )
132 bool abs;
133 OUString url;
134 try {
135 url = rtl::Uri::convertRelToAbs(SvtPathOptions().GetWorkPath(), rNm);
136 abs = true;
137 } catch (rtl::MalformedUriException &) {
138 abs = false;
140 if ( abs && lcl_IsDocument( url ) )
142 // File exists? then try to load it:
143 SfxStringItem aName( SID_FILE_NAME, url );
144 SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, true);
146 SfxBoolItem aSilent(SID_SILENT, true);
147 const SfxPoolItemHolder aResult(SfxGetpApp()->GetDispatcher_Impl()->ExecuteList(SID_OPENDOC,
148 SfxCallMode::SYNCHRON,
149 { &aName, &aNewView, &aSilent }));
151 if( auto const item = dynamic_cast< const SfxViewFrameItem *>(aResult.getItem());
152 item &&
153 item->GetFrame() &&
154 nullptr != ( pShell = item->GetFrame()->GetObjectShell() ) )
156 SfxGetpApp()->AddDdeTopic( pShell );
157 bRet = true;
161 return bRet;
164 OUString ImplDdeService::Topics()
166 OUString sRet;
167 if( GetSysTopic() )
168 sRet += GetSysTopic()->GetName();
170 SfxObjectShell* pShell = SfxObjectShell::GetFirst();
171 while( pShell )
173 if( SfxViewFrame::GetFirst( pShell ) )
175 if( !sRet.isEmpty() )
176 sRet += "\t";
177 sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
179 pShell = SfxObjectShell::GetNext( *pShell );
181 if( !sRet.isEmpty() )
182 sRet += "\r\n";
183 return sRet;
186 bool ImplDdeService::SysTopicExecute( const OUString* pStr )
188 return SfxApplication::DdeExecute( *pStr );
190 #endif
192 class SfxDdeDocTopic_Impl : public DdeTopic
194 #if defined(_WIN32)
195 public:
196 SfxObjectShell* pSh;
197 DdeData aData;
198 css::uno::Sequence< sal_Int8 > aSeq;
200 explicit SfxDdeDocTopic_Impl( SfxObjectShell* pShell )
201 : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell )
204 virtual DdeData* Get( SotClipboardFormatId ) override;
205 virtual bool Put( const DdeData* ) override;
206 virtual bool Execute( const OUString* ) override;
207 virtual bool StartAdviseLoop() override;
208 virtual bool MakeItem( const OUString& rItem ) override;
209 #endif
213 #if defined(_WIN32)
215 namespace {
217 /* [Description]
219 Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
220 this data into a <ApplicationEvent>, which is then executed through
221 <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
222 TRUE is returned, otherwise FALSE.
224 [Example]
226 rCmd = "Open(\"d:\doc\doc.sdw\")"
227 rEvent = "Open"
229 bool SfxAppEvent_Impl( const OUString& rCmd, std::u16string_view rEvent,
230 ApplicationEvent::Type eType )
232 OUString sEvent(OUString::Concat(rEvent) + "(");
233 if (rCmd.startsWithIgnoreAsciiCase(sEvent))
235 sal_Int32 start = sEvent.getLength();
236 if ( rCmd.getLength() - start >= 2 )
238 // Transform into the ApplicationEvent Format
239 //TODO: I /assume/ that rCmd should match the syntax of
240 // <http://msdn.microsoft.com/en-us/library/ms648995.aspx>
241 // "WM_DDE_EXECUTE message" but does not (handle commands enclosed
242 // in [...]; handle commas separating multiple arguments; handle
243 // double "", ((, )), [[, ]] in quoted arguments); see also the mail
244 // thread starting at <http://lists.freedesktop.org/archives/
245 // libreoffice/2013-July/054779.html> "DDE on Windows."
246 std::vector<OUString> aData;
247 for ( sal_Int32 n = start; n < rCmd.getLength() - 1; )
249 // Resiliently read arguments either starting with " and
250 // spanning to the next " (if any; TODO: do we need to undo any
251 // escaping within the string?) or with neither " nor SPC and
252 // spanning to the next SPC (if any; TODO: is this from not
253 // wrapped in "..." relevant? it would have been parsed by the
254 // original code even if that was only by accident, so I left it
255 // in), with runs of SPCs treated like single ones:
256 switch ( rCmd[n] )
258 case '"':
260 sal_Int32 i = rCmd.indexOf('"', ++n);
261 if (i < 0 || i > rCmd.getLength() - 1) {
262 i = rCmd.getLength() - 1;
264 aData.push_back(rCmd.copy(n, i - n));
265 n = i + 1;
266 break;
268 case ' ':
269 ++n;
270 break;
271 default:
273 sal_Int32 i = rCmd.indexOf(' ', n);
274 if (i < 0 || i > rCmd.getLength() - 1) {
275 i = rCmd.getLength() - 1;
277 aData.push_back(rCmd.copy(n, i - n));
278 n = i + 1;
279 break;
284 GetpApp()->AppEvent( ApplicationEvent(eType, std::move(aData)) );
285 return true;
289 return false;
294 /* Description]
296 This method can be overridden by application developers, to receive
297 DDE-commands directed to their SfxApplication subclass.
299 The base implementation understands the API functionality of the
300 relevant SfxApplication subclass in BASIC syntax. Return values can
301 not be transferred, unfortunately.
303 bool SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
305 // Print or Open-Event?
306 if ( !( SfxAppEvent_Impl( rCmd, u"Print", ApplicationEvent::Type::Print ) ||
307 SfxAppEvent_Impl( rCmd, u"Open", ApplicationEvent::Type::Open ) ) )
309 // all others are BASIC
310 StarBASIC* pBasic = GetBasic();
311 DBG_ASSERT( pBasic, "Where is the Basic???" );
312 SbxVariable* pRet = pBasic->Execute( rCmd );
313 if( !pRet )
315 SbxBase::ResetError();
316 return false;
319 return true;
322 /* [Description]
324 This method can be overridden by application developers, to receive
325 DDE-commands directed to the their SfxApplication subclass.
327 The base implementation does nothing and returns 0.
329 bool SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
331 #if !HAVE_FEATURE_SCRIPTING
332 (void) rCmd;
333 #else
334 StarBASIC* pBasic = GetBasic();
335 DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
336 SbxVariable* pRet = pBasic->Execute( rCmd );
337 if( !pRet )
339 SbxBase::ResetError();
340 return false;
342 #endif
343 return true;
346 /* [Description]
348 This method can be overridden by application developers, to receive
349 DDE-data-requests directed to their SfxApplication subclass.
351 The base implementation provides no data and returns false.
353 bool SfxObjectShell::DdeGetData( const OUString&, // the Item to be addressed
354 const OUString&, // in: Format
355 css::uno::Any& )// out: requested data
357 return false;
361 /* [Description]
363 This method can be overridden by application developers, to receive
364 DDE-data directed to their SfxApplication subclass.
366 The base implementation is not receiving any data and returns false.
368 bool SfxObjectShell::DdeSetData( const OUString&, // the Item to be addressed
369 const OUString&, // in: Format
370 const css::uno::Any& )// out: requested data
372 return false;
375 #endif
377 /* [Description]
379 This method can be overridden by application developers, to establish
380 a DDE-hotlink to their SfxApplication subclass.
382 The base implementation is not generate a link and returns 0.
384 ::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed
386 return nullptr;
389 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
393 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
395 SfxObjectShell* p = GetFirst(nullptr, false);
396 while (p)
398 if (&rServer != p)
399 p->ReconnectDdeLink(rServer);
401 p = GetNext(*p, nullptr, false);
405 bool SfxApplication::InitializeDde()
407 int nError = 0;
408 #if defined(_WIN32)
409 DBG_ASSERT( !pImpl->pDdeService,
410 "Dde can not be initialized multiple times" );
412 pImpl->pDdeService.reset(new ImplDdeService( Application::GetAppName() ));
413 nError = pImpl->pDdeService->GetError();
414 if( !nError )
416 // we certainly want to support RTF!
417 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RTF );
418 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RICHTEXT );
420 // Config path as a topic because of multiple starts
421 INetURLObject aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
422 aOfficeLockFile.insertName( u"soffice.lck" );
423 OUString aService( SfxDdeServiceName_Impl(
424 aOfficeLockFile.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) ) );
425 aService = aService.toAsciiUpperCase();
426 pImpl->pDdeService2.reset( new ImplDdeService( aService ));
427 pImpl->pTriggerTopic.reset(new SfxDdeTriggerTopic_Impl);
428 pImpl->pDdeService2->AddTopic( *pImpl->pTriggerTopic );
430 #endif
431 return !nError;
434 void SfxAppData_Impl::DeInitDDE()
436 pTriggerTopic.reset();
437 pDdeService2.reset();
438 maDocTopics.clear();
439 pDdeService.reset();
442 #if defined(_WIN32)
443 void SfxApplication::AddDdeTopic( SfxObjectShell* pSh )
445 //OV: DDE is disconnected in server mode!
446 if( pImpl->maDocTopics.empty() )
447 return;
449 // prevent double submit
450 OUString sShellNm;
451 bool bFnd = false;
452 for (size_t n = pImpl->maDocTopics.size(); n;)
454 if( pImpl->maDocTopics[ --n ]->pSh == pSh )
456 // If the document is untitled, is still a new Topic is created!
457 if( !bFnd )
459 bFnd = true;
460 sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
462 OUString sNm( pImpl->maDocTopics[ n ]->GetName() );
463 if( sShellNm == sNm.toAsciiLowerCase() )
464 return ;
468 SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
469 pImpl->maDocTopics.push_back(pTopic);
470 pImpl->pDdeService->AddTopic( *pTopic );
472 #endif
474 void SfxApplication::RemoveDdeTopic( SfxObjectShell const * pSh )
476 #if defined(_WIN32)
477 //OV: DDE is disconnected in server mode!
478 if( pImpl->maDocTopics.empty() )
479 return;
481 for (size_t n = pImpl->maDocTopics.size(); n; )
483 SfxDdeDocTopic_Impl *const pTopic = pImpl->maDocTopics[ --n ];
484 if (pTopic->pSh == pSh)
486 pImpl->pDdeService->RemoveTopic( *pTopic );
487 delete pTopic;
488 pImpl->maDocTopics.erase( pImpl->maDocTopics.begin() + n );
491 #else
492 (void) pSh;
493 #endif
496 const DdeService* SfxApplication::GetDdeService() const
498 return pImpl->pDdeService.get();
501 DdeService* SfxApplication::GetDdeService()
503 return pImpl->pDdeService.get();
506 #if defined(_WIN32)
508 DdeData* SfxDdeDocTopic_Impl::Get(SotClipboardFormatId nFormat)
510 OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
511 css::uno::Any aValue;
512 bool bRet = pSh->DdeGetData( GetCurItem(), sMimeType, aValue );
513 if( bRet && aValue.hasValue() && ( aValue >>= aSeq ) )
515 aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
516 return &aData;
518 aSeq.realloc( 0 );
519 return nullptr;
522 bool SfxDdeDocTopic_Impl::Put( const DdeData* pData )
524 aSeq = css::uno::Sequence< sal_Int8 >(
525 static_cast<sal_Int8 const *>(pData->getData()), pData->getSize() );
526 bool bRet;
527 if( aSeq.getLength() )
529 css::uno::Any aValue;
530 aValue <<= aSeq;
531 OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
532 bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
534 else
535 bRet = false;
536 return bRet;
539 bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
541 return pStr && pSh->DdeExecute( *pStr );
544 bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
546 AddItem( DdeItem( rItem ) );
547 return true;
550 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
552 bool bRet = false;
553 ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() );
554 if( pNewObj )
556 // then we also establish a corresponding SvBaseLink
557 OUString sNm, sTmp( Application::GetAppName() );
558 ::sfx2::MakeLnkName( sNm, &sTmp, pSh->GetTitle(SFX_TITLE_FULLNAME), GetCurItem() );
559 new ::sfx2::SvBaseLink( sNm, sfx2::SvBaseLinkObjectType::DdeExternal, pNewObj );
560 bRet = true;
562 return bRet;
565 #endif
567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */