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 <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>
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();
66 class ImplDdeService
: public DdeService
69 explicit ImplDdeService( const OUString
& 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
;
84 INetURLObject
aObj( rContent
);
85 DBG_ASSERT( aObj
.GetProtocol() != INetProtocol::NotValid
, "Invalid URL!" );
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", "" );
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() )
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:
112 OUString
sNm( rNm
.toAsciiLowerCase() );
113 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst();
116 OUString
sTmp( pShell
->GetTitle(SFX_TITLE_FULLNAME
) );
117 if( sNm
== sTmp
.toAsciiLowerCase() )
119 SfxGetpApp()->AddDdeTopic( pShell
);
123 pShell
= SfxObjectShell::GetNext( *pShell
);
131 url
= rtl::Uri::convertRelToAbs(SvtPathOptions().GetWorkPath(), rNm
);
133 } catch (rtl::MalformedUriException
&) {
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
);
161 OUString
ImplDdeService::Topics()
165 sRet
+= GetSysTopic()->GetName();
167 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst();
170 if( SfxViewFrame::GetFirst( pShell
) )
172 if( !sRet
.isEmpty() )
174 sRet
+= pShell
->GetTitle(SFX_TITLE_FULLNAME
);
176 pShell
= SfxObjectShell::GetNext( *pShell
);
178 if( !sRet
.isEmpty() )
183 bool ImplDdeService::SysTopicExecute( const OUString
* pStr
)
185 return SfxApplication::DdeExecute( *pStr
);
189 class SfxDdeDocTopic_Impl
: public DdeTopic
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
;
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.
223 rCmd = "Open(\"d:\doc\doc.sdw\")"
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:
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
));
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
));
281 GetpApp()->AppEvent( ApplicationEvent(eType
, aData
) );
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
);
312 SbxBase::ResetError();
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
331 StarBASIC
* pBasic
= GetBasic();
332 DBG_ASSERT( pBasic
, "Where is the Basic???" ) ;
333 SbxVariable
* pRet
= pBasic
->Execute( rCmd
);
336 SbxBase::ResetError();
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
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
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
386 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell
& /*rServer*/)
390 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell
& rServer
)
392 SfxObjectShell
* p
= GetFirst(nullptr, false);
396 p
->ReconnectDdeLink(rServer
);
398 p
= GetNext(*p
, nullptr, false);
402 bool SfxApplication::InitializeDde()
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();
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
);
433 void SfxAppData_Impl::DeInitDDE()
435 pTriggerTopic
.reset();
436 pDdeService2
.reset();
442 void SfxApplication::AddDdeTopic( SfxObjectShell
* pSh
)
444 //OV: DDE is disconnected in server mode!
445 if( !pImpl
->pDocTopics
)
448 // prevent double submit
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!
459 sShellNm
= pSh
->GetTitle(SFX_TITLE_FULLNAME
).toAsciiLowerCase();
461 OUString
sNm( (*pImpl
->pDocTopics
)[ n
]->GetName() );
462 if( sShellNm
== sNm
.toAsciiLowerCase() )
467 SfxDdeDocTopic_Impl
*const pTopic
= new SfxDdeDocTopic_Impl(pSh
);
468 pImpl
->pDocTopics
->push_back(pTopic
);
469 pImpl
->pDdeService
->AddTopic( *pTopic
);
473 void SfxApplication::RemoveDdeTopic( SfxObjectShell
const * pSh
)
476 //OV: DDE is disconnected in server mode!
477 if( !pImpl
->pDocTopics
)
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
);
487 pImpl
->pDocTopics
->erase( pImpl
->pDocTopics
->begin() + n
);
495 const DdeService
* SfxApplication::GetDdeService() const
497 return pImpl
->pDdeService
.get();
500 DdeService
* SfxApplication::GetDdeService()
502 return pImpl
->pDdeService
.get();
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
);
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() );
526 if( aSeq
.getLength() )
528 css::uno::Any aValue
;
530 OUString
sMimeType( SotExchange::GetFormatMimeType( pData
->GetFormat() ));
531 bRet
= pSh
->DdeSetData( GetCurItem(), sMimeType
, aValue
);
538 bool SfxDdeDocTopic_Impl::Execute( const OUString
* pStr
)
540 long nRet
= pStr
? pSh
->DdeExecute( *pStr
) : 0;
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: */