Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / appl / appdde.cxx
blob9c2e21dcc426165e90978d2ea3df3de4a535b42c
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 <config_features.h>
21 #include <rtl/character.hxx>
22 #include <rtl/malformeduriexception.hxx>
23 #include <rtl/uri.hxx>
24 #include <sot/exchange.hxx>
25 #include <svl/eitem.hxx>
26 #include <basic/sbstar.hxx>
27 #include <svl/stritem.hxx>
28 #include <svl/svdde.hxx>
29 #include <sfx2/lnkbase.hxx>
30 #include <sfx2/linkmgr.hxx>
32 #include <tools/debug.hxx>
33 #include <tools/urlobj.hxx>
34 #include <tools/diagnose_ex.h>
35 #include <unotools/pathoptions.hxx>
36 #include <vcl/svapp.hxx>
38 #include <sfx2/app.hxx>
39 #include <appdata.hxx>
40 #include <sfx2/objsh.hxx>
41 #include <sfx2/viewfrm.hxx>
42 #include <sfx2/dispatch.hxx>
43 #include <sfx2/sfxsids.hrc>
44 #include <sfx2/docfile.hxx>
45 #include <ucbhelper/content.hxx>
46 #include <comphelper/processfactory.hxx>
48 #if defined(_WIN32)
50 static OUString SfxDdeServiceName_Impl( const OUString& sIn )
52 OUStringBuffer sReturn(sIn.getLength());
54 for ( sal_uInt16 n = sIn.getLength(); n; --n )
56 sal_Unicode cChar = sIn[n-1];
57 if (rtl::isAsciiAlphanumeric(cChar))
58 sReturn.append(cChar);
61 return sReturn.makeStringAndClear();
64 namespace {
66 class ImplDdeService : public DdeService
68 public:
69 explicit ImplDdeService( const OUString& rNm )
70 : DdeService( rNm )
72 virtual bool MakeTopic( const OUString& );
74 virtual OUString Topics();
76 virtual bool SysTopicExecute( const OUString* pStr );
79 bool lcl_IsDocument( const OUString& rContent )
81 using namespace com::sun::star;
83 bool bRet = false;
84 INetURLObject aObj( rContent );
85 DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
87 try
89 ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
90 bRet = aCnt.isDocument();
92 catch( const uno::Exception& )
94 TOOLS_WARN_EXCEPTION( "sfx.appl", "" );
97 return bRet;
101 bool ImplDdeService::MakeTopic( const OUString& rNm )
103 // Workaround for Event after Main() under OS/2
104 // happens when exiting starts the App again
105 if ( !Application::IsInExecute() )
106 return false;
108 // The Topic rNm is sought, do we have it?
109 // First only loop over the ObjectShells to find those
110 // with the specific name:
111 bool bRet = false;
112 OUString sNm( rNm.toAsciiLowerCase() );
113 SfxObjectShell* pShell = SfxObjectShell::GetFirst();
114 while( pShell )
116 OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
117 if( sNm == sTmp.toAsciiLowerCase() )
119 SfxGetpApp()->AddDdeTopic( pShell );
120 bRet = true;
121 break;
123 pShell = SfxObjectShell::GetNext( *pShell );
126 if( !bRet )
128 bool abs;
129 OUString url;
130 try {
131 url = rtl::Uri::convertRelToAbs(SvtPathOptions().GetWorkPath(), rNm);
132 abs = true;
133 } catch (rtl::MalformedUriException &) {
134 abs = false;
136 if ( abs && lcl_IsDocument( url ) )
138 // File exists? then try to load it:
139 SfxStringItem aName( SID_FILE_NAME, url );
140 SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, true);
142 SfxBoolItem aSilent(SID_SILENT, true);
143 SfxDispatcher* pDispatcher = SfxGetpApp()->GetDispatcher_Impl();
144 const SfxPoolItem* pRet = pDispatcher->ExecuteList(SID_OPENDOC,
145 SfxCallMode::SYNCHRON,
146 { &aName, &aNewView, &aSilent });
148 if( dynamic_cast< const SfxViewFrameItem *>( pRet ) &&
149 static_cast<SfxViewFrameItem const *>(pRet)->GetFrame() &&
150 nullptr != ( pShell = static_cast<SfxViewFrameItem const *>(pRet)
151 ->GetFrame()->GetObjectShell() ) )
153 SfxGetpApp()->AddDdeTopic( pShell );
154 bRet = true;
158 return bRet;
161 OUString ImplDdeService::Topics()
163 OUString sRet;
164 if( GetSysTopic() )
165 sRet += GetSysTopic()->GetName();
167 SfxObjectShell* pShell = SfxObjectShell::GetFirst();
168 while( pShell )
170 if( SfxViewFrame::GetFirst( pShell ) )
172 if( !sRet.isEmpty() )
173 sRet += "\t";
174 sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
176 pShell = SfxObjectShell::GetNext( *pShell );
178 if( !sRet.isEmpty() )
179 sRet += "\r\n";
180 return sRet;
183 bool ImplDdeService::SysTopicExecute( const OUString* pStr )
185 return SfxApplication::DdeExecute( *pStr );
187 #endif
189 class SfxDdeDocTopic_Impl : public DdeTopic
191 #if defined(_WIN32)
192 public:
193 SfxObjectShell* pSh;
194 DdeData aData;
195 css::uno::Sequence< sal_Int8 > aSeq;
197 explicit SfxDdeDocTopic_Impl( SfxObjectShell* pShell )
198 : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell )
201 virtual DdeData* Get( SotClipboardFormatId ) override;
202 virtual bool Put( const DdeData* ) override;
203 virtual bool Execute( const OUString* ) override;
204 virtual bool StartAdviseLoop() override;
205 virtual bool MakeItem( const OUString& rItem ) override;
206 #endif
210 #if defined(_WIN32)
212 namespace {
214 /* [Description]
216 Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
217 this data into a <ApplicationEvent>, which is then executed through
218 <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
219 TRUE is returned, otherwise FALSE.
221 [Example]
223 rCmd = "Open(\"d:\doc\doc.sdw\")"
224 rEvent = "Open"
226 bool SfxAppEvent_Impl( const OUString& rCmd, const OUString& rEvent,
227 ApplicationEvent::Type eType )
229 OUString sEvent(rEvent + "(");
230 if (rCmd.startsWithIgnoreAsciiCase(sEvent))
232 sal_Int32 start = sEvent.getLength();
233 if ( rCmd.getLength() - start >= 2 )
235 // Transform into the ApplicationEvent Format
236 //TODO: I /assume/ that rCmd should match the syntax of
237 // <http://msdn.microsoft.com/en-us/library/ms648995.aspx>
238 // "WM_DDE_EXECUTE message" but does not (handle commands enclosed
239 // in [...]; handle commas separating multiple arguments; handle
240 // double "", ((, )), [[, ]] in quoted arguments); see also the mail
241 // thread starting at <http://lists.freedesktop.org/archives/
242 // libreoffice/2013-July/054779.html> "DDE on Windows."
243 std::vector<OUString> aData;
244 for ( sal_Int32 n = start; n < rCmd.getLength() - 1; )
246 // Resiliently read arguments either starting with " and
247 // spanning to the next " (if any; TODO: do we need to undo any
248 // escaping within the string?) or with neither " nor SPC and
249 // spanning to the next SPC (if any; TODO: is this from not
250 // wrapped in "..." relevant? it would have been parsed by the
251 // original code even if that was only by accident, so I left it
252 // in), with runs of SPCs treated like single ones:
253 switch ( rCmd[n] )
255 case '"':
257 sal_Int32 i = rCmd.indexOf('"', ++n);
258 if (i < 0 || i > rCmd.getLength() - 1) {
259 i = rCmd.getLength() - 1;
261 aData.push_back(rCmd.copy(n, i - n));
262 n = i + 1;
263 break;
265 case ' ':
266 ++n;
267 break;
268 default:
270 sal_Int32 i = rCmd.indexOf(' ', n);
271 if (i < 0 || i > rCmd.getLength() - 1) {
272 i = rCmd.getLength() - 1;
274 aData.push_back(rCmd.copy(n, i - n));
275 n = i + 1;
276 break;
281 GetpApp()->AppEvent( ApplicationEvent(eType, aData) );
282 return true;
286 return false;
291 /* Description]
293 This method can be overridden by application developers, to receive
294 DDE-commands directed to their SfxApplication subclass.
296 The base implementation understands the API functionality of the
297 relevant SfxApplication subclass in BASIC syntax. Return values can
298 not be transferred, unfortunately.
300 long SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
302 // Print or Open-Event?
303 if ( !( SfxAppEvent_Impl( rCmd, "Print", ApplicationEvent::Type::Print ) ||
304 SfxAppEvent_Impl( rCmd, "Open", ApplicationEvent::Type::Open ) ) )
306 // all others are BASIC
307 StarBASIC* pBasic = GetBasic();
308 DBG_ASSERT( pBasic, "Where is the Basic???" );
309 SbxVariable* pRet = pBasic->Execute( rCmd );
310 if( !pRet )
312 SbxBase::ResetError();
313 return 0;
316 return 1;
319 /* [Description]
321 This method can be overridden by application developers, to receive
322 DDE-commands directed to the their SfxApplication subclass.
324 The base implementation does nothing and returns 0.
326 long SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
328 #if !HAVE_FEATURE_SCRIPTING
329 (void) rCmd;
330 #else
331 StarBASIC* pBasic = GetBasic();
332 DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
333 SbxVariable* pRet = pBasic->Execute( rCmd );
334 if( !pRet )
336 SbxBase::ResetError();
337 return 0;
339 #endif
340 return 1;
343 /* [Description]
345 This method can be overridden by application developers, to receive
346 DDE-data-requests directed to their SfxApplication subclass.
348 The base implementation provides no data and returns false.
350 bool SfxObjectShell::DdeGetData( const OUString&, // the Item to be addressed
351 const OUString&, // in: Format
352 css::uno::Any& )// out: requested data
354 return false;
358 /* [Description]
360 This method can be overridden by application developers, to receive
361 DDE-data directed to their SfxApplication subclass.
363 The base implementation is not receiving any data and returns false.
365 bool SfxObjectShell::DdeSetData( const OUString&, // the Item to be addressed
366 const OUString&, // in: Format
367 const css::uno::Any& )// out: requested data
369 return false;
372 #endif
374 /* [Description]
376 This method can be overridden by application developers, to establish
377 a DDE-hotlink to their SfxApplication subclass.
379 The base implementation is not generate a link and returns 0.
381 ::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed
383 return nullptr;
386 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
390 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
392 SfxObjectShell* p = GetFirst(nullptr, false);
393 while (p)
395 if (&rServer != p)
396 p->ReconnectDdeLink(rServer);
398 p = GetNext(*p, nullptr, false);
402 bool SfxApplication::InitializeDde()
404 int nError = 0;
405 #if defined(_WIN32)
406 DBG_ASSERT( !pImpl->pDdeService,
407 "Dde can not be initialized multiple times" );
409 pImpl->pDdeService.reset(new ImplDdeService( Application::GetAppName() ));
410 nError = pImpl->pDdeService->GetError();
411 if( !nError )
413 pImpl->pDocTopics.reset(new SfxDdeDocTopics_Impl);
415 // we certainly want to support RTF!
416 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RTF );
417 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RICHTEXT );
419 // Config path as a topic because of multiple starts
420 INetURLObject aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
421 aOfficeLockFile.insertName( "soffice.lck" );
422 OUString aService( SfxDdeServiceName_Impl(
423 aOfficeLockFile.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) ) );
424 aService = aService.toAsciiUpperCase();
425 pImpl->pDdeService2.reset( new ImplDdeService( aService ));
426 pImpl->pTriggerTopic.reset(new SfxDdeTriggerTopic_Impl);
427 pImpl->pDdeService2->AddTopic( *pImpl->pTriggerTopic );
429 #endif
430 return !nError;
433 void SfxAppData_Impl::DeInitDDE()
435 pTriggerTopic.reset();
436 pDdeService2.reset();
437 pDocTopics.reset();
438 pDdeService.reset();
441 #if defined(_WIN32)
442 void SfxApplication::AddDdeTopic( SfxObjectShell* pSh )
444 //OV: DDE is disconnected in server mode!
445 if( !pImpl->pDocTopics )
446 return;
448 // prevent double submit
449 OUString sShellNm;
450 bool bFnd = false;
451 for (size_t n = pImpl->pDocTopics->size(); n;)
453 if( (*pImpl->pDocTopics)[ --n ]->pSh == pSh )
455 // If the document is untitled, is still a new Topic is created!
456 if( !bFnd )
458 bFnd = true;
459 sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
461 OUString sNm( (*pImpl->pDocTopics)[ n ]->GetName() );
462 if( sShellNm == sNm.toAsciiLowerCase() )
463 return ;
467 SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
468 pImpl->pDocTopics->push_back(pTopic);
469 pImpl->pDdeService->AddTopic( *pTopic );
471 #endif
473 void SfxApplication::RemoveDdeTopic( SfxObjectShell const * pSh )
475 #if defined(_WIN32)
476 //OV: DDE is disconnected in server mode!
477 if( !pImpl->pDocTopics )
478 return;
480 for (size_t n = pImpl->pDocTopics->size(); n; )
482 SfxDdeDocTopic_Impl *const pTopic = (*pImpl->pDocTopics)[ --n ];
483 if (pTopic->pSh == pSh)
485 pImpl->pDdeService->RemoveTopic( *pTopic );
486 delete pTopic;
487 pImpl->pDocTopics->erase( pImpl->pDocTopics->begin() + n );
490 #else
491 (void) pSh;
492 #endif
495 const DdeService* SfxApplication::GetDdeService() const
497 return pImpl->pDdeService.get();
500 DdeService* SfxApplication::GetDdeService()
502 return pImpl->pDdeService.get();
505 #if defined(_WIN32)
507 DdeData* SfxDdeDocTopic_Impl::Get(SotClipboardFormatId nFormat)
509 OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
510 css::uno::Any aValue;
511 bool bRet = pSh->DdeGetData( GetCurItem(), sMimeType, aValue );
512 if( bRet && aValue.hasValue() && ( aValue >>= aSeq ) )
514 aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
515 return &aData;
517 aSeq.realloc( 0 );
518 return nullptr;
521 bool SfxDdeDocTopic_Impl::Put( const DdeData* pData )
523 aSeq = css::uno::Sequence< sal_Int8 >(
524 static_cast<sal_Int8 const *>(pData->getData()), pData->getSize() );
525 bool bRet;
526 if( aSeq.getLength() )
528 css::uno::Any aValue;
529 aValue <<= aSeq;
530 OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
531 bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
533 else
534 bRet = false;
535 return bRet;
538 bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
540 long nRet = pStr ? pSh->DdeExecute( *pStr ) : 0;
541 return 0 != nRet;
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: */