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>
22 #include <vcl/wrkwin.hxx>
23 #include <svl/rectitem.hxx>
24 #include <svl/eitem.hxx>
25 #include <svl/intitem.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/urlobj.hxx>
33 #include <tools/diagnose_ex.h>
34 #include <unotools/pathoptions.hxx>
36 #include <sfx2/app.hxx>
37 #include "appdata.hxx"
38 #include <sfx2/objsh.hxx>
39 #include <sfx2/viewfrm.hxx>
40 #include <sfx2/dispatch.hxx>
41 #include "sfxtypes.hxx"
42 #include <sfx2/sfxsids.hrc>
44 #include <sfx2/docfile.hxx>
45 #include <comphelper/processfactory.hxx>
46 #include <comphelper/string.hxx>
47 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
51 OUString
SfxDdeServiceName_Impl( const OUString
& sIn
)
53 OUStringBuffer
sReturn(sIn
.getLength());
55 for ( sal_uInt16 n
= sIn
.getLength(); n
; --n
)
57 sal_Unicode cChar
= sIn
[n
-1];
58 if (comphelper::string::isalnumAscii(cChar
))
59 sReturn
.append(cChar
);
62 return sReturn
.makeStringAndClear();
65 class ImplDdeService
: public DdeService
68 ImplDdeService( const OUString
& rNm
)
71 virtual bool MakeTopic( const OUString
& );
73 virtual OUString
Topics();
75 virtual bool SysTopicExecute( const OUString
* pStr
);
81 sal_Bool
lcl_IsDocument( const OUString
& rContent
)
83 using namespace com::sun::star
;
85 sal_Bool bRet
= sal_False
;
86 INetURLObject
aObj( rContent
);
87 DBG_ASSERT( aObj
.GetProtocol() != INetProtocol::NotValid
, "Invalid URL!" );
91 ::ucbhelper::Content
aCnt( aObj
.GetMainURL( INetURLObject::NO_DECODE
), uno::Reference
< ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
92 bRet
= aCnt
.isDocument();
94 catch( const ucb::CommandAbortedException
& )
96 DBG_WARNING( "CommandAbortedException" );
98 catch( const ucb::IllegalIdentifierException
& )
100 DBG_WARNING( "IllegalIdentifierException" );
102 catch( const ucb::ContentCreationException
& )
104 DBG_WARNING( "IllegalIdentifierException" );
106 catch( const uno::Exception
& )
108 SAL_WARN( "sfx.appl", "Any other exception" );
115 bool ImplDdeService::MakeTopic( const OUString
& rNm
)
117 // Workaround for Event after Main() under OS/2
118 // happens when exiting starts the App again
119 if ( !Application::IsInExecute() )
122 // The Topic rNm is sought, do we have it?
123 // First only loop over the ObjectShells to find those
124 // with the specific name:
125 sal_Bool bRet
= sal_False
;
126 OUString
sNm( rNm
.toAsciiLowerCase() );
127 TypeId
aType( TYPE(SfxObjectShell
) );
128 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst( &aType
);
131 OUString
sTmp( pShell
->GetTitle(SFX_TITLE_FULLNAME
) );
132 if( sNm
== sTmp
.toAsciiLowerCase() )
134 SfxGetpApp()->AddDdeTopic( pShell
);
138 pShell
= SfxObjectShell::GetNext( *pShell
, &aType
);
143 INetURLObject
aWorkPath( SvtPathOptions().GetWorkPath() );
145 if ( aWorkPath
.GetNewAbsURL( rNm
, &aFile
) &&
146 lcl_IsDocument( aFile
.GetMainURL( INetURLObject::NO_DECODE
) ) )
148 // File exists? then try to load it:
149 SfxStringItem
aName( SID_FILE_NAME
, aFile
.GetMainURL( INetURLObject::NO_DECODE
) );
150 SfxBoolItem
aNewView(SID_OPEN_NEW_VIEW
, sal_True
);
152 SfxBoolItem
aSilent(SID_SILENT
, sal_True
);
153 SfxDispatcher
* pDispatcher
= SfxGetpApp()->GetDispatcher_Impl();
154 const SfxPoolItem
* pRet
= pDispatcher
->Execute( SID_OPENDOC
,
155 SfxCallMode::SYNCHRON
,
159 if( pRet
&& pRet
->ISA( SfxViewFrameItem
) &&
160 ((SfxViewFrameItem
*)pRet
)->GetFrame() &&
161 0 != ( pShell
= ((SfxViewFrameItem
*)pRet
)
162 ->GetFrame()->GetObjectShell() ) )
164 SfxGetpApp()->AddDdeTopic( pShell
);
172 OUString
ImplDdeService::Topics()
176 sRet
+= GetSysTopic()->GetName();
178 TypeId
aType( TYPE(SfxObjectShell
) );
179 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst( &aType
);
182 if( SfxViewFrame::GetFirst( pShell
) )
184 if( !sRet
.isEmpty() )
186 sRet
+= pShell
->GetTitle(SFX_TITLE_FULLNAME
);
188 pShell
= SfxObjectShell::GetNext( *pShell
, &aType
);
190 if( !sRet
.isEmpty() )
195 bool ImplDdeService::SysTopicExecute( const OUString
* pStr
)
197 return SfxGetpApp()->DdeExecute( *pStr
);
201 class SfxDdeTriggerTopic_Impl
: public DdeTopic
205 SfxDdeTriggerTopic_Impl()
206 : DdeTopic( "TRIGGER" )
209 virtual bool Execute( const OUString
* ) SAL_OVERRIDE
{ return true; }
213 class SfxDdeDocTopic_Impl
: public DdeTopic
219 ::com::sun::star::uno::Sequence
< sal_Int8
> aSeq
;
221 SfxDdeDocTopic_Impl( SfxObjectShell
* pShell
)
222 : DdeTopic( pShell
->GetTitle(SFX_TITLE_FULLNAME
) ), pSh( pShell
)
225 virtual DdeData
* Get( SotClipboardFormatId
) SAL_OVERRIDE
;
226 virtual bool Put( const DdeData
* ) SAL_OVERRIDE
;
227 virtual bool Execute( const OUString
* ) SAL_OVERRIDE
;
228 virtual bool StartAdviseLoop() SAL_OVERRIDE
;
229 virtual bool MakeItem( const OUString
& rItem
) SAL_OVERRIDE
;
240 Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
241 this data into a <ApplicationEvent>, which is then executed through
242 <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
243 TRUE is returned, otherwise FALSE.
247 rCmd = "Open(\"d:\doc\doc.sdw\")"
250 sal_Bool
SfxAppEvent_Impl( const OUString
& rCmd
, const OUString
& rEvent
,
251 ApplicationEvent::Type eType
)
253 OUString
sEvent(rEvent
+ "(");
254 if (rCmd
.startsWithIgnoreAsciiCase(sEvent
))
256 sal_Int32 start
= sEvent
.getLength();
257 if ( rCmd
.getLength() - start
>= 2 )
259 // Transform into the ApplicationEvent Format
260 //TODO: I /assume/ that rCmd should match the syntax of
261 // <http://msdn.microsoft.com/en-us/library/ms648995.aspx>
262 // "WM_DDE_EXECUTE message" but does not (handle commands enclosed
263 // in [...]; handle commas separating multiple arguments; handle
264 // double "", ((, )), [[, ]] in quoted arguments); see also the mail
265 // thread starting at <http://lists.freedesktop.org/archives/
266 // libreoffice/2013-July/054779.html> "DDE on Windows."
267 std::vector
<OUString
> aData
;
268 for ( sal_Int32 n
= start
; n
< rCmd
.getLength() - 1; )
270 // Resiliently read arguments either starting with " and
271 // spanning to the next " (if any; TODO: do we need to undo any
272 // escaping within the string?) or with neither " nor SPC and
273 // spanning to the next SPC (if any; TODO: is this from not
274 // wrapped in "..." relevant? it would have been parsed by the
275 // original code even if that was only by accident, so I left it
276 // in), with runs of SPCs treated like single ones:
281 sal_Int32 i
= rCmd
.indexOf('"', ++n
);
282 if (i
< 0 || i
> rCmd
.getLength() - 1) {
283 i
= rCmd
.getLength() - 1;
285 aData
.push_back(rCmd
.copy(n
, i
- n
));
294 sal_Int32 i
= rCmd
.indexOf(' ', n
);
295 if (i
< 0 || i
> rCmd
.getLength() - 1) {
296 i
= rCmd
.getLength() - 1;
298 aData
.push_back(rCmd
.copy(n
, i
- n
));
305 GetpApp()->AppEvent( ApplicationEvent(eType
, aData
) );
317 This method can be overridden by application developers, to receive
318 DDE-commands directed to their SfxApplication subclass.
320 The base implementation understands the API functionality of the
321 relevant SfxApplication subclass in BASIC syntax. Return values can
322 not be transferred, unfortunately.
324 long SfxApplication::DdeExecute( const OUString
& rCmd
) // Expressed in our BASIC-Syntax
326 // Print or Open-Event?
327 if ( !( SfxAppEvent_Impl( rCmd
, "Print", ApplicationEvent::TYPE_PRINT
) ||
328 SfxAppEvent_Impl( rCmd
, "Open", ApplicationEvent::TYPE_OPEN
) ) )
330 // all others are BASIC
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-commands directed to the their SfxApplication subclass.
348 The base implementation does nothing and returns 0.
350 long SfxObjectShell::DdeExecute( const OUString
& rCmd
) // Expressed in our BASIC-Syntax
352 #if !HAVE_FEATURE_SCRIPTING
355 StarBASIC
* pBasic
= GetBasic();
356 DBG_ASSERT( pBasic
, "Where is the Basic???" ) ;
357 SbxVariable
* pRet
= pBasic
->Execute( rCmd
);
360 SbxBase::ResetError();
369 This method can be overridden by application developers, to receive
370 DDE-data-requests directed to their SfxApplication subclass.
372 The base implementation provides no data and returns false.
374 bool SfxObjectShell::DdeGetData( const OUString
&, // the Item to be addressed
375 const OUString
&, // in: Format
376 ::com::sun::star::uno::Any
& )// out: requested data
385 This method can be overridden by application developers, to receive
386 DDE-data directed to their SfxApplication subclass.
388 The base implementation is not receiving any data and returns false.
390 bool SfxObjectShell::DdeSetData( const OUString
&, // the Item to be addressed
391 const OUString
&, // in: Format
392 const ::com::sun::star::uno::Any
& )// out: requested data
401 This method can be overridden by application developers, to establish
402 a DDE-hotlink to their SfxApplication subclass.
404 The base implementation is not generate a link and returns 0.
406 ::sfx2::SvLinkSource
* SfxObjectShell::DdeCreateLinkSource( const OUString
& ) // the Item to be addressed
411 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell
& /*rServer*/)
415 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell
& rServer
)
417 TypeId aType
= TYPE(SfxObjectShell
);
418 SfxObjectShell
* p
= GetFirst(&aType
, false);
422 p
->ReconnectDdeLink(rServer
);
424 p
= GetNext(*p
, &aType
, false);
428 bool SfxApplication::InitializeDde()
432 DBG_ASSERT( !pAppData_Impl
->pDdeService
,
433 "Dde can not be initialized multiple times" );
435 pAppData_Impl
->pDdeService
= new ImplDdeService( Application::GetAppName() );
436 nError
= pAppData_Impl
->pDdeService
->GetError();
439 pAppData_Impl
->pDocTopics
= new SfxDdeDocTopics_Impl
;
441 // we certainly want to support RTF!
442 pAppData_Impl
->pDdeService
->AddFormat( SotClipboardFormatId::RTF
);
444 // Config path as a topic becauseof multiple starts
445 INetURLObject
aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
446 aOfficeLockFile
.insertName( "soffice.lck" );
447 OUString
aService( SfxDdeServiceName_Impl(
448 aOfficeLockFile
.GetMainURL(INetURLObject::DECODE_TO_IURI
) ) );
449 aService
= aService
.toAsciiUpperCase();
450 pAppData_Impl
->pDdeService2
= new ImplDdeService( aService
);
451 pAppData_Impl
->pTriggerTopic
= new SfxDdeTriggerTopic_Impl
;
452 pAppData_Impl
->pDdeService2
->AddTopic( *pAppData_Impl
->pTriggerTopic
);
458 void SfxAppData_Impl::DeInitDDE()
460 DELETEZ( pTriggerTopic
);
461 DELETEZ( pDdeService2
);
462 DELETEZ( pDocTopics
);
463 DELETEZ( pDdeService
);
467 void SfxApplication::AddDdeTopic( SfxObjectShell
* pSh
)
469 DBG_ASSERT( pAppData_Impl
->pDocTopics
, "There is no Dde-Service" );
470 //OV: DDE is disconnected in server mode!
471 if( !pAppData_Impl
->pDocTopics
)
474 // prevent double submit
476 sal_Bool bFnd
= sal_False
;
477 for (size_t n
= pAppData_Impl
->pDocTopics
->size(); n
;)
479 if( (*pAppData_Impl
->pDocTopics
)[ --n
]->pSh
== pSh
)
481 // If the document is untitled, is still a new Topic is created!
485 sShellNm
= pSh
->GetTitle(SFX_TITLE_FULLNAME
).toAsciiLowerCase();
487 OUString
sNm( (*pAppData_Impl
->pDocTopics
)[ n
]->GetName() );
488 if( sShellNm
== sNm
.toAsciiLowerCase() )
493 SfxDdeDocTopic_Impl
*const pTopic
= new SfxDdeDocTopic_Impl(pSh
);
494 pAppData_Impl
->pDocTopics
->push_back(pTopic
);
495 pAppData_Impl
->pDdeService
->AddTopic( *pTopic
);
499 void SfxApplication::RemoveDdeTopic( SfxObjectShell
* pSh
)
502 DBG_ASSERT( pAppData_Impl
->pDocTopics
, "There is no Dde-Service" );
503 //OV: DDE is disconnected in server mode!
504 if( !pAppData_Impl
->pDocTopics
)
507 for (size_t n
= pAppData_Impl
->pDocTopics
->size(); n
; )
509 SfxDdeDocTopic_Impl
*const pTopic
= (*pAppData_Impl
->pDocTopics
)[ --n
];
510 if (pTopic
->pSh
== pSh
)
512 pAppData_Impl
->pDdeService
->RemoveTopic( *pTopic
);
514 pAppData_Impl
->pDocTopics
->erase( pAppData_Impl
->pDocTopics
->begin() + n
);
522 const DdeService
* SfxApplication::GetDdeService() const
524 return pAppData_Impl
->pDdeService
;
527 DdeService
* SfxApplication::GetDdeService()
529 return pAppData_Impl
->pDdeService
;
534 DdeData
* SfxDdeDocTopic_Impl::Get(SotClipboardFormatId nFormat
)
536 OUString
sMimeType( SotExchange::GetFormatMimeType( nFormat
));
537 ::com::sun::star::uno::Any aValue
;
538 bool nRet
= pSh
->DdeGetData( GetCurItem(), sMimeType
, aValue
);
539 if( nRet
&& aValue
.hasValue() && ( aValue
>>= aSeq
) )
541 aData
= DdeData( aSeq
.getConstArray(), aSeq
.getLength(), nFormat
);
548 bool SfxDdeDocTopic_Impl::Put( const DdeData
* pData
)
550 aSeq
= ::com::sun::star::uno::Sequence
< sal_Int8
>(
551 (sal_Int8
*)(const void*)*pData
, (long)*pData
);
553 if( aSeq
.getLength() )
555 ::com::sun::star::uno::Any aValue
;
557 OUString
sMimeType( SotExchange::GetFormatMimeType( pData
->GetFormat() ));
558 bRet
= pSh
->DdeSetData( GetCurItem(), sMimeType
, aValue
);
565 bool SfxDdeDocTopic_Impl::Execute( const OUString
* pStr
)
567 long nRet
= pStr
? pSh
->DdeExecute( *pStr
) : 0;
571 bool SfxDdeDocTopic_Impl::MakeItem( const OUString
& rItem
)
573 AddItem( DdeItem( rItem
) );
577 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
580 ::sfx2::SvLinkSource
* pNewObj
= pSh
->DdeCreateLinkSource( GetCurItem() );
583 // then we also establish a corresponding SvBaseLink
584 OUString sNm
, sTmp( Application::GetAppName() );
585 ::sfx2::MakeLnkName( sNm
, &sTmp
, pSh
->GetTitle(SFX_TITLE_FULLNAME
), GetCurItem() );
586 new ::sfx2::SvBaseLink( sNm
, OBJECT_DDE_EXTERN
, pNewObj
);
594 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */