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 <vcl/wrkwin.hxx>
21 #include <svl/rectitem.hxx>
22 #include <svl/eitem.hxx>
23 #include <svl/intitem.hxx>
24 #include <basic/sbstar.hxx>
25 #include <svl/stritem.hxx>
26 #include <svl/svdde.hxx>
27 #include <sfx2/lnkbase.hxx>
28 #include <sfx2/linkmgr.hxx>
30 #include <tools/urlobj.hxx>
31 #include <tools/diagnose_ex.h>
32 #include <unotools/pathoptions.hxx>
34 #include <sfx2/app.hxx>
35 #include "appdata.hxx"
36 #include <sfx2/objsh.hxx>
37 #include <sfx2/viewfrm.hxx>
38 #include <sfx2/dispatch.hxx>
39 #include "sfxtypes.hxx"
40 #include <sfx2/sfxsids.hrc>
42 #include <sfx2/docfile.hxx>
43 #include <comphelper/processfactory.hxx>
44 #include <comphelper/string.hxx>
45 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
47 //========================================================================
49 OUString
SfxDdeServiceName_Impl( const OUString
& sIn
)
51 OUStringBuffer
sReturn(sIn
.getLength());
53 for ( sal_uInt16 n
= sIn
.getLength(); n
; --n
)
55 sal_Unicode cChar
= sIn
[n
-1];
56 if (comphelper::string::isalnumAscii(cChar
))
57 sReturn
.append(cChar
);
60 return sReturn
.makeStringAndClear();
64 class ImplDdeService
: public DdeService
67 ImplDdeService( const OUString
& rNm
)
70 virtual bool MakeTopic( const OUString
& );
72 virtual OUString
Topics();
74 virtual bool SysTopicExecute( const OUString
* pStr
);
77 //--------------------------------------------------------------------
80 sal_Bool
lcl_IsDocument( const OUString
& rContent
)
82 using namespace com::sun::star
;
84 sal_Bool bRet
= sal_False
;
85 INetURLObject
aObj( rContent
);
86 DBG_ASSERT( aObj
.GetProtocol() != INET_PROT_NOT_VALID
, "Invalid URL!" );
90 ::ucbhelper::Content
aCnt( aObj
.GetMainURL( INetURLObject::NO_DECODE
), uno::Reference
< ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
91 bRet
= aCnt
.isDocument();
93 catch( const ucb::CommandAbortedException
& )
95 DBG_WARNING( "CommandAbortedException" );
97 catch( const ucb::IllegalIdentifierException
& )
99 DBG_WARNING( "IllegalIdentifierException" );
101 catch( const ucb::ContentCreationException
& )
103 DBG_WARNING( "IllegalIdentifierException" );
105 catch( const uno::Exception
& )
107 SAL_WARN( "sfx.appl", "Any other exception" );
114 bool ImplDdeService::MakeTopic( const OUString
& rNm
)
116 // Workaround for Event after Main() under OS/2
117 // happens when exiting starts the App again
118 if ( !Application::IsInExecute() )
121 // The Topic rNm is sought, do we have it?
122 // First only loop over the ObjectShells to find those
123 // with the specific name:
124 sal_Bool bRet
= sal_False
;
125 OUString
sNm( rNm
.toAsciiLowerCase() );
126 TypeId
aType( TYPE(SfxObjectShell
) );
127 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst( &aType
);
130 OUString
sTmp( pShell
->GetTitle(SFX_TITLE_FULLNAME
) );
131 if( sNm
== sTmp
.toAsciiLowerCase() )
133 SFX_APP()->AddDdeTopic( pShell
);
137 pShell
= SfxObjectShell::GetNext( *pShell
, &aType
);
142 INetURLObject
aWorkPath( SvtPathOptions().GetWorkPath() );
144 if ( aWorkPath
.GetNewAbsURL( rNm
, &aFile
) &&
145 lcl_IsDocument( aFile
.GetMainURL( INetURLObject::NO_DECODE
) ) )
147 // File exists? then try to load it:
148 SfxStringItem
aName( SID_FILE_NAME
, aFile
.GetMainURL( INetURLObject::NO_DECODE
) );
149 SfxBoolItem
aNewView(SID_OPEN_NEW_VIEW
, sal_True
);
151 SfxBoolItem
aSilent(SID_SILENT
, sal_True
);
152 SfxDispatcher
* pDispatcher
= SFX_APP()->GetDispatcher_Impl();
153 const SfxPoolItem
* pRet
= pDispatcher
->Execute( SID_OPENDOC
,
154 SFX_CALLMODE_SYNCHRON
,
158 if( pRet
&& pRet
->ISA( SfxViewFrameItem
) &&
159 ((SfxViewFrameItem
*)pRet
)->GetFrame() &&
160 0 != ( pShell
= ((SfxViewFrameItem
*)pRet
)
161 ->GetFrame()->GetObjectShell() ) )
163 SFX_APP()->AddDdeTopic( pShell
);
171 OUString
ImplDdeService::Topics()
175 sRet
+= GetSysTopic()->GetName();
177 TypeId
aType( TYPE(SfxObjectShell
) );
178 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst( &aType
);
181 if( SfxViewFrame::GetFirst( pShell
) )
183 if( !sRet
.isEmpty() )
185 sRet
+= pShell
->GetTitle(SFX_TITLE_FULLNAME
);
187 pShell
= SfxObjectShell::GetNext( *pShell
, &aType
);
189 if( !sRet
.isEmpty() )
194 bool ImplDdeService::SysTopicExecute( const OUString
* pStr
)
196 return SFX_APP()->DdeExecute( *pStr
);
200 class SfxDdeTriggerTopic_Impl
: public DdeTopic
203 SfxDdeTriggerTopic_Impl()
204 : DdeTopic( "TRIGGER" )
207 virtual bool Execute( const OUString
* );
210 class SfxDdeDocTopic_Impl
: public DdeTopic
215 ::com::sun::star::uno::Sequence
< sal_Int8
> aSeq
;
217 SfxDdeDocTopic_Impl( SfxObjectShell
* pShell
)
218 : DdeTopic( pShell
->GetTitle(SFX_TITLE_FULLNAME
) ), pSh( pShell
)
221 virtual DdeData
* Get( sal_uIntPtr
);
222 virtual bool Put( const DdeData
* );
223 virtual bool Execute( const OUString
* );
224 virtual bool StartAdviseLoop();
225 virtual bool MakeItem( const OUString
& rItem
);
229 class SfxDdeDocTopics_Impl
: public std::vector
<SfxDdeDocTopic_Impl
*> {};
237 Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
238 this data into a <ApplicationEvent>, which is then excecuted through
239 <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
240 TRUE is returned, otherwise FALSE.
244 rCmd = "Open(\"d:\doc\doc.sdw\")"
247 sal_Bool
SfxAppEvent_Impl( const OUString
& rCmd
, const OUString
& rEvent
,
248 ApplicationEvent::Type eType
)
250 OUString
sEvent(rEvent
+ "(");
251 if (rCmd
.startsWithIgnoreAsciiCase(sEvent
))
253 sal_Int32 start
= sEvent
.getLength();
254 if ( rCmd
.getLength() - start
>= 2 )
256 // Transform into the ApplicationEvent Format
257 //TODO: I /assume/ that rCmd should match the syntax of
258 // <http://msdn.microsoft.com/en-us/library/ms648995.aspx>
259 // "WM_DDE_EXECUTE message" but does not (handle commands enclosed
260 // in [...]; handle commas separating multiple arguments; handle
261 // double "", ((, )), [[, ]] in quoted arguments); see also the mail
262 // thread starting at <http://lists.freedesktop.org/archives/
263 // libreoffice/2013-July/054779.html> "DDE on Windows."
264 std::vector
<OUString
> aData
;
265 for ( sal_Int32 n
= start
; n
< rCmd
.getLength() - 1; )
267 // Resiliently read arguments either starting with " and
268 // spanning to the next " (if any; TODO: do we need to undo any
269 // escaping within the string?) or with neither " nor SPC and
270 // spanning to the next SPC (if any; TODO: is this from not
271 // wrapped in "..." relevant? it would have been parsed by the
272 // original code even if that was only by accident, so I left it
273 // in), with runs of SPCs treated like single ones:
278 sal_Int32 i
= rCmd
.indexOf('"', ++n
);
279 if (i
< 0 || i
> rCmd
.getLength() - 1) {
280 i
= rCmd
.getLength() - 1;
282 aData
.push_back(rCmd
.copy(n
, i
- n
));
291 sal_Int32 i
= rCmd
.indexOf(' ', n
);
292 if (i
< 0 || i
> rCmd
.getLength() - 1) {
293 i
= rCmd
.getLength() - 1;
295 aData
.push_back(rCmd
.copy(n
, i
- n
));
302 GetpApp()->AppEvent( ApplicationEvent(eType
, aData
) );
314 This method can be overloaded by application developers, to receive
315 DDE-commands directed to their SfxApplication subclass.
317 The base implementation understands the API functionality of the
318 relevant SfxApplication subclass in BASIC syntax. Return values can
319 not be transferred, unfortunately.
321 long SfxApplication::DdeExecute( const OUString
& rCmd
) // Expressed in our BASIC-Syntax
323 // Print or Open-Event?
324 if ( !( SfxAppEvent_Impl( rCmd
, "Print", ApplicationEvent::TYPE_PRINT
) ||
325 SfxAppEvent_Impl( rCmd
, "Open", ApplicationEvent::TYPE_OPEN
) ) )
327 // all others are BASIC
328 StarBASIC
* pBasic
= GetBasic();
329 DBG_ASSERT( pBasic
, "Where is the Basic???" );
330 SbxVariable
* pRet
= pBasic
->Execute( rCmd
);
333 SbxBase::ResetError();
344 This method can be overloaded by application developers, to receive
345 DDE-commands directed to the their SfxApplication subclass.
347 The base implementation does nothing and returns 0.
349 long SfxObjectShell::DdeExecute( const OUString
& rCmd
) // Expressed in our BASIC-Syntax
351 #ifdef DISABLE_SCRIPTING
354 StarBASIC
* pBasic
= GetBasic();
355 DBG_ASSERT( pBasic
, "Where is the Basic???" ) ;
356 SbxVariable
* pRet
= pBasic
->Execute( rCmd
);
359 SbxBase::ResetError();
366 //--------------------------------------------------------------------
370 This method can be overloaded by application developers, to receive
371 DDE-data-requests directed to their SfxApplication subclass.
373 The base implementation provides no data and returns 0.
375 long SfxObjectShell::DdeGetData( const OUString
&, // the Item to be addressed
376 const OUString
&, // in: Format
377 ::com::sun::star::uno::Any
& )// out: requested data
382 //--------------------------------------------------------------------
386 This method can be overloaded by application developers, to receive
387 DDE-data directed to their SfxApplication subclass.
389 The base implementation is not receiving any data and returns 0.
391 long SfxObjectShell::DdeSetData( const OUString
&, // the Item to be addressed
392 const OUString
&, // in: Format
393 const ::com::sun::star::uno::Any
& )// out: requested data
400 This method can be overloaded by application developers, to establish
401 a DDE-hotlink to their SfxApplication subclass.
403 The base implementation is not generate a link and returns 0.
405 ::sfx2::SvLinkSource
* SfxObjectShell::DdeCreateLinkSource( const OUString
& ) // the Item to be addressed
410 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell
& /*rServer*/)
414 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell
& rServer
)
416 TypeId aType
= TYPE(SfxObjectShell
);
417 SfxObjectShell
* p
= GetFirst(&aType
, false);
421 p
->ReconnectDdeLink(rServer
);
423 p
= GetNext(*p
, &aType
, false);
429 This method can be overloaded by application developers, to receive
430 DDE-commands directed to the their SfxApplication subclass.
432 The base implementation understands the API functionality of the
433 relevant SfxViewFrame, which is shown and the relevant SfxViewShell
434 and the relevant SfxApplication subclass in BASIC syntax. Return
435 values can not be transferred, unfortunately.
437 long SfxViewFrame::DdeExecute( const OUString
& rCmd
) // Expressed in our BASIC-Syntax
439 if ( GetObjectShell() )
440 return GetObjectShell()->DdeExecute( rCmd
);
447 This method can be overloaded by application developers, to receive
448 DDE-data-requests directed to their SfxApplication subclass.
450 The base implementation provides no data and returns 0.
452 long SfxViewFrame::DdeGetData( const OUString
&, // the Item to be addressed
453 const OUString
&, // in: Format
454 ::com::sun::star::uno::Any
& )// out: requested data
461 This method can be overloaded by application developers, to receive
462 DDE-data directed to their SfxApplication subclass.
464 The base implementation is not receiving any data and returns 0.
466 long SfxViewFrame::DdeSetData( const OUString
&, // the Item to be addressed
467 const OUString
&, // in: Format
468 const ::com::sun::star::uno::Any
& )// out: requested data
475 This method can be overloaded by application developers, to establish
476 a DDE-hotlink to their SfxApplication subclass.
478 The base implementation is not generate a link and returns 0.
480 ::sfx2::SvLinkSource
* SfxViewFrame::DdeCreateLinkSource( const OUString
& )// the Item to be addressed
485 sal_Bool
SfxApplication::InitializeDde()
489 DBG_ASSERT( !pAppData_Impl
->pDdeService
,
490 "Dde can not be initialized multiple times" );
492 pAppData_Impl
->pDdeService
= new ImplDdeService( Application::GetAppName() );
493 nError
= pAppData_Impl
->pDdeService
->GetError();
496 pAppData_Impl
->pDocTopics
= new SfxDdeDocTopics_Impl
;
498 // we certainly want to support RTF!
499 pAppData_Impl
->pDdeService
->AddFormat( FORMAT_RTF
);
501 // Config path as a topic becauseof multiple starts
502 INetURLObject
aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
503 aOfficeLockFile
.insertName( "soffice.lck" );
504 OUString
aService( SfxDdeServiceName_Impl(
505 aOfficeLockFile
.GetMainURL(INetURLObject::DECODE_TO_IURI
) ) );
506 aService
= aService
.toAsciiUpperCase();
507 pAppData_Impl
->pDdeService2
= new ImplDdeService( aService
);
508 pAppData_Impl
->pTriggerTopic
= new SfxDdeTriggerTopic_Impl
;
509 pAppData_Impl
->pDdeService2
->AddTopic( *pAppData_Impl
->pTriggerTopic
);
515 void SfxAppData_Impl::DeInitDDE()
517 DELETEZ( pTriggerTopic
);
518 DELETEZ( pDdeService2
);
519 DELETEZ( pDocTopics
);
520 DELETEZ( pDdeService
);
524 void SfxApplication::AddDdeTopic( SfxObjectShell
* pSh
)
526 DBG_ASSERT( pAppData_Impl
->pDocTopics
, "There is no Dde-Service" );
527 //OV: DDE is disconnected in server mode!
528 if( !pAppData_Impl
->pDocTopics
)
531 // prevent double submit
533 sal_Bool bFnd
= sal_False
;
534 for (size_t n
= pAppData_Impl
->pDocTopics
->size(); n
;)
536 if( (*pAppData_Impl
->pDocTopics
)[ --n
]->pSh
== pSh
)
538 // If the document is untitled, is still a new Topic is created!
542 sShellNm
= pSh
->GetTitle(SFX_TITLE_FULLNAME
).toAsciiLowerCase();
544 OUString
sNm( (*pAppData_Impl
->pDocTopics
)[ n
]->GetName() );
545 if( sShellNm
== sNm
.toAsciiLowerCase() )
550 SfxDdeDocTopic_Impl
*const pTopic
= new SfxDdeDocTopic_Impl(pSh
);
551 pAppData_Impl
->pDocTopics
->push_back(pTopic
);
552 pAppData_Impl
->pDdeService
->AddTopic( *pTopic
);
556 void SfxApplication::RemoveDdeTopic( SfxObjectShell
* pSh
)
558 DBG_ASSERT( pAppData_Impl
->pDocTopics
, "There is no Dde-Service" );
559 //OV: DDE is disconnected in server mode!
560 if( !pAppData_Impl
->pDocTopics
)
563 for (size_t n
= pAppData_Impl
->pDocTopics
->size(); n
; )
565 SfxDdeDocTopic_Impl
*const pTopic
= (*pAppData_Impl
->pDocTopics
)[ --n
];
566 if (pTopic
->pSh
== pSh
)
568 pAppData_Impl
->pDdeService
->RemoveTopic( *pTopic
);
570 pAppData_Impl
->pDocTopics
->erase( pAppData_Impl
->pDocTopics
->begin() + n
);
575 const DdeService
* SfxApplication::GetDdeService() const
577 return pAppData_Impl
->pDdeService
;
580 DdeService
* SfxApplication::GetDdeService()
582 return pAppData_Impl
->pDdeService
;
585 //--------------------------------------------------------------------
587 bool SfxDdeTriggerTopic_Impl::Execute( const OUString
* )
592 //--------------------------------------------------------------------
593 DdeData
* SfxDdeDocTopic_Impl::Get( sal_uIntPtr nFormat
)
595 OUString
sMimeType( SotExchange::GetFormatMimeType( nFormat
));
596 ::com::sun::star::uno::Any aValue
;
597 long nRet
= pSh
->DdeGetData( GetCurItem(), sMimeType
, aValue
);
598 if( nRet
&& aValue
.hasValue() && ( aValue
>>= aSeq
) )
600 aData
= DdeData( aSeq
.getConstArray(), aSeq
.getLength(), nFormat
);
607 bool SfxDdeDocTopic_Impl::Put( const DdeData
* pData
)
609 aSeq
= ::com::sun::star::uno::Sequence
< sal_Int8
>(
610 (sal_Int8
*)(const void*)*pData
, (long)*pData
);
612 if( aSeq
.getLength() )
614 ::com::sun::star::uno::Any aValue
;
616 OUString
sMimeType( SotExchange::GetFormatMimeType( pData
->GetFormat() ));
617 bRet
= 0 != pSh
->DdeSetData( GetCurItem(), sMimeType
, aValue
);
624 bool SfxDdeDocTopic_Impl::Execute( const OUString
* pStr
)
626 long nRet
= pStr
? pSh
->DdeExecute( *pStr
) : 0;
630 bool SfxDdeDocTopic_Impl::MakeItem( const OUString
& rItem
)
632 AddItem( DdeItem( rItem
) );
636 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
639 ::sfx2::SvLinkSource
* pNewObj
= pSh
->DdeCreateLinkSource( GetCurItem() );
642 // then we also establish a corresponding SvBaseLink
643 OUString sNm
, sTmp( Application::GetAppName() );
644 ::sfx2::MakeLnkName( sNm
, &sTmp
, pSh
->GetTitle(SFX_TITLE_FULLNAME
), GetCurItem() );
645 new ::sfx2::SvBaseLink( sNm
, OBJECT_DDE_EXTERN
, pNewObj
);
651 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */