LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / appl / appdde.cxx
blobb5c6ee54b6fc6164d09b25fd0406373d3df2d939
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 <tools/diagnose_ex.h>
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( const OUString& 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 SfxDispatcher* pDispatcher = SfxGetpApp()->GetDispatcher_Impl();
148 const SfxPoolItem* pRet = pDispatcher->ExecuteList(SID_OPENDOC,
149 SfxCallMode::SYNCHRON,
150 { &aName, &aNewView, &aSilent });
152 if( auto const item = dynamic_cast< const SfxViewFrameItem *>( pRet );
153 item &&
154 item->GetFrame() &&
155 nullptr != ( pShell = item->GetFrame()->GetObjectShell() ) )
157 SfxGetpApp()->AddDdeTopic( pShell );
158 bRet = true;
162 return bRet;
165 OUString ImplDdeService::Topics()
167 OUString sRet;
168 if( GetSysTopic() )
169 sRet += GetSysTopic()->GetName();
171 SfxObjectShell* pShell = SfxObjectShell::GetFirst();
172 while( pShell )
174 if( SfxViewFrame::GetFirst( pShell ) )
176 if( !sRet.isEmpty() )
177 sRet += "\t";
178 sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
180 pShell = SfxObjectShell::GetNext( *pShell );
182 if( !sRet.isEmpty() )
183 sRet += "\r\n";
184 return sRet;
187 bool ImplDdeService::SysTopicExecute( const OUString* pStr )
189 return SfxApplication::DdeExecute( *pStr );
191 #endif
193 class SfxDdeDocTopic_Impl : public DdeTopic
195 #if defined(_WIN32)
196 public:
197 SfxObjectShell* pSh;
198 DdeData aData;
199 css::uno::Sequence< sal_Int8 > aSeq;
201 explicit SfxDdeDocTopic_Impl( SfxObjectShell* pShell )
202 : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell )
205 virtual DdeData* Get( SotClipboardFormatId ) override;
206 virtual bool Put( const DdeData* ) override;
207 virtual bool Execute( const OUString* ) override;
208 virtual bool StartAdviseLoop() override;
209 virtual bool MakeItem( const OUString& rItem ) override;
210 #endif
214 #if defined(_WIN32)
216 namespace {
218 /* [Description]
220 Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
221 this data into a <ApplicationEvent>, which is then executed through
222 <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
223 TRUE is returned, otherwise FALSE.
225 [Example]
227 rCmd = "Open(\"d:\doc\doc.sdw\")"
228 rEvent = "Open"
230 bool SfxAppEvent_Impl( const OUString& rCmd, std::u16string_view rEvent,
231 ApplicationEvent::Type eType )
233 OUString sEvent(OUString::Concat(rEvent) + "(");
234 if (rCmd.startsWithIgnoreAsciiCase(sEvent))
236 sal_Int32 start = sEvent.getLength();
237 if ( rCmd.getLength() - start >= 2 )
239 // Transform into the ApplicationEvent Format
240 //TODO: I /assume/ that rCmd should match the syntax of
241 // <http://msdn.microsoft.com/en-us/library/ms648995.aspx>
242 // "WM_DDE_EXECUTE message" but does not (handle commands enclosed
243 // in [...]; handle commas separating multiple arguments; handle
244 // double "", ((, )), [[, ]] in quoted arguments); see also the mail
245 // thread starting at <http://lists.freedesktop.org/archives/
246 // libreoffice/2013-July/054779.html> "DDE on Windows."
247 std::vector<OUString> aData;
248 for ( sal_Int32 n = start; n < rCmd.getLength() - 1; )
250 // Resiliently read arguments either starting with " and
251 // spanning to the next " (if any; TODO: do we need to undo any
252 // escaping within the string?) or with neither " nor SPC and
253 // spanning to the next SPC (if any; TODO: is this from not
254 // wrapped in "..." relevant? it would have been parsed by the
255 // original code even if that was only by accident, so I left it
256 // in), with runs of SPCs treated like single ones:
257 switch ( rCmd[n] )
259 case '"':
261 sal_Int32 i = rCmd.indexOf('"', ++n);
262 if (i < 0 || i > rCmd.getLength() - 1) {
263 i = rCmd.getLength() - 1;
265 aData.push_back(rCmd.copy(n, i - n));
266 n = i + 1;
267 break;
269 case ' ':
270 ++n;
271 break;
272 default:
274 sal_Int32 i = rCmd.indexOf(' ', n);
275 if (i < 0 || i > rCmd.getLength() - 1) {
276 i = rCmd.getLength() - 1;
278 aData.push_back(rCmd.copy(n, i - n));
279 n = i + 1;
280 break;
285 GetpApp()->AppEvent( ApplicationEvent(eType, std::move(aData)) );
286 return true;
290 return false;
295 /* Description]
297 This method can be overridden by application developers, to receive
298 DDE-commands directed to their SfxApplication subclass.
300 The base implementation understands the API functionality of the
301 relevant SfxApplication subclass in BASIC syntax. Return values can
302 not be transferred, unfortunately.
304 bool SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
306 // Print or Open-Event?
307 if ( !( SfxAppEvent_Impl( rCmd, u"Print", ApplicationEvent::Type::Print ) ||
308 SfxAppEvent_Impl( rCmd, u"Open", ApplicationEvent::Type::Open ) ) )
310 // all others are BASIC
311 StarBASIC* pBasic = GetBasic();
312 DBG_ASSERT( pBasic, "Where is the Basic???" );
313 SbxVariable* pRet = pBasic->Execute( rCmd );
314 if( !pRet )
316 SbxBase::ResetError();
317 return false;
320 return true;
323 /* [Description]
325 This method can be overridden by application developers, to receive
326 DDE-commands directed to the their SfxApplication subclass.
328 The base implementation does nothing and returns 0.
330 bool SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
332 #if !HAVE_FEATURE_SCRIPTING
333 (void) rCmd;
334 #else
335 StarBASIC* pBasic = GetBasic();
336 DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
337 SbxVariable* pRet = pBasic->Execute( rCmd );
338 if( !pRet )
340 SbxBase::ResetError();
341 return false;
343 #endif
344 return true;
347 /* [Description]
349 This method can be overridden by application developers, to receive
350 DDE-data-requests directed to their SfxApplication subclass.
352 The base implementation provides no data and returns false.
354 bool SfxObjectShell::DdeGetData( const OUString&, // the Item to be addressed
355 const OUString&, // in: Format
356 css::uno::Any& )// out: requested data
358 return false;
362 /* [Description]
364 This method can be overridden by application developers, to receive
365 DDE-data directed to their SfxApplication subclass.
367 The base implementation is not receiving any data and returns false.
369 bool SfxObjectShell::DdeSetData( const OUString&, // the Item to be addressed
370 const OUString&, // in: Format
371 const css::uno::Any& )// out: requested data
373 return false;
376 #endif
378 /* [Description]
380 This method can be overridden by application developers, to establish
381 a DDE-hotlink to their SfxApplication subclass.
383 The base implementation is not generate a link and returns 0.
385 ::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed
387 return nullptr;
390 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
394 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
396 SfxObjectShell* p = GetFirst(nullptr, false);
397 while (p)
399 if (&rServer != p)
400 p->ReconnectDdeLink(rServer);
402 p = GetNext(*p, nullptr, false);
406 bool SfxApplication::InitializeDde()
408 int nError = 0;
409 #if defined(_WIN32)
410 DBG_ASSERT( !pImpl->pDdeService,
411 "Dde can not be initialized multiple times" );
413 pImpl->pDdeService.reset(new ImplDdeService( Application::GetAppName() ));
414 nError = pImpl->pDdeService->GetError();
415 if( !nError )
417 pImpl->pDocTopics.reset(new SfxDdeDocTopics_Impl);
419 // we certainly want to support RTF!
420 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RTF );
421 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RICHTEXT );
423 // Config path as a topic because of multiple starts
424 INetURLObject aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
425 aOfficeLockFile.insertName( u"soffice.lck" );
426 OUString aService( SfxDdeServiceName_Impl(
427 aOfficeLockFile.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) ) );
428 aService = aService.toAsciiUpperCase();
429 pImpl->pDdeService2.reset( new ImplDdeService( aService ));
430 pImpl->pTriggerTopic.reset(new SfxDdeTriggerTopic_Impl);
431 pImpl->pDdeService2->AddTopic( *pImpl->pTriggerTopic );
433 #endif
434 return !nError;
437 void SfxAppData_Impl::DeInitDDE()
439 pTriggerTopic.reset();
440 pDdeService2.reset();
441 pDocTopics.reset();
442 pDdeService.reset();
445 #if defined(_WIN32)
446 void SfxApplication::AddDdeTopic( SfxObjectShell* pSh )
448 //OV: DDE is disconnected in server mode!
449 if( !pImpl->pDocTopics )
450 return;
452 // prevent double submit
453 OUString sShellNm;
454 bool bFnd = false;
455 for (size_t n = pImpl->pDocTopics->size(); n;)
457 if( (*pImpl->pDocTopics)[ --n ]->pSh == pSh )
459 // If the document is untitled, is still a new Topic is created!
460 if( !bFnd )
462 bFnd = true;
463 sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
465 OUString sNm( (*pImpl->pDocTopics)[ n ]->GetName() );
466 if( sShellNm == sNm.toAsciiLowerCase() )
467 return ;
471 SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
472 pImpl->pDocTopics->push_back(pTopic);
473 pImpl->pDdeService->AddTopic( *pTopic );
475 #endif
477 void SfxApplication::RemoveDdeTopic( SfxObjectShell const * pSh )
479 #if defined(_WIN32)
480 //OV: DDE is disconnected in server mode!
481 if( !pImpl->pDocTopics )
482 return;
484 for (size_t n = pImpl->pDocTopics->size(); n; )
486 SfxDdeDocTopic_Impl *const pTopic = (*pImpl->pDocTopics)[ --n ];
487 if (pTopic->pSh == pSh)
489 pImpl->pDdeService->RemoveTopic( *pTopic );
490 delete pTopic;
491 pImpl->pDocTopics->erase( pImpl->pDocTopics->begin() + n );
494 #else
495 (void) pSh;
496 #endif
499 const DdeService* SfxApplication::GetDdeService() const
501 return pImpl->pDdeService.get();
504 DdeService* SfxApplication::GetDdeService()
506 return pImpl->pDdeService.get();
509 #if defined(_WIN32)
511 DdeData* SfxDdeDocTopic_Impl::Get(SotClipboardFormatId nFormat)
513 OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
514 css::uno::Any aValue;
515 bool bRet = pSh->DdeGetData( GetCurItem(), sMimeType, aValue );
516 if( bRet && aValue.hasValue() && ( aValue >>= aSeq ) )
518 aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
519 return &aData;
521 aSeq.realloc( 0 );
522 return nullptr;
525 bool SfxDdeDocTopic_Impl::Put( const DdeData* pData )
527 aSeq = css::uno::Sequence< sal_Int8 >(
528 static_cast<sal_Int8 const *>(pData->getData()), pData->getSize() );
529 bool bRet;
530 if( aSeq.getLength() )
532 css::uno::Any aValue;
533 aValue <<= aSeq;
534 OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
535 bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
537 else
538 bRet = false;
539 return bRet;
542 bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
544 return pStr && pSh->DdeExecute( *pStr );
547 bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
549 AddItem( DdeItem( rItem ) );
550 return true;
553 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
555 bool bRet = false;
556 ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() );
557 if( pNewObj )
559 // then we also establish a corresponding SvBaseLink
560 OUString sNm, sTmp( Application::GetAppName() );
561 ::sfx2::MakeLnkName( sNm, &sTmp, pSh->GetTitle(SFX_TITLE_FULLNAME), GetCurItem() );
562 new ::sfx2::SvBaseLink( sNm, sfx2::SvBaseLinkObjectType::DdeExternal, pNewObj );
563 bRet = true;
565 return bRet;
568 #endif
570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */