LibreOffice Module sfx2 (master) 1
appdde.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <string_view>
23
24#include <config_features.h>
25#include <rtl/character.hxx>
26#include <rtl/malformeduriexception.hxx>
27#include <rtl/uri.hxx>
28#include <sot/exchange.hxx>
29#include <svl/eitem.hxx>
30#include <basic/sbstar.hxx>
31#include <svl/stritem.hxx>
32#include <svl/svdde.hxx>
33#include <sfx2/lnkbase.hxx>
34#include <sfx2/linkmgr.hxx>
35
36#include <tools/debug.hxx>
37#include <tools/urlobj.hxx>
40#include <vcl/svapp.hxx>
41
42#include <sfx2/app.hxx>
43#include <appdata.hxx>
44#include <sfx2/objsh.hxx>
45#include <sfx2/viewfrm.hxx>
46#include <sfx2/dispatch.hxx>
47#include <sfx2/sfxsids.hrc>
48#include <sfx2/docfile.hxx>
49#include <ucbhelper/content.hxx>
51
52#if defined(_WIN32)
53
54static OUString SfxDdeServiceName_Impl( const OUString& sIn )
55{
56 OUStringBuffer sReturn(sIn.getLength());
57
58 for ( sal_uInt16 n = sIn.getLength(); n; --n )
59 {
60 sal_Unicode cChar = sIn[n-1];
61 if (rtl::isAsciiAlphanumeric(cChar))
62 sReturn.append(cChar);
63 }
64
65 return sReturn.makeStringAndClear();
66}
67
68namespace {
69
70class ImplDdeService : public DdeService
71{
72public:
73 explicit ImplDdeService( const OUString& rNm )
74 : DdeService( rNm )
75 {}
76 virtual bool MakeTopic( const OUString& );
77
78 virtual OUString Topics();
79
80 virtual bool SysTopicExecute( const OUString* pStr );
81};
82
83 bool lcl_IsDocument( std::u16string_view rContent )
84 {
85 using namespace com::sun::star;
86
87 bool bRet = false;
88 INetURLObject aObj( rContent );
89 DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
90
91 try
92 {
93 ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
94 bRet = aCnt.isDocument();
95 }
96 catch( const uno::Exception& )
97 {
98 TOOLS_WARN_EXCEPTION( "sfx.appl", "" );
99 }
100
101 return bRet;
102 }
103}
104
105bool ImplDdeService::MakeTopic( const OUString& rNm )
106{
107 // Workaround for Event after Main() under OS/2
108 // happens when exiting starts the App again
110 return false;
111
112 // The Topic rNm is sought, do we have it?
113 // First only loop over the ObjectShells to find those
114 // with the specific name:
115 bool bRet = false;
116 OUString sNm( rNm.toAsciiLowerCase() );
118 while( pShell )
119 {
120 OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
121 if( sNm == sTmp.toAsciiLowerCase() )
122 {
123 SfxGetpApp()->AddDdeTopic( pShell );
124 bRet = true;
125 break;
126 }
127 pShell = SfxObjectShell::GetNext( *pShell );
128 }
129
130 if( !bRet )
131 {
132 bool abs;
133 OUString url;
134 try {
135 url = rtl::Uri::convertRelToAbs(SvtPathOptions().GetWorkPath(), rNm);
136 abs = true;
137 } catch (rtl::MalformedUriException &) {
138 abs = false;
139 }
140 if ( abs && lcl_IsDocument( url ) )
141 {
142 // File exists? then try to load it:
143 SfxStringItem aName( SID_FILE_NAME, url );
144 SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, true);
145
146 SfxBoolItem aSilent(SID_SILENT, true);
147 SfxDispatcher* pDispatcher = SfxGetpApp()->GetDispatcher_Impl();
148 const SfxPoolItem* pRet = pDispatcher->ExecuteList(SID_OPENDOC,
150 { &aName, &aNewView, &aSilent });
151
152 if( auto const item = dynamic_cast< const SfxViewFrameItem *>( pRet );
153 item &&
154 item->GetFrame() &&
155 nullptr != ( pShell = item->GetFrame()->GetObjectShell() ) )
156 {
157 SfxGetpApp()->AddDdeTopic( pShell );
158 bRet = true;
159 }
160 }
161 }
162 return bRet;
163}
164
165OUString ImplDdeService::Topics()
166{
167 OUString sRet;
168 if( GetSysTopic() )
169 sRet += GetSysTopic()->GetName();
170
172 while( pShell )
173 {
174 if( SfxViewFrame::GetFirst( pShell ) )
175 {
176 if( !sRet.isEmpty() )
177 sRet += "\t";
178 sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
179 }
180 pShell = SfxObjectShell::GetNext( *pShell );
181 }
182 if( !sRet.isEmpty() )
183 sRet += "\r\n";
184 return sRet;
185}
186
187bool ImplDdeService::SysTopicExecute( const OUString* pStr )
188{
189 return SfxApplication::DdeExecute( *pStr );
190}
191#endif
192
194{
195#if defined(_WIN32)
196public:
197 SfxObjectShell* pSh;
199 css::uno::Sequence< sal_Int8 > aSeq;
200
201 explicit SfxDdeDocTopic_Impl( SfxObjectShell* pShell )
202 : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell )
203 {}
204
205 virtual DdeData* Get( SotClipboardFormatId ) override;
206 virtual bool Put( const DdeData* ) override;
207 virtual bool Execute( const OUString* ) override;
208 virtual bool StartAdviseLoop() override;
209 virtual bool MakeItem( const OUString& rItem ) override;
210#endif
211};
212
213
214#if defined(_WIN32)
215
216namespace {
217
218/* [Description]
219
220 Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
221 this data into a <ApplicationEvent>, which is then executed through
222 <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
223 TRUE is returned, otherwise FALSE.
224
225 [Example]
226
227 rCmd = "Open(\"d:\doc\doc.sdw\")"
228 rEvent = "Open"
229*/
230bool SfxAppEvent_Impl( const OUString& rCmd, std::u16string_view rEvent,
232{
233 OUString sEvent(OUString::Concat(rEvent) + "(");
234 if (rCmd.startsWithIgnoreAsciiCase(sEvent))
235 {
236 sal_Int32 start = sEvent.getLength();
237 if ( rCmd.getLength() - start >= 2 )
238 {
239 // Transform into the ApplicationEvent Format
240 //TODO: I /assume/ that rCmd should match the syntax of
241 // <http://msdn.microsoft.com/en-us/library/ms648995.aspx>
242 // "WM_DDE_EXECUTE message" but does not (handle commands enclosed
243 // in [...]; handle commas separating multiple arguments; handle
244 // double "", ((, )), [[, ]] in quoted arguments); see also the mail
245 // thread starting at <http://lists.freedesktop.org/archives/
246 // libreoffice/2013-July/054779.html> "DDE on Windows."
247 std::vector<OUString> aData;
248 for ( sal_Int32 n = start; n < rCmd.getLength() - 1; )
249 {
250 // Resiliently read arguments either starting with " and
251 // spanning to the next " (if any; TODO: do we need to undo any
252 // escaping within the string?) or with neither " nor SPC and
253 // spanning to the next SPC (if any; TODO: is this from not
254 // wrapped in "..." relevant? it would have been parsed by the
255 // original code even if that was only by accident, so I left it
256 // in), with runs of SPCs treated like single ones:
257 switch ( rCmd[n] )
258 {
259 case '"':
260 {
261 sal_Int32 i = rCmd.indexOf('"', ++n);
262 if (i < 0 || i > rCmd.getLength() - 1) {
263 i = rCmd.getLength() - 1;
264 }
265 aData.push_back(rCmd.copy(n, i - n));
266 n = i + 1;
267 break;
268 }
269 case ' ':
270 ++n;
271 break;
272 default:
273 {
274 sal_Int32 i = rCmd.indexOf(' ', n);
275 if (i < 0 || i > rCmd.getLength() - 1) {
276 i = rCmd.getLength() - 1;
277 }
278 aData.push_back(rCmd.copy(n, i - n));
279 n = i + 1;
280 break;
281 }
282 }
283 }
284
285 GetpApp()->AppEvent( ApplicationEvent(eType, std::move(aData)) );
286 return true;
287 }
288 }
289
290 return false;
291}
292
293}
294
295/* Description]
296
297 This method can be overridden by application developers, to receive
298 DDE-commands directed to their SfxApplication subclass.
299
300 The base implementation understands the API functionality of the
301 relevant SfxApplication subclass in BASIC syntax. Return values can
302 not be transferred, unfortunately.
303*/
304bool SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
305{
306 // Print or Open-Event?
307 if ( !( SfxAppEvent_Impl( rCmd, u"Print", ApplicationEvent::Type::Print ) ||
308 SfxAppEvent_Impl( rCmd, u"Open", ApplicationEvent::Type::Open ) ) )
309 {
310 // all others are BASIC
312 DBG_ASSERT( pBasic, "Where is the Basic???" );
313 SbxVariable* pRet = pBasic->Execute( rCmd );
314 if( !pRet )
315 {
317 return false;
318 }
319 }
320 return true;
321}
322
323/* [Description]
324
325 This method can be overridden by application developers, to receive
326 DDE-commands directed to the their SfxApplication subclass.
327
328 The base implementation does nothing and returns 0.
329*/
330bool SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
331{
332#if !HAVE_FEATURE_SCRIPTING
333 (void) rCmd;
334#else
336 DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
337 SbxVariable* pRet = pBasic->Execute( rCmd );
338 if( !pRet )
339 {
341 return false;
342 }
343#endif
344 return true;
345}
346
347/* [Description]
348
349 This method can be overridden by application developers, to receive
350 DDE-data-requests directed to their SfxApplication subclass.
351
352 The base implementation provides no data and returns false.
353*/
354bool SfxObjectShell::DdeGetData( const OUString&, // the Item to be addressed
355 const OUString&, // in: Format
356 css::uno::Any& )// out: requested data
357{
358 return false;
359}
360
361
362/* [Description]
363
364 This method can be overridden by application developers, to receive
365 DDE-data directed to their SfxApplication subclass.
366
367 The base implementation is not receiving any data and returns false.
368*/
369bool SfxObjectShell::DdeSetData( const OUString&, // the Item to be addressed
370 const OUString&, // in: Format
371 const css::uno::Any& )// out: requested data
372{
373 return false;
374}
375
376#endif
377
378/* [Description]
379
380 This method can be overridden by application developers, to establish
381 a DDE-hotlink to their SfxApplication subclass.
382
383 The base implementation is not generate a link and returns 0.
384*/
385::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed
386{
387 return nullptr;
388}
389
391{
392}
393
395{
396 SfxObjectShell* p = GetFirst(nullptr, false);
397 while (p)
398 {
399 if (&rServer != p)
400 p->ReconnectDdeLink(rServer);
401
402 p = GetNext(*p, nullptr, false);
403 }
404}
405
407{
408 int nError = 0;
409#if defined(_WIN32)
410 DBG_ASSERT( !pImpl->pDdeService,
411 "Dde can not be initialized multiple times" );
412
413 pImpl->pDdeService.reset(new ImplDdeService( Application::GetAppName() ));
414 nError = pImpl->pDdeService->GetError();
415 if( !nError )
416 {
417 // we certainly want to support RTF!
418 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RTF );
419 pImpl->pDdeService->AddFormat( SotClipboardFormatId::RICHTEXT );
420
421 // Config path as a topic because of multiple starts
422 INetURLObject aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
423 aOfficeLockFile.insertName( u"soffice.lck" );
424 OUString aService( SfxDdeServiceName_Impl(
426 aService = aService.toAsciiUpperCase();
427 pImpl->pDdeService2.reset( new ImplDdeService( aService ));
428 pImpl->pTriggerTopic.reset(new SfxDdeTriggerTopic_Impl);
429 pImpl->pDdeService2->AddTopic( *pImpl->pTriggerTopic );
430 }
431#endif
432 return !nError;
433}
434
436{
437 pTriggerTopic.reset();
438 pDdeService2.reset();
439 maDocTopics.clear();
440 pDdeService.reset();
441}
442
443#if defined(_WIN32)
444void SfxApplication::AddDdeTopic( SfxObjectShell* pSh )
445{
446 //OV: DDE is disconnected in server mode!
447 if( pImpl->maDocTopics.empty() )
448 return;
449
450 // prevent double submit
451 OUString sShellNm;
452 bool bFnd = false;
453 for (size_t n = pImpl->maDocTopics.size(); n;)
454 {
455 if( pImpl->maDocTopics[ --n ]->pSh == pSh )
456 {
457 // If the document is untitled, is still a new Topic is created!
458 if( !bFnd )
459 {
460 bFnd = true;
461 sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
462 }
463 OUString sNm( pImpl->maDocTopics[ n ]->GetName() );
464 if( sShellNm == sNm.toAsciiLowerCase() )
465 return ;
466 }
467 }
468
469 SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
470 pImpl->maDocTopics.push_back(pTopic);
471 pImpl->pDdeService->AddTopic( *pTopic );
472}
473#endif
474
476{
477#if defined(_WIN32)
478 //OV: DDE is disconnected in server mode!
479 if( pImpl->maDocTopics.empty() )
480 return;
481
482 for (size_t n = pImpl->maDocTopics.size(); n; )
483 {
484 SfxDdeDocTopic_Impl *const pTopic = pImpl->maDocTopics[ --n ];
485 if (pTopic->pSh == pSh)
486 {
487 pImpl->pDdeService->RemoveTopic( *pTopic );
488 delete pTopic;
489 pImpl->maDocTopics.erase( pImpl->maDocTopics.begin() + n );
490 }
491 }
492#else
493 (void) pSh;
494#endif
495}
496
498{
499 return pImpl->pDdeService.get();
500}
501
503{
504 return pImpl->pDdeService.get();
505}
506
507#if defined(_WIN32)
508
510{
511 OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
512 css::uno::Any aValue;
513 bool bRet = pSh->DdeGetData( GetCurItem(), sMimeType, aValue );
514 if( bRet && aValue.hasValue() && ( aValue >>= aSeq ) )
515 {
516 aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
517 return &aData;
518 }
519 aSeq.realloc( 0 );
520 return nullptr;
521}
522
523bool SfxDdeDocTopic_Impl::Put( const DdeData* pData )
524{
525 aSeq = css::uno::Sequence< sal_Int8 >(
526 static_cast<sal_Int8 const *>(pData->getData()), pData->getSize() );
527 bool bRet;
528 if( aSeq.getLength() )
529 {
530 css::uno::Any aValue;
531 aValue <<= aSeq;
532 OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
533 bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
534 }
535 else
536 bRet = false;
537 return bRet;
538}
539
540bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
541{
542 return pStr && pSh->DdeExecute( *pStr );
543}
544
545bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
546{
547 AddItem( DdeItem( rItem ) );
548 return true;
549}
550
552{
553 bool bRet = false;
555 if( pNewObj )
556 {
557 // then we also establish a corresponding SvBaseLink
558 OUString sNm, sTmp( Application::GetAppName() );
560 new ::sfx2::SvBaseLink( sNm, sfx2::SvBaseLinkObjectType::DdeExternal, pNewObj );
561 bRet = true;
562 }
563 return bRet;
564}
565
566#endif
567
568/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SfxApplication * SfxGetpApp()
Definition: app.hxx:231
static BasicDLL * pBasic
Definition: appdata.cxx:44
static bool IsInExecute()
virtual void AppEvent(const ApplicationEvent &rAppEvent)
static OUString GetAppName()
OUString Topics()
virtual bool MakeItem(const OUString &rItem)
virtual bool StartAdviseLoop()
DdeTopic(SAL_UNUSED_PARAMETER const OUString &)
const OUString & GetCurItem() const
virtual bool Put(const DdeData *)
virtual bool Execute(const OUString *)
DdeItem * AddItem(const DdeItem &)
friend friend class DdeItem
virtual DdeData * Get(SotClipboardFormatId)
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool insertName(std::u16string_view rTheName, bool bAppendFinalSlash=false, sal_Int32 nIndex=LAST_SEGMENT, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
static void ResetError()
void DeInitDDE()
Definition: appdde.cxx:435
std::unique_ptr< DdeService > pDdeService
Definition: appdata.hxx:67
std::vector< SfxDdeDocTopic_Impl * > maDocTopics
Definition: appdata.hxx:68
std::unique_ptr< SfxDdeTriggerTopic_Impl > pTriggerTopic
Definition: appdata.hxx:69
std::unique_ptr< DdeService > pDdeService2
Definition: appdata.hxx:70
static StarBASIC * GetBasic()
Definition: appbas.cxx:100
SAL_DLLPRIVATE SfxDispatcher * GetDispatcher_Impl()
Definition: app.cxx:228
std::unique_ptr< SfxAppData_Impl > pImpl
Definition: app.hxx:97
void RemoveDdeTopic(SfxObjectShell const *)
Definition: appdde.cxx:475
bool InitializeDde()
Definition: appdde.cxx:406
const DdeService * GetDdeService() const
Definition: appdde.cxx:497
const SfxPoolItem * ExecuteList(sal_uInt16 nSlot, SfxCallMode nCall, std::initializer_list< SfxPoolItem const * > args, std::initializer_list< SfxPoolItem const * > internalargs=std::initializer_list< SfxPoolItem const * >())
Method to execute a <SfxSlot>s over the Slot-Id.
Definition: dispatch.cxx:931
virtual void ReconnectDdeLink(SfxObjectShell &rServer)
Definition: appdde.cxx:390
virtual ::sfx2::SvLinkSource * DdeCreateLinkSource(const OUString &rItem)
Definition: appdde.cxx:385
static SAL_WARN_UNUSED_RESULT SfxObjectShell * GetNext(const SfxObjectShell &rPrev, const std::function< bool(const SfxObjectShell *)> &isObjectShell=nullptr, bool bOnlyVisible=true)
Definition: objxtor.cxx:452
OUString GetTitle(sal_uInt16 nMaxLen=0) const
Definition: objmisc.cxx:710
StarBASIC * GetBasic() const
Definition: objxtor.cxx:755
static SAL_WARN_UNUSED_RESULT SfxObjectShell * GetFirst(const std::function< bool(const SfxObjectShell *)> &isObjectShell=nullptr, bool bOnlyVisible=true)
Definition: objxtor.cxx:427
static void ReconnectDdeLinks(SfxObjectShell &rServer)
Definition: appdde.cxx:394
const OUString & GetName() const
Returns the name of the Shell object.
Definition: shell.cxx:119
SfxViewFrame * GetFrame() const
Definition: viewfrm.hxx:275
static SAL_WARN_UNUSED_RESULT SfxViewFrame * GetFirst(const SfxObjectShell *pDoc=nullptr, bool bOnlyVisible=true)
Definition: viewfrm.cxx:1983
static OUString GetFormatMimeType(SotClipboardFormatId nFormat)
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
float u
SotClipboardFormatId
OUString aName
void * p
sal_Int64 n
Sequence< sal_Int8 > aSeq
Definition: lnkbase2.cxx:83
DdeData aData
Definition: lnkbase2.cxx:82
std::unique_ptr< sal_Int32[]> pData
Reference< XComponentContext > getProcessComponentContext()
int i
void MakeLnkName(OUString &rName, const OUString *pType, std::u16string_view rFile, std::u16string_view rLink, const OUString *pFilter)
Definition: linkmgr2.cxx:379
SwNodeOffset abs(const SwNodeOffset &a)
#define SFX_TITLE_FULLNAME
Definition: objsh.hxx:114
VCL_DLLPUBLIC Application * GetpApp()
sal_uInt16 sal_Unicode
signed char sal_Int8