Line data Source code
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 <config_features.h>
21 :
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>
31 :
32 : #include <tools/urlobj.hxx>
33 : #include <tools/diagnose_ex.h>
34 : #include <unotools/pathoptions.hxx>
35 :
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>
48 :
49 : #if defined WNT
50 :
51 : OUString SfxDdeServiceName_Impl( const OUString& sIn )
52 : {
53 : OUStringBuffer sReturn(sIn.getLength());
54 :
55 : for ( sal_uInt16 n = sIn.getLength(); n; --n )
56 : {
57 : sal_Unicode cChar = sIn[n-1];
58 : if (comphelper::string::isalnumAscii(cChar))
59 : sReturn.append(cChar);
60 : }
61 :
62 : return sReturn.makeStringAndClear();
63 : }
64 :
65 : class ImplDdeService : public DdeService
66 : {
67 : public:
68 : ImplDdeService( const OUString& rNm )
69 : : DdeService( rNm )
70 : {}
71 : virtual bool MakeTopic( const OUString& );
72 :
73 : virtual OUString Topics();
74 :
75 : virtual bool SysTopicExecute( const OUString* pStr );
76 : };
77 :
78 :
79 : namespace
80 : {
81 : sal_Bool lcl_IsDocument( const OUString& rContent )
82 : {
83 : using namespace com::sun::star;
84 :
85 : sal_Bool bRet = sal_False;
86 : INetURLObject aObj( rContent );
87 : DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
88 :
89 : try
90 : {
91 : ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::NO_DECODE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
92 : bRet = aCnt.isDocument();
93 : }
94 : catch( const ucb::CommandAbortedException& )
95 : {
96 : DBG_WARNING( "CommandAbortedException" );
97 : }
98 : catch( const ucb::IllegalIdentifierException& )
99 : {
100 : DBG_WARNING( "IllegalIdentifierException" );
101 : }
102 : catch( const ucb::ContentCreationException& )
103 : {
104 : DBG_WARNING( "IllegalIdentifierException" );
105 : }
106 : catch( const uno::Exception& )
107 : {
108 : SAL_WARN( "sfx.appl", "Any other exception" );
109 : }
110 :
111 : return bRet;
112 : }
113 : }
114 :
115 : bool ImplDdeService::MakeTopic( const OUString& rNm )
116 : {
117 : // Workaround for Event after Main() under OS/2
118 : // happens when exiting starts the App again
119 : if ( !Application::IsInExecute() )
120 : return false;
121 :
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 )
130 : {
131 : OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
132 : if( sNm == sTmp.toAsciiLowerCase() )
133 : {
134 : SfxGetpApp()->AddDdeTopic( pShell );
135 : bRet = true;
136 : break;
137 : }
138 : pShell = SfxObjectShell::GetNext( *pShell, &aType );
139 : }
140 :
141 : if( !bRet )
142 : {
143 : INetURLObject aWorkPath( SvtPathOptions().GetWorkPath() );
144 : INetURLObject aFile;
145 : if ( aWorkPath.GetNewAbsURL( rNm, &aFile ) &&
146 : lcl_IsDocument( aFile.GetMainURL( INetURLObject::NO_DECODE ) ) )
147 : {
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);
151 :
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 );
158 :
159 : if( pRet && pRet->ISA( SfxViewFrameItem ) &&
160 : ((SfxViewFrameItem*)pRet)->GetFrame() &&
161 : 0 != ( pShell = ((SfxViewFrameItem*)pRet)
162 : ->GetFrame()->GetObjectShell() ) )
163 : {
164 : SfxGetpApp()->AddDdeTopic( pShell );
165 : bRet = true;
166 : }
167 : }
168 : }
169 : return bRet;
170 : }
171 :
172 : OUString ImplDdeService::Topics()
173 : {
174 : OUString sRet;
175 : if( GetSysTopic() )
176 : sRet += GetSysTopic()->GetName();
177 :
178 : TypeId aType( TYPE(SfxObjectShell) );
179 : SfxObjectShell* pShell = SfxObjectShell::GetFirst( &aType );
180 : while( pShell )
181 : {
182 : if( SfxViewFrame::GetFirst( pShell ) )
183 : {
184 : if( !sRet.isEmpty() )
185 : sRet += "\t";
186 : sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
187 : }
188 : pShell = SfxObjectShell::GetNext( *pShell, &aType );
189 : }
190 : if( !sRet.isEmpty() )
191 : sRet += "\r\n";
192 : return sRet;
193 : }
194 :
195 : bool ImplDdeService::SysTopicExecute( const OUString* pStr )
196 : {
197 : return SfxGetpApp()->DdeExecute( *pStr );
198 : }
199 : #endif
200 :
201 : class SfxDdeTriggerTopic_Impl : public DdeTopic
202 : {
203 : #if defined WNT
204 : public:
205 : SfxDdeTriggerTopic_Impl()
206 : : DdeTopic( "TRIGGER" )
207 : {}
208 :
209 : virtual bool Execute( const OUString* ) SAL_OVERRIDE { return true; }
210 : #endif
211 : };
212 :
213 : class SfxDdeDocTopic_Impl : public DdeTopic
214 : {
215 : #if defined WNT
216 : public:
217 : SfxObjectShell* pSh;
218 : DdeData aData;
219 : ::com::sun::star::uno::Sequence< sal_Int8 > aSeq;
220 :
221 : SfxDdeDocTopic_Impl( SfxObjectShell* pShell )
222 : : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell )
223 : {}
224 :
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
231 : };
232 :
233 :
234 : #if defined WNT
235 :
236 : namespace {
237 :
238 : /* [Description]
239 :
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.
244 :
245 : [Example]
246 :
247 : rCmd = "Open(\"d:\doc\doc.sdw\")"
248 : rEvent = "Open"
249 : */
250 : sal_Bool SfxAppEvent_Impl( const OUString& rCmd, const OUString& rEvent,
251 : ApplicationEvent::Type eType )
252 : {
253 : OUString sEvent(rEvent + "(");
254 : if (rCmd.startsWithIgnoreAsciiCase(sEvent))
255 : {
256 : sal_Int32 start = sEvent.getLength();
257 : if ( rCmd.getLength() - start >= 2 )
258 : {
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; )
269 : {
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] )
278 : {
279 : case '"':
280 : {
281 : sal_Int32 i = rCmd.indexOf('"', ++n);
282 : if (i < 0 || i > rCmd.getLength() - 1) {
283 : i = rCmd.getLength() - 1;
284 : }
285 : aData.push_back(rCmd.copy(n, i - n));
286 : n = i + 1;
287 : break;
288 : }
289 : case ' ':
290 : ++n;
291 : break;
292 : default:
293 : {
294 : sal_Int32 i = rCmd.indexOf(' ', n);
295 : if (i < 0 || i > rCmd.getLength() - 1) {
296 : i = rCmd.getLength() - 1;
297 : }
298 : aData.push_back(rCmd.copy(n, i - n));
299 : n = i + 1;
300 : break;
301 : }
302 : }
303 : }
304 :
305 : GetpApp()->AppEvent( ApplicationEvent(eType, aData) );
306 : return sal_True;
307 : }
308 : }
309 :
310 : return sal_False;
311 : }
312 :
313 : }
314 :
315 : /* Description]
316 :
317 : This method can be overridden by application developers, to receive
318 : DDE-commands directed to their SfxApplication subclass.
319 :
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.
323 : */
324 : long SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
325 : {
326 : // Print or Open-Event?
327 : if ( !( SfxAppEvent_Impl( rCmd, "Print", ApplicationEvent::TYPE_PRINT ) ||
328 : SfxAppEvent_Impl( rCmd, "Open", ApplicationEvent::TYPE_OPEN ) ) )
329 : {
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 )
335 : {
336 : SbxBase::ResetError();
337 : return 0;
338 : }
339 : }
340 : return 1;
341 : }
342 :
343 : /* [Description]
344 :
345 : This method can be overridden by application developers, to receive
346 : DDE-commands directed to the their SfxApplication subclass.
347 :
348 : The base implementation does nothing and returns 0.
349 : */
350 : long SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
351 : {
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 )
359 : {
360 : SbxBase::ResetError();
361 : return 0;
362 : }
363 : #endif
364 : return 1;
365 : }
366 :
367 : /* [Description]
368 :
369 : This method can be overridden by application developers, to receive
370 : DDE-data-requests directed to their SfxApplication subclass.
371 :
372 : The base implementation provides no data and returns false.
373 : */
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
377 : {
378 : return false;
379 : }
380 :
381 :
382 :
383 : /* [Description]
384 :
385 : This method can be overridden by application developers, to receive
386 : DDE-data directed to their SfxApplication subclass.
387 :
388 : The base implementation is not receiving any data and returns false.
389 : */
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
393 : {
394 : return false;
395 : }
396 :
397 : #endif
398 :
399 : /* [Description]
400 :
401 : This method can be overridden by application developers, to establish
402 : a DDE-hotlink to their SfxApplication subclass.
403 :
404 : The base implementation is not generate a link and returns 0.
405 : */
406 0 : ::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed
407 : {
408 0 : return 0;
409 : }
410 :
411 497 : void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
412 : {
413 497 : }
414 :
415 2955 : void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
416 : {
417 2955 : TypeId aType = TYPE(SfxObjectShell);
418 2955 : SfxObjectShell* p = GetFirst(&aType, false);
419 10218 : while (p)
420 : {
421 4308 : if (&rServer != p)
422 1353 : p->ReconnectDdeLink(rServer);
423 :
424 4308 : p = GetNext(*p, &aType, false);
425 : }
426 2955 : }
427 :
428 208 : bool SfxApplication::InitializeDde()
429 : {
430 208 : int nError = 0;
431 : #if defined( WNT )
432 : DBG_ASSERT( !pAppData_Impl->pDdeService,
433 : "Dde can not be initialized multiple times" );
434 :
435 : pAppData_Impl->pDdeService = new ImplDdeService( Application::GetAppName() );
436 : nError = pAppData_Impl->pDdeService->GetError();
437 : if( !nError )
438 : {
439 : pAppData_Impl->pDocTopics = new SfxDdeDocTopics_Impl;
440 :
441 : // we certainly want to support RTF!
442 : pAppData_Impl->pDdeService->AddFormat( SotClipboardFormatId::RTF );
443 :
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 );
453 : }
454 : #endif
455 208 : return !nError;
456 : }
457 :
458 119 : void SfxAppData_Impl::DeInitDDE()
459 : {
460 119 : DELETEZ( pTriggerTopic );
461 119 : DELETEZ( pDdeService2 );
462 119 : DELETEZ( pDocTopics );
463 119 : DELETEZ( pDdeService );
464 119 : }
465 :
466 : #if defined( WNT )
467 : void SfxApplication::AddDdeTopic( SfxObjectShell* pSh )
468 : {
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;
473 :
474 : // prevent double submit
475 : OUString sShellNm;
476 : sal_Bool bFnd = sal_False;
477 : for (size_t n = pAppData_Impl->pDocTopics->size(); n;)
478 : {
479 : if( (*pAppData_Impl->pDocTopics)[ --n ]->pSh == pSh )
480 : {
481 : // If the document is untitled, is still a new Topic is created!
482 : if( !bFnd )
483 : {
484 : bFnd = sal_True;
485 : sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
486 : }
487 : OUString sNm( (*pAppData_Impl->pDocTopics)[ n ]->GetName() );
488 : if( sShellNm == sNm.toAsciiLowerCase() )
489 : return ;
490 : }
491 : }
492 :
493 : SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
494 : pAppData_Impl->pDocTopics->push_back(pTopic);
495 : pAppData_Impl->pDdeService->AddTopic( *pTopic );
496 : }
497 : #endif
498 :
499 0 : void SfxApplication::RemoveDdeTopic( SfxObjectShell* pSh )
500 : {
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;
506 :
507 : for (size_t n = pAppData_Impl->pDocTopics->size(); n; )
508 : {
509 : SfxDdeDocTopic_Impl *const pTopic = (*pAppData_Impl->pDocTopics)[ --n ];
510 : if (pTopic->pSh == pSh)
511 : {
512 : pAppData_Impl->pDdeService->RemoveTopic( *pTopic );
513 : delete pTopic;
514 : pAppData_Impl->pDocTopics->erase( pAppData_Impl->pDocTopics->begin() + n );
515 : }
516 : }
517 : #else
518 : (void) pSh;
519 : #endif
520 0 : }
521 :
522 0 : const DdeService* SfxApplication::GetDdeService() const
523 : {
524 0 : return pAppData_Impl->pDdeService;
525 : }
526 :
527 6170 : DdeService* SfxApplication::GetDdeService()
528 : {
529 6170 : return pAppData_Impl->pDdeService;
530 : }
531 :
532 : #if defined WNT
533 :
534 : DdeData* SfxDdeDocTopic_Impl::Get(SotClipboardFormatId nFormat)
535 : {
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 ) )
540 : {
541 : aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
542 : return &aData;
543 : }
544 : aSeq.realloc( 0 );
545 : return 0;
546 : }
547 :
548 : bool SfxDdeDocTopic_Impl::Put( const DdeData* pData )
549 : {
550 : aSeq = ::com::sun::star::uno::Sequence< sal_Int8 >(
551 : (sal_Int8*)(const void*)*pData, (long)*pData );
552 : bool bRet;
553 : if( aSeq.getLength() )
554 : {
555 : ::com::sun::star::uno::Any aValue;
556 : aValue <<= aSeq;
557 : OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
558 : bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
559 : }
560 : else
561 : bRet = false;
562 : return bRet;
563 : }
564 :
565 : bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
566 : {
567 : long nRet = pStr ? pSh->DdeExecute( *pStr ) : 0;
568 : return 0 != nRet;
569 : }
570 :
571 : bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
572 : {
573 : AddItem( DdeItem( rItem ) );
574 : return true;
575 : }
576 :
577 : bool SfxDdeDocTopic_Impl::StartAdviseLoop()
578 : {
579 : bool bRet = false;
580 : ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() );
581 : if( pNewObj )
582 : {
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;
588 : }
589 : return bRet;
590 : }
591 :
592 : #endif
593 :
594 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|