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 <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>
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( std::u16string_view 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 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());
154 nullptr != ( pShell
= item
->GetFrame()->GetObjectShell() ) )
156 SfxGetpApp()->AddDdeTopic( pShell
);
164 OUString
ImplDdeService::Topics()
168 sRet
+= GetSysTopic()->GetName();
170 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst();
173 if( SfxViewFrame::GetFirst( pShell
) )
175 if( !sRet
.isEmpty() )
177 sRet
+= pShell
->GetTitle(SFX_TITLE_FULLNAME
);
179 pShell
= SfxObjectShell::GetNext( *pShell
);
181 if( !sRet
.isEmpty() )
186 bool ImplDdeService::SysTopicExecute( const OUString
* pStr
)
188 return SfxApplication::DdeExecute( *pStr
);
192 class SfxDdeDocTopic_Impl
: public DdeTopic
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
;
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.
226 rCmd = "Open(\"d:\doc\doc.sdw\")"
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:
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
));
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
));
284 GetpApp()->AppEvent( ApplicationEvent(eType
, std::move(aData
)) );
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
);
315 SbxBase::ResetError();
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
334 StarBASIC
* pBasic
= GetBasic();
335 DBG_ASSERT( pBasic
, "Where is the Basic???" ) ;
336 SbxVariable
* pRet
= pBasic
->Execute( rCmd
);
339 SbxBase::ResetError();
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
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
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
389 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell
& /*rServer*/)
393 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell
& rServer
)
395 SfxObjectShell
* p
= GetFirst(nullptr, false);
399 p
->ReconnectDdeLink(rServer
);
401 p
= GetNext(*p
, nullptr, false);
405 bool SfxApplication::InitializeDde()
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();
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
);
434 void SfxAppData_Impl::DeInitDDE()
436 pTriggerTopic
.reset();
437 pDdeService2
.reset();
443 void SfxApplication::AddDdeTopic( SfxObjectShell
* pSh
)
445 //OV: DDE is disconnected in server mode!
446 if( pImpl
->maDocTopics
.empty() )
449 // prevent double submit
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!
460 sShellNm
= pSh
->GetTitle(SFX_TITLE_FULLNAME
).toAsciiLowerCase();
462 OUString
sNm( pImpl
->maDocTopics
[ n
]->GetName() );
463 if( sShellNm
== sNm
.toAsciiLowerCase() )
468 SfxDdeDocTopic_Impl
*const pTopic
= new SfxDdeDocTopic_Impl(pSh
);
469 pImpl
->maDocTopics
.push_back(pTopic
);
470 pImpl
->pDdeService
->AddTopic( *pTopic
);
474 void SfxApplication::RemoveDdeTopic( SfxObjectShell
const * pSh
)
477 //OV: DDE is disconnected in server mode!
478 if( pImpl
->maDocTopics
.empty() )
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
);
488 pImpl
->maDocTopics
.erase( pImpl
->maDocTopics
.begin() + n
);
496 const DdeService
* SfxApplication::GetDdeService() const
498 return pImpl
->pDdeService
.get();
501 DdeService
* SfxApplication::GetDdeService()
503 return pImpl
->pDdeService
.get();
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
);
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() );
527 if( aSeq
.getLength() )
529 css::uno::Any aValue
;
531 OUString
sMimeType( SotExchange::GetFormatMimeType( pData
->GetFormat() ));
532 bRet
= pSh
->DdeSetData( GetCurItem(), sMimeType
, aValue
);
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
) );
550 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
553 ::sfx2::SvLinkSource
* pNewObj
= pSh
->DdeCreateLinkSource( GetCurItem() );
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
);
567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */