Branch libreoffice-5-0-4
[LibreOffice.git] / sfx2 / source / appl / appdde.cxx
blob241fedf17166a8b2b34c623c821e953a06296bd0
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 <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>
43 #include "helper.hxx"
44 #include <sfx2/docfile.hxx>
45 #include <comphelper/processfactory.hxx>
46 #include <comphelper/string.hxx>
47 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
49 #if defined WNT
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
67 public:
68 ImplDdeService( const OUString& rNm )
69 : DdeService( rNm )
71 virtual bool MakeTopic( const OUString& );
73 virtual OUString Topics();
75 virtual bool SysTopicExecute( const OUString* pStr );
79 namespace
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!" );
89 try
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" );
111 return bRet;
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() )
120 return false;
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 );
129 while( pShell )
131 OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
132 if( sNm == sTmp.toAsciiLowerCase() )
134 SfxGetpApp()->AddDdeTopic( pShell );
135 bRet = true;
136 break;
138 pShell = SfxObjectShell::GetNext( *pShell, &aType );
141 if( !bRet )
143 INetURLObject aWorkPath( SvtPathOptions().GetWorkPath() );
144 INetURLObject aFile;
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,
156 &aName, &aNewView,
157 &aSilent, 0L );
159 if( pRet && pRet->ISA( SfxViewFrameItem ) &&
160 ((SfxViewFrameItem*)pRet)->GetFrame() &&
161 0 != ( pShell = ((SfxViewFrameItem*)pRet)
162 ->GetFrame()->GetObjectShell() ) )
164 SfxGetpApp()->AddDdeTopic( pShell );
165 bRet = true;
169 return bRet;
172 OUString ImplDdeService::Topics()
174 OUString sRet;
175 if( GetSysTopic() )
176 sRet += GetSysTopic()->GetName();
178 TypeId aType( TYPE(SfxObjectShell) );
179 SfxObjectShell* pShell = SfxObjectShell::GetFirst( &aType );
180 while( pShell )
182 if( SfxViewFrame::GetFirst( pShell ) )
184 if( !sRet.isEmpty() )
185 sRet += "\t";
186 sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
188 pShell = SfxObjectShell::GetNext( *pShell, &aType );
190 if( !sRet.isEmpty() )
191 sRet += "\r\n";
192 return sRet;
195 bool ImplDdeService::SysTopicExecute( const OUString* pStr )
197 return SfxGetpApp()->DdeExecute( *pStr );
199 #endif
201 class SfxDdeTriggerTopic_Impl : public DdeTopic
203 #if defined WNT
204 public:
205 SfxDdeTriggerTopic_Impl()
206 : DdeTopic( "TRIGGER" )
209 virtual bool Execute( const OUString* ) SAL_OVERRIDE { return true; }
210 #endif
213 class SfxDdeDocTopic_Impl : public DdeTopic
215 #if defined WNT
216 public:
217 SfxObjectShell* pSh;
218 DdeData aData;
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;
230 #endif
234 #if defined WNT
236 namespace {
238 /* [Description]
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.
245 [Example]
247 rCmd = "Open(\"d:\doc\doc.sdw\")"
248 rEvent = "Open"
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:
277 switch ( rCmd[n] )
279 case '"':
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));
286 n = i + 1;
287 break;
289 case ' ':
290 ++n;
291 break;
292 default:
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));
299 n = i + 1;
300 break;
305 GetpApp()->AppEvent( ApplicationEvent(eType, aData) );
306 return sal_True;
310 return sal_False;
315 /* Description]
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 );
334 if( !pRet )
336 SbxBase::ResetError();
337 return 0;
340 return 1;
343 /* [Description]
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
353 (void) rCmd;
354 #else
355 StarBASIC* pBasic = GetBasic();
356 DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
357 SbxVariable* pRet = pBasic->Execute( rCmd );
358 if( !pRet )
360 SbxBase::ResetError();
361 return 0;
363 #endif
364 return 1;
367 /* [Description]
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
378 return false;
383 /* [Description]
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
394 return false;
397 #endif
399 /* [Description]
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
408 return 0;
411 void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
415 void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
417 TypeId aType = TYPE(SfxObjectShell);
418 SfxObjectShell* p = GetFirst(&aType, false);
419 while (p)
421 if (&rServer != p)
422 p->ReconnectDdeLink(rServer);
424 p = GetNext(*p, &aType, false);
428 bool SfxApplication::InitializeDde()
430 int nError = 0;
431 #if defined( WNT )
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();
437 if( !nError )
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 );
454 #endif
455 return !nError;
458 void SfxAppData_Impl::DeInitDDE()
460 DELETEZ( pTriggerTopic );
461 DELETEZ( pDdeService2 );
462 DELETEZ( pDocTopics );
463 DELETEZ( pDdeService );
466 #if defined( WNT )
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 )
472 return;
474 // prevent double submit
475 OUString sShellNm;
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!
482 if( !bFnd )
484 bFnd = sal_True;
485 sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
487 OUString sNm( (*pAppData_Impl->pDocTopics)[ n ]->GetName() );
488 if( sShellNm == sNm.toAsciiLowerCase() )
489 return ;
493 SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
494 pAppData_Impl->pDocTopics->push_back(pTopic);
495 pAppData_Impl->pDdeService->AddTopic( *pTopic );
497 #endif
499 void SfxApplication::RemoveDdeTopic( SfxObjectShell* pSh )
501 #if defined WNT
502 DBG_ASSERT( pAppData_Impl->pDocTopics, "There is no Dde-Service" );
503 //OV: DDE is disconnected in server mode!
504 if( !pAppData_Impl->pDocTopics )
505 return;
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 );
513 delete pTopic;
514 pAppData_Impl->pDocTopics->erase( pAppData_Impl->pDocTopics->begin() + n );
517 #else
518 (void) pSh;
519 #endif
522 const DdeService* SfxApplication::GetDdeService() const
524 return pAppData_Impl->pDdeService;
527 DdeService* SfxApplication::GetDdeService()
529 return pAppData_Impl->pDdeService;
532 #if defined WNT
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 );
542 return &aData;
544 aSeq.realloc( 0 );
545 return 0;
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 );
552 bool bRet;
553 if( aSeq.getLength() )
555 ::com::sun::star::uno::Any aValue;
556 aValue <<= aSeq;
557 OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
558 bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
560 else
561 bRet = false;
562 return bRet;
565 bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
567 long nRet = pStr ? pSh->DdeExecute( *pStr ) : 0;
568 return 0 != nRet;
571 bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
573 AddItem( DdeItem( rItem ) );
574 return true;
577 bool SfxDdeDocTopic_Impl::StartAdviseLoop()
579 bool bRet = false;
580 ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() );
581 if( pNewObj )
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 );
587 bRet = true;
589 return bRet;
592 #endif
594 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */