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 <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>
29 :
30 : #include <tools/urlobj.hxx>
31 : #include <tools/diagnose_ex.h>
32 : #include <unotools/pathoptions.hxx>
33 :
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>
46 :
47 : //========================================================================
48 :
49 0 : OUString SfxDdeServiceName_Impl( const OUString& sIn )
50 : {
51 0 : OUStringBuffer sReturn(sIn.getLength());
52 :
53 0 : for ( sal_uInt16 n = sIn.getLength(); n; --n )
54 : {
55 0 : sal_Unicode cChar = sIn[n-1];
56 0 : if (comphelper::string::isalnumAscii(cChar))
57 0 : sReturn.append(cChar);
58 : }
59 :
60 0 : return sReturn.makeStringAndClear();
61 : }
62 :
63 : #if defined( WNT )
64 : class ImplDdeService : public DdeService
65 : {
66 : public:
67 : ImplDdeService( const OUString& rNm )
68 : : DdeService( rNm )
69 : {}
70 : virtual bool MakeTopic( const OUString& );
71 :
72 : virtual OUString Topics();
73 :
74 : virtual bool SysTopicExecute( const OUString* pStr );
75 : };
76 :
77 : //--------------------------------------------------------------------
78 : namespace
79 : {
80 : sal_Bool lcl_IsDocument( const String& rContent )
81 : {
82 : using namespace com::sun::star;
83 :
84 : sal_Bool bRet = sal_False;
85 : INetURLObject aObj( rContent );
86 : DBG_ASSERT( aObj.GetProtocol() != INET_PROT_NOT_VALID, "Invalid URL!" );
87 :
88 : try
89 : {
90 : ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::NO_DECODE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
91 : bRet = aCnt.isDocument();
92 : }
93 : catch( const ucb::CommandAbortedException& )
94 : {
95 : DBG_WARNING( "CommandAbortedException" );
96 : }
97 : catch( const ucb::IllegalIdentifierException& )
98 : {
99 : DBG_WARNING( "IllegalIdentifierException" );
100 : }
101 : catch( const ucb::ContentCreationException& )
102 : {
103 : DBG_WARNING( "IllegalIdentifierException" );
104 : }
105 : catch( const uno::Exception& )
106 : {
107 : SAL_WARN( "sfx2.appl", "Any other exception" );
108 : }
109 :
110 : return bRet;
111 : }
112 : }
113 :
114 : bool ImplDdeService::MakeTopic( const OUString& rNm )
115 : {
116 : // Workaround for Event after Main() under OS/2
117 : // happens when exiting starts the App again
118 : if ( !Application::IsInExecute() )
119 : return false;
120 :
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 : String sNm( rNm );
126 : sNm.ToLowerAscii();
127 : TypeId aType( TYPE(SfxObjectShell) );
128 : SfxObjectShell* pShell = SfxObjectShell::GetFirst( &aType );
129 : while( pShell )
130 : {
131 : String sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
132 : sTmp.ToLowerAscii();
133 : if( sTmp == sNm )
134 : {
135 : SFX_APP()->AddDdeTopic( pShell );
136 : bRet = true;
137 : break;
138 : }
139 : pShell = SfxObjectShell::GetNext( *pShell, &aType );
140 : }
141 :
142 : if( !bRet )
143 : {
144 : INetURLObject aWorkPath( SvtPathOptions().GetWorkPath() );
145 : INetURLObject aFile;
146 : if ( aWorkPath.GetNewAbsURL( rNm, &aFile ) &&
147 : lcl_IsDocument( aFile.GetMainURL( INetURLObject::NO_DECODE ) ) )
148 : {
149 : // File exists? then try to load it:
150 : SfxStringItem aName( SID_FILE_NAME, aFile.GetMainURL( INetURLObject::NO_DECODE ) );
151 : SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, sal_True);
152 :
153 : SfxBoolItem aSilent(SID_SILENT, sal_True);
154 : SfxDispatcher* pDispatcher = SFX_APP()->GetDispatcher_Impl();
155 : const SfxPoolItem* pRet = pDispatcher->Execute( SID_OPENDOC,
156 : SFX_CALLMODE_SYNCHRON,
157 : &aName, &aNewView,
158 : &aSilent, 0L );
159 :
160 : if( pRet && pRet->ISA( SfxViewFrameItem ) &&
161 : ((SfxViewFrameItem*)pRet)->GetFrame() &&
162 : 0 != ( pShell = ((SfxViewFrameItem*)pRet)
163 : ->GetFrame()->GetObjectShell() ) )
164 : {
165 : SFX_APP()->AddDdeTopic( pShell );
166 : bRet = true;
167 : }
168 : }
169 : }
170 : return bRet;
171 : }
172 :
173 : OUString ImplDdeService::Topics()
174 : {
175 : OUString sRet;
176 : if( GetSysTopic() )
177 : sRet += GetSysTopic()->GetName();
178 :
179 : TypeId aType( TYPE(SfxObjectShell) );
180 : SfxObjectShell* pShell = SfxObjectShell::GetFirst( &aType );
181 : while( pShell )
182 : {
183 : if( SfxViewFrame::GetFirst( pShell ) )
184 : {
185 : if( !sRet.isEmpty() )
186 : sRet += "\t";
187 : sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
188 : }
189 : pShell = SfxObjectShell::GetNext( *pShell, &aType );
190 : }
191 : if( !sRet.isEmpty() )
192 : sRet += "\r\n";
193 : return sRet;
194 : }
195 :
196 : bool ImplDdeService::SysTopicExecute( const OUString* pStr )
197 : {
198 : return SFX_APP()->DdeExecute( *pStr );
199 : }
200 : #endif
201 :
202 0 : class SfxDdeTriggerTopic_Impl : public DdeTopic
203 : {
204 : public:
205 : SfxDdeTriggerTopic_Impl()
206 : : DdeTopic( "TRIGGER" )
207 : {}
208 :
209 : virtual bool Execute( const OUString* );
210 : };
211 :
212 0 : class SfxDdeDocTopic_Impl : public DdeTopic
213 : {
214 : public:
215 : SfxObjectShell* pSh;
216 : DdeData aData;
217 : ::com::sun::star::uno::Sequence< sal_Int8 > aSeq;
218 :
219 : SfxDdeDocTopic_Impl( SfxObjectShell* pShell )
220 : : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell )
221 : {}
222 :
223 : virtual DdeData* Get( sal_uIntPtr );
224 : virtual bool Put( const DdeData* );
225 : virtual bool Execute( const OUString* );
226 : virtual bool StartAdviseLoop();
227 : virtual bool MakeItem( const OUString& rItem );
228 : };
229 :
230 :
231 0 : class SfxDdeDocTopics_Impl : public std::vector<SfxDdeDocTopic_Impl*> {};
232 :
233 : /* [Description]
234 :
235 : Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
236 : this data into a <ApplicationEvent>, which can be excecuted through
237 : <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
238 : TRUE is returned, otherwise FALSE.
239 :
240 : [Example]
241 :
242 : rCmd = "Open(\"d:\doc\doc.sdw\")"
243 : rEvent = "Open"
244 : */
245 0 : sal_Bool SfxAppEvent_Impl( ApplicationEvent &rAppEvent,
246 : const OUString& rCmd, const OUString& rEvent,
247 : ApplicationEvent::Type eType )
248 : {
249 0 : OUString sEvent(rEvent);
250 0 : sEvent += "(";
251 0 : if (rCmd.startsWithIgnoreAsciiCase(sEvent))
252 : {
253 0 : OUStringBuffer aData( rCmd );
254 0 : aData.remove(0, sEvent.getLength());
255 0 : if ( aData.getLength() > 2 )
256 : {
257 : // Transform into the ApplicationEvent Format
258 0 : aData.remove( aData.getLength() - 1, 1 );
259 0 : for ( sal_Int32 n = 0; n < aData.getLength(); )
260 : {
261 0 : switch ( aData[n] )
262 : {
263 : case '"':
264 0 : aData.remove( n, 1 );
265 0 : while ( n < aData.getLength() && aData[n] != '"' )
266 0 : ++n;
267 0 : if ( n < aData.getLength() )
268 0 : aData.remove( n, 1 );
269 0 : break;
270 : case ' ':
271 0 : aData[n++] = '\n';
272 0 : break;
273 : default:
274 0 : ++n;
275 0 : break;
276 : }
277 : }
278 :
279 0 : rAppEvent = ApplicationEvent(eType, aData.makeStringAndClear());
280 0 : return sal_True;
281 0 : }
282 : }
283 :
284 0 : return sal_False;
285 : }
286 :
287 : #if defined( WNT )
288 : /* Description]
289 :
290 : This method can be overloaded by application developers, to receive
291 : DDE-commands directed to their SfxApplication subclass.
292 :
293 : The base implementation understands the API functionality of the
294 : relevant SfxApplication subclass in BASIC syntax. Return values can
295 : not be transferred, unfortunately.
296 : */
297 : long SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
298 : {
299 : // Print or Open-Event?
300 : ApplicationEvent aAppEvent;
301 : if ( SfxAppEvent_Impl( aAppEvent, rCmd, "Print", ApplicationEvent::TYPE_PRINT ) ||
302 : SfxAppEvent_Impl( aAppEvent, rCmd, "Open", ApplicationEvent::TYPE_OPEN ) )
303 : GetpApp()->AppEvent( aAppEvent );
304 : else
305 : {
306 : // all others are BASIC
307 : StarBASIC* pBasic = GetBasic();
308 : DBG_ASSERT( pBasic, "Where is the Basic???" );
309 : SbxVariable* pRet = pBasic->Execute( rCmd );
310 : if( !pRet )
311 : {
312 : SbxBase::ResetError();
313 : return 0;
314 : }
315 : }
316 : return 1;
317 : }
318 : #endif
319 :
320 : /* [Description]
321 :
322 : This method can be overloaded by application developers, to receive
323 : DDE-commands directed to the their SfxApplication subclass.
324 :
325 : The base implementation does nothing and returns 0.
326 : */
327 0 : long SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
328 : {
329 : #ifdef DISABLE_SCRIPTING
330 : (void) rCmd;
331 : #else
332 0 : StarBASIC* pBasic = GetBasic();
333 : DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
334 0 : SbxVariable* pRet = pBasic->Execute( rCmd );
335 0 : if( !pRet )
336 : {
337 0 : SbxBase::ResetError();
338 0 : return 0;
339 : }
340 : #endif
341 0 : return 1;
342 : }
343 :
344 : //--------------------------------------------------------------------
345 :
346 : /* [Description]
347 :
348 : This method can be overloaded by application developers, to receive
349 : DDE-data-requests directed to their SfxApplication subclass.
350 :
351 : The base implementation provides no data and returns 0.
352 : */
353 0 : long SfxObjectShell::DdeGetData( const OUString&, // the Item to be addressed
354 : const OUString&, // in: Format
355 : ::com::sun::star::uno::Any& )// out: requested data
356 : {
357 0 : return 0;
358 : }
359 :
360 : //--------------------------------------------------------------------
361 :
362 : /* [Description]
363 :
364 : This method can be overloaded 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 0.
368 : */
369 0 : long SfxObjectShell::DdeSetData( const OUString&, // the Item to be addressed
370 : const OUString&, // in: Format
371 : const ::com::sun::star::uno::Any& )// out: requested data
372 : {
373 0 : return 0;
374 : }
375 :
376 : /* [Description]
377 :
378 : This method can be overloaded by application developers, to establish
379 : a DDE-hotlink to their SfxApplication subclass.
380 :
381 : The base implementation is not generate a link and returns 0.
382 : */
383 0 : ::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed
384 : {
385 0 : return 0;
386 : }
387 :
388 168 : void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
389 : {
390 168 : }
391 :
392 618 : void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
393 : {
394 618 : TypeId aType = TYPE(SfxObjectShell);
395 618 : SfxObjectShell* p = GetFirst(&aType, false);
396 2297 : while (p)
397 : {
398 1061 : if (&rServer != p)
399 443 : p->ReconnectDdeLink(rServer);
400 :
401 1061 : p = GetNext(*p, &aType, false);
402 : }
403 618 : }
404 :
405 : /* [Description]
406 :
407 : This method can be overloaded by application developers, to receive
408 : DDE-commands directed to the their SfxApplication subclass.
409 :
410 : The base implementation understands the API functionality of the
411 : relevant SfxViewFrame, which is shown and the relevant SfxViewShell
412 : and the relevant SfxApplication subclass in BASIC syntax. Return
413 : values can not be transferred, unfortunately.
414 : */
415 0 : long SfxViewFrame::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
416 : {
417 0 : if ( GetObjectShell() )
418 0 : return GetObjectShell()->DdeExecute( rCmd );
419 :
420 0 : return 0;
421 : }
422 :
423 : /* [Description]
424 :
425 : This method can be overloaded by application developers, to receive
426 : DDE-data-requests directed to their SfxApplication subclass.
427 :
428 : The base implementation provides no data and returns 0.
429 : */
430 0 : long SfxViewFrame::DdeGetData( const OUString&, // the Item to be addressed
431 : const OUString&, // in: Format
432 : ::com::sun::star::uno::Any& )// out: requested data
433 : {
434 0 : return 0;
435 : }
436 :
437 : /* [Description]
438 :
439 : This method can be overloaded by application developers, to receive
440 : DDE-data directed to their SfxApplication subclass.
441 :
442 : The base implementation is not receiving any data and returns 0.
443 : */
444 0 : long SfxViewFrame::DdeSetData( const OUString&, // the Item to be addressed
445 : const OUString&, // in: Format
446 : const ::com::sun::star::uno::Any& )// out: requested data
447 : {
448 0 : return 0;
449 : }
450 :
451 : /* [Description]
452 :
453 : This method can be overloaded by application developers, to establish
454 : a DDE-hotlink to their SfxApplication subclass.
455 :
456 : The base implementation is not generate a link and returns 0.
457 : */
458 0 : ::sfx2::SvLinkSource* SfxViewFrame::DdeCreateLinkSource( const OUString& )// the Item to be addressed
459 : {
460 0 : return 0;
461 : }
462 :
463 133 : sal_Bool SfxApplication::InitializeDde()
464 : {
465 133 : int nError = 0;
466 : #if defined( WNT )
467 : DBG_ASSERT( !pAppData_Impl->pDdeService,
468 : "Dde can not be initialized multiple times" );
469 :
470 : pAppData_Impl->pDdeService = new ImplDdeService( Application::GetAppName() );
471 : nError = pAppData_Impl->pDdeService->GetError();
472 : if( !nError )
473 : {
474 : pAppData_Impl->pDocTopics = new SfxDdeDocTopics_Impl;
475 :
476 : // we certainly want to support RTF!
477 : pAppData_Impl->pDdeService->AddFormat( FORMAT_RTF );
478 :
479 : // Config path as a topic becauseof multiple starts
480 : INetURLObject aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
481 : aOfficeLockFile.insertName( "soffice.lck" );
482 : String aService( SfxDdeServiceName_Impl(
483 : aOfficeLockFile.GetMainURL(INetURLObject::DECODE_TO_IURI) ) );
484 : aService.ToUpperAscii();
485 : pAppData_Impl->pDdeService2 = new ImplDdeService( aService );
486 : pAppData_Impl->pTriggerTopic = new SfxDdeTriggerTopic_Impl;
487 : pAppData_Impl->pDdeService2->AddTopic( *pAppData_Impl->pTriggerTopic );
488 : }
489 : #endif
490 133 : return !nError;
491 : }
492 :
493 83 : void SfxAppData_Impl::DeInitDDE()
494 : {
495 83 : DELETEZ( pTriggerTopic );
496 83 : DELETEZ( pDdeService2 );
497 83 : DELETEZ( pDocTopics );
498 83 : DELETEZ( pDdeService );
499 83 : }
500 :
501 : #if defined( WNT )
502 : void SfxApplication::AddDdeTopic( SfxObjectShell* pSh )
503 : {
504 : DBG_ASSERT( pAppData_Impl->pDocTopics, "There is no Dde-Service" );
505 : //OV: DDE is disconnected in server mode!
506 : if( !pAppData_Impl->pDocTopics )
507 : return;
508 :
509 : // prevent double submit
510 : String sShellNm;
511 : sal_Bool bFnd = sal_False;
512 : for (size_t n = pAppData_Impl->pDocTopics->size(); n;)
513 : {
514 : if( (*pAppData_Impl->pDocTopics)[ --n ]->pSh == pSh )
515 : {
516 : // If the document is untitled, is still a new Topic is created!
517 : if( !bFnd )
518 : {
519 : bFnd = sal_True;
520 : (sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME)).ToLowerAscii();
521 : }
522 : String sNm( (*pAppData_Impl->pDocTopics)[ n ]->GetName() );
523 : if( sShellNm == sNm.ToLowerAscii() )
524 : return ;
525 : }
526 : }
527 :
528 : SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
529 : pAppData_Impl->pDocTopics->push_back(pTopic);
530 : pAppData_Impl->pDdeService->AddTopic( *pTopic );
531 : }
532 : #endif
533 :
534 0 : void SfxApplication::RemoveDdeTopic( SfxObjectShell* pSh )
535 : {
536 : DBG_ASSERT( pAppData_Impl->pDocTopics, "There is no Dde-Service" );
537 : //OV: DDE is disconnected in server mode!
538 0 : if( !pAppData_Impl->pDocTopics )
539 0 : return;
540 :
541 0 : for (size_t n = pAppData_Impl->pDocTopics->size(); n; )
542 : {
543 0 : SfxDdeDocTopic_Impl *const pTopic = (*pAppData_Impl->pDocTopics)[ --n ];
544 0 : if (pTopic->pSh == pSh)
545 : {
546 0 : pAppData_Impl->pDdeService->RemoveTopic( *pTopic );
547 0 : delete pTopic;
548 0 : pAppData_Impl->pDocTopics->erase( pAppData_Impl->pDocTopics->begin() + n );
549 : }
550 : }
551 : }
552 :
553 0 : const DdeService* SfxApplication::GetDdeService() const
554 : {
555 0 : return pAppData_Impl->pDdeService;
556 : }
557 :
558 2074 : DdeService* SfxApplication::GetDdeService()
559 : {
560 2074 : return pAppData_Impl->pDdeService;
561 : }
562 :
563 : //--------------------------------------------------------------------
564 :
565 0 : bool SfxDdeTriggerTopic_Impl::Execute( const OUString* )
566 : {
567 0 : return true;
568 : }
569 :
570 : //--------------------------------------------------------------------
571 0 : DdeData* SfxDdeDocTopic_Impl::Get( sal_uIntPtr nFormat )
572 : {
573 0 : String sMimeType( SotExchange::GetFormatMimeType( nFormat ));
574 0 : ::com::sun::star::uno::Any aValue;
575 0 : long nRet = pSh->DdeGetData( GetCurItem(), sMimeType, aValue );
576 0 : if( nRet && aValue.hasValue() && ( aValue >>= aSeq ) )
577 : {
578 0 : aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
579 0 : return &aData;
580 : }
581 0 : aSeq.realloc( 0 );
582 0 : return 0;
583 : }
584 :
585 0 : bool SfxDdeDocTopic_Impl::Put( const DdeData* pData )
586 : {
587 0 : aSeq = ::com::sun::star::uno::Sequence< sal_Int8 >(
588 0 : (sal_Int8*)(const void*)*pData, (long)*pData );
589 : bool bRet;
590 0 : if( aSeq.getLength() )
591 : {
592 0 : ::com::sun::star::uno::Any aValue;
593 0 : aValue <<= aSeq;
594 0 : String sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
595 0 : bRet = 0 != pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
596 : }
597 : else
598 0 : bRet = false;
599 0 : return bRet;
600 : }
601 :
602 0 : bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
603 : {
604 0 : long nRet = pStr ? pSh->DdeExecute( *pStr ) : 0;
605 0 : return 0 != nRet;
606 : }
607 :
608 0 : bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
609 : {
610 0 : AddItem( DdeItem( rItem ) );
611 0 : return true;
612 : }
613 :
614 0 : bool SfxDdeDocTopic_Impl::StartAdviseLoop()
615 : {
616 0 : bool bRet = false;
617 0 : ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() );
618 0 : if( pNewObj )
619 : {
620 : // then we also establish a corresponding SvBaseLink
621 0 : String sNm, sTmp( Application::GetAppName() );
622 0 : ::sfx2::MakeLnkName( sNm, &sTmp, pSh->GetTitle(SFX_TITLE_FULLNAME), GetCurItem() );
623 0 : new ::sfx2::SvBaseLink( sNm, OBJECT_DDE_EXTERN, pNewObj );
624 0 : bRet = true;
625 : }
626 0 : return bRet;
627 408 : }
628 :
629 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|