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 .
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>
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();
70 class ImplDdeService
: public DdeService
73 explicit ImplDdeService( const OUString
& 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
;
88 INetURLObject
aObj( rContent
);
89 DBG_ASSERT( aObj
.GetProtocol() != INetProtocol::NotValid
, "Invalid URL!" );
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", "" );
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() )
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:
116 OUString
sNm( rNm
.toAsciiLowerCase() );
117 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst();
120 OUString
sTmp( pShell
->GetTitle(SFX_TITLE_FULLNAME
) );
121 if( sNm
== sTmp
.toAsciiLowerCase() )
123 SfxGetpApp()->AddDdeTopic( pShell
);
127 pShell
= SfxObjectShell::GetNext( *pShell
);
135 url
= rtl::Uri::convertRelToAbs(SvtPathOptions().GetWorkPath(), rNm
);
137 } catch (rtl::MalformedUriException
&) {
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
);
155 nullptr != ( pShell
= item
->GetFrame()->GetObjectShell() ) )
157 SfxGetpApp()->AddDdeTopic( pShell
);
165 OUString
ImplDdeService::Topics()
169 sRet
+= GetSysTopic()->GetName();
171 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst();
174 if( SfxViewFrame::GetFirst( pShell
) )
176 if( !sRet
.isEmpty() )
178 sRet
+= pShell
->GetTitle(SFX_TITLE_FULLNAME
);
180 pShell
= SfxObjectShell::GetNext( *pShell
);
182 if( !sRet
.isEmpty() )
187 bool ImplDdeService::SysTopicExecute( const OUString
* pStr
)
189 return SfxApplication::DdeExecute( *pStr
);
193 class SfxDdeDocTopic_Impl
: public DdeTopic
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
;
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.
227 rCmd = "Open(\"d:\doc\doc.sdw\")"
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:
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
));
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
));
285 GetpApp()->AppEvent( ApplicationEvent(eType
, std::move(aData
)) );
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
);
316 SbxBase::ResetError();
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
335 StarBASIC
* pBasic
= GetBasic();
336 DBG_ASSERT( pBasic
, "Where is the Basic???" ) ;
337 SbxVariable
* pRet
= pBasic
->Execute( rCmd
);
340 SbxBase::ResetError();
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
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
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
390 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell
& /*rServer*/)
394 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell
& rServer
)
396 SfxObjectShell
* p
= GetFirst(nullptr, false);
400 p
->ReconnectDdeLink(rServer
);
402 p
= GetNext(*p
, nullptr, false);
406 bool SfxApplication::InitializeDde()
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();
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
);
437 void SfxAppData_Impl::DeInitDDE()
439 pTriggerTopic
.reset();
440 pDdeService2
.reset();
446 void SfxApplication::AddDdeTopic( SfxObjectShell
* pSh
)
448 //OV: DDE is disconnected in server mode!
449 if( !pImpl
->pDocTopics
)
452 // prevent double submit
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!
463 sShellNm
= pSh
->GetTitle(SFX_TITLE_FULLNAME
).toAsciiLowerCase();
465 OUString
sNm( (*pImpl
->pDocTopics
)[ n
]->GetName() );
466 if( sShellNm
== sNm
.toAsciiLowerCase() )
471 SfxDdeDocTopic_Impl
*const pTopic
= new SfxDdeDocTopic_Impl(pSh
);
472 pImpl
->pDocTopics
->push_back(pTopic
);
473 pImpl
->pDdeService
->AddTopic( *pTopic
);
477 void SfxApplication::RemoveDdeTopic( SfxObjectShell
const * pSh
)
480 //OV: DDE is disconnected in server mode!
481 if( !pImpl
->pDocTopics
)
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
);
491 pImpl
->pDocTopics
->erase( pImpl
->pDocTopics
->begin() + n
);
499 const DdeService
* SfxApplication::GetDdeService() const
501 return pImpl
->pDdeService
.get();
504 DdeService
* SfxApplication::GetDdeService()
506 return pImpl
->pDdeService
.get();
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
);
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() );
530 if( aSeq
.getLength() )
532 css::uno::Any aValue
;
534 OUString
sMimeType( SotExchange::GetFormatMimeType( pData
->GetFormat() ));
535 bRet
= pSh
->DdeSetData( GetCurItem(), sMimeType
, aValue
);
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
) );
553 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
556 ::sfx2::SvLinkSource
* pNewObj
= pSh
->DdeCreateLinkSource( GetCurItem() );
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
);
570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */