bump product version to 4.2.0.1
[LibreOffice.git] / sfx2 / source / appl / appdde.cxx
blobf3efec169948150c6ffebd551e7090808eda99fe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
41 #include "helper.hxx"
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();
63 #if defined( WNT )
64 class ImplDdeService : public DdeService
66 public:
67 ImplDdeService( const OUString& rNm )
68 : DdeService( rNm )
70 virtual bool MakeTopic( const OUString& );
72 virtual OUString Topics();
74 virtual bool SysTopicExecute( const OUString* pStr );
77 //--------------------------------------------------------------------
78 namespace
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!" );
88 try
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" );
110 return bRet;
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() )
119 return false;
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 );
128 while( pShell )
130 OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
131 if( sNm == sTmp.toAsciiLowerCase() )
133 SFX_APP()->AddDdeTopic( pShell );
134 bRet = true;
135 break;
137 pShell = SfxObjectShell::GetNext( *pShell, &aType );
140 if( !bRet )
142 INetURLObject aWorkPath( SvtPathOptions().GetWorkPath() );
143 INetURLObject aFile;
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,
155 &aName, &aNewView,
156 &aSilent, 0L );
158 if( pRet && pRet->ISA( SfxViewFrameItem ) &&
159 ((SfxViewFrameItem*)pRet)->GetFrame() &&
160 0 != ( pShell = ((SfxViewFrameItem*)pRet)
161 ->GetFrame()->GetObjectShell() ) )
163 SFX_APP()->AddDdeTopic( pShell );
164 bRet = true;
168 return bRet;
171 OUString ImplDdeService::Topics()
173 OUString sRet;
174 if( GetSysTopic() )
175 sRet += GetSysTopic()->GetName();
177 TypeId aType( TYPE(SfxObjectShell) );
178 SfxObjectShell* pShell = SfxObjectShell::GetFirst( &aType );
179 while( pShell )
181 if( SfxViewFrame::GetFirst( pShell ) )
183 if( !sRet.isEmpty() )
184 sRet += "\t";
185 sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
187 pShell = SfxObjectShell::GetNext( *pShell, &aType );
189 if( !sRet.isEmpty() )
190 sRet += "\r\n";
191 return sRet;
194 bool ImplDdeService::SysTopicExecute( const OUString* pStr )
196 return SFX_APP()->DdeExecute( *pStr );
198 #endif
200 class SfxDdeTriggerTopic_Impl : public DdeTopic
202 public:
203 SfxDdeTriggerTopic_Impl()
204 : DdeTopic( "TRIGGER" )
207 virtual bool Execute( const OUString* );
210 class SfxDdeDocTopic_Impl : public DdeTopic
212 public:
213 SfxObjectShell* pSh;
214 DdeData aData;
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*> {};
231 #if defined( WNT )
233 namespace {
235 /* [Description]
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.
242 [Example]
244 rCmd = "Open(\"d:\doc\doc.sdw\")"
245 rEvent = "Open"
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:
274 switch ( rCmd[n] )
276 case '"':
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));
283 n = i + 1;
284 break;
286 case ' ':
287 ++n;
288 break;
289 default:
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));
296 n = i + 1;
297 break;
302 GetpApp()->AppEvent( ApplicationEvent(eType, aData) );
303 return sal_True;
307 return sal_False;
312 /* Description]
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 );
331 if( !pRet )
333 SbxBase::ResetError();
334 return 0;
337 return 1;
340 #endif
342 /* [Description]
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
352 (void) rCmd;
353 #else
354 StarBASIC* pBasic = GetBasic();
355 DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
356 SbxVariable* pRet = pBasic->Execute( rCmd );
357 if( !pRet )
359 SbxBase::ResetError();
360 return 0;
362 #endif
363 return 1;
366 //--------------------------------------------------------------------
368 /* [Description]
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
379 return 0;
382 //--------------------------------------------------------------------
384 /* [Description]
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
395 return 0;
398 /* [Description]
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
407 return 0;
410 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
414 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
416 TypeId aType = TYPE(SfxObjectShell);
417 SfxObjectShell* p = GetFirst(&aType, false);
418 while (p)
420 if (&rServer != p)
421 p->ReconnectDdeLink(rServer);
423 p = GetNext(*p, &aType, false);
427 /* [Description]
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 );
442 return 0;
445 /* [Description]
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
456 return 0;
459 /* [Description]
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
470 return 0;
473 /* [Description]
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
482 return 0;
485 sal_Bool SfxApplication::InitializeDde()
487 int nError = 0;
488 #if defined( WNT )
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();
494 if( !nError )
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 );
511 #endif
512 return !nError;
515 void SfxAppData_Impl::DeInitDDE()
517 DELETEZ( pTriggerTopic );
518 DELETEZ( pDdeService2 );
519 DELETEZ( pDocTopics );
520 DELETEZ( pDdeService );
523 #if defined( WNT )
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 )
529 return;
531 // prevent double submit
532 OUString sShellNm;
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!
539 if( !bFnd )
541 bFnd = sal_True;
542 sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
544 OUString sNm( (*pAppData_Impl->pDocTopics)[ n ]->GetName() );
545 if( sShellNm == sNm.toAsciiLowerCase() )
546 return ;
550 SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
551 pAppData_Impl->pDocTopics->push_back(pTopic);
552 pAppData_Impl->pDdeService->AddTopic( *pTopic );
554 #endif
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 )
561 return;
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 );
569 delete 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* )
589 return true;
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 );
601 return &aData;
603 aSeq.realloc( 0 );
604 return 0;
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 );
611 bool bRet;
612 if( aSeq.getLength() )
614 ::com::sun::star::uno::Any aValue;
615 aValue <<= aSeq;
616 OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
617 bRet = 0 != pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
619 else
620 bRet = false;
621 return bRet;
624 bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
626 long nRet = pStr ? pSh->DdeExecute( *pStr ) : 0;
627 return 0 != nRet;
630 bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
632 AddItem( DdeItem( rItem ) );
633 return true;
636 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
638 bool bRet = false;
639 ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() );
640 if( pNewObj )
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 );
646 bRet = true;
648 return bRet;
651 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */