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 :
21 : #include <sfx2/lnkbase.hxx>
22 : #include <sot/exchange.hxx>
23 : #include <com/sun/star/uno/Any.hxx>
24 : #include <com/sun/star/uno/Sequence.hxx>
25 : #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
26 : #include <vcl/msgbox.hxx>
27 : #include <sfx2/linkmgr.hxx>
28 : #include <vcl/svapp.hxx>
29 : #include "app.hrc"
30 : #include <sfx2/sfxresid.hxx>
31 : #include <sfx2/filedlghelper.hxx>
32 : #include <tools/debug.hxx>
33 : #include <svl/svdde.hxx>
34 :
35 : using namespace ::com::sun::star;
36 : using namespace ::com::sun::star::uno;
37 :
38 : namespace sfx2
39 : {
40 :
41 0 : TYPEINIT0( SvBaseLink )
42 :
43 : static DdeTopic* FindTopic( const OUString &, sal_uInt16* = 0 );
44 :
45 : class ImplDdeItem;
46 :
47 : struct BaseLink_Impl
48 : {
49 : Link m_aEndEditLink;
50 : LinkManager* m_pLinkMgr;
51 : Window* m_pParentWin;
52 : FileDialogHelper* m_pFileDlg;
53 : bool m_bIsConnect;
54 :
55 0 : BaseLink_Impl() :
56 : m_pLinkMgr( NULL )
57 : , m_pParentWin( NULL )
58 : , m_pFileDlg( NULL )
59 0 : , m_bIsConnect( false )
60 0 : {}
61 :
62 0 : ~BaseLink_Impl()
63 0 : { delete m_pFileDlg; }
64 : };
65 :
66 : // only for internal management
67 : struct ImplBaseLinkData
68 : {
69 : struct tClientType
70 : {
71 : // applies for all links
72 : sal_uIntPtr nCntntType; // Update Format
73 : // Not Ole-Links
74 : bool bIntrnlLnk; // It is an internal link
75 : sal_uInt16 nUpdateMode; // UpdateMode
76 : };
77 :
78 : struct tDDEType
79 : {
80 : ImplDdeItem* pItem;
81 : };
82 :
83 : union {
84 : tClientType ClientType;
85 : tDDEType DDEType;
86 : };
87 0 : ImplBaseLinkData()
88 : {
89 0 : ClientType.nCntntType = 0;
90 0 : ClientType.bIntrnlLnk = false;
91 0 : ClientType.nUpdateMode = 0;
92 0 : DDEType.pItem = NULL;
93 0 : }
94 : };
95 :
96 :
97 : class ImplDdeItem : public DdeGetPutItem
98 : {
99 : SvBaseLink* pLink;
100 : DdeData aData;
101 : Sequence< sal_Int8 > aSeq; // Datacontainer for DdeData !!!
102 : bool bIsValidData : 1;
103 : bool bIsInDTOR : 1;
104 : public:
105 0 : ImplDdeItem( SvBaseLink& rLink, const OUString& rStr )
106 : : DdeGetPutItem( rStr ), pLink( &rLink ), bIsValidData( false ),
107 0 : bIsInDTOR( false )
108 0 : {}
109 : virtual ~ImplDdeItem();
110 :
111 : virtual DdeData* Get( sal_uIntPtr ) SAL_OVERRIDE;
112 : virtual bool Put( const DdeData* ) SAL_OVERRIDE;
113 : virtual void AdviseLoop( bool ) SAL_OVERRIDE;
114 :
115 0 : void Notify()
116 : {
117 0 : bIsValidData = false;
118 0 : DdeGetPutItem::NotifyClient();
119 0 : }
120 :
121 0 : bool IsInDTOR() const { return bIsInDTOR; }
122 : };
123 :
124 :
125 :
126 0 : SvBaseLink::SvBaseLink()
127 0 : : m_bIsReadOnly(false)
128 : {
129 0 : pImpl = new BaseLink_Impl();
130 0 : nObjType = OBJECT_CLIENT_SO;
131 0 : pImplData = new ImplBaseLinkData;
132 0 : bVisible = bSynchron = bUseCache = true;
133 0 : bWasLastEditOK = false;
134 0 : }
135 :
136 :
137 :
138 0 : SvBaseLink::SvBaseLink( sal_uInt16 nUpdateMode, sal_uIntPtr nContentType )
139 0 : : m_bIsReadOnly(false)
140 : {
141 0 : pImpl = new BaseLink_Impl();
142 0 : nObjType = OBJECT_CLIENT_SO;
143 0 : pImplData = new ImplBaseLinkData;
144 0 : bVisible = bSynchron = bUseCache = true;
145 0 : bWasLastEditOK = false;
146 :
147 : // It it going to be a Ole-Link,
148 0 : pImplData->ClientType.nUpdateMode = nUpdateMode;
149 0 : pImplData->ClientType.nCntntType = nContentType;
150 0 : pImplData->ClientType.bIntrnlLnk = false;
151 0 : }
152 :
153 :
154 :
155 0 : SvBaseLink::SvBaseLink( const OUString& rLinkName, sal_uInt16 nObjectType, SvLinkSource* pObj )
156 : : pImpl(0)
157 0 : , m_bIsReadOnly(false)
158 : {
159 0 : bVisible = bSynchron = bUseCache = true;
160 0 : bWasLastEditOK = false;
161 0 : aLinkName = rLinkName;
162 0 : pImplData = new ImplBaseLinkData;
163 0 : nObjType = nObjectType;
164 :
165 0 : if( !pObj )
166 : {
167 : DBG_ASSERT( pObj, "Where is my left-most object" );
168 0 : return;
169 : }
170 :
171 0 : if( OBJECT_DDE_EXTERN == nObjType )
172 : {
173 0 : sal_uInt16 nItemStt = 0;
174 0 : DdeTopic* pTopic = FindTopic( aLinkName, &nItemStt );
175 0 : if( pTopic )
176 : {
177 : // then we have it all together
178 : // MM_TODO how do I get the name
179 0 : OUString aStr = aLinkName; // xLinkName->GetDisplayName();
180 0 : aStr = aStr.copy( nItemStt );
181 0 : pImplData->DDEType.pItem = new ImplDdeItem( *this, aStr );
182 0 : pTopic->InsertItem( pImplData->DDEType.pItem );
183 :
184 : // store the Advice
185 0 : xObj = pObj;
186 : }
187 : }
188 0 : else if( pObj->Connect( this ) )
189 0 : xObj = pObj;
190 : }
191 :
192 :
193 :
194 0 : SvBaseLink::~SvBaseLink()
195 : {
196 0 : Disconnect();
197 :
198 0 : switch( nObjType )
199 : {
200 : case OBJECT_DDE_EXTERN:
201 0 : if( !pImplData->DDEType.pItem->IsInDTOR() )
202 0 : delete pImplData->DDEType.pItem;
203 0 : break;
204 : }
205 :
206 0 : delete pImplData;
207 0 : delete pImpl;
208 0 : }
209 :
210 0 : IMPL_LINK( SvBaseLink, EndEditHdl, OUString*, _pNewName )
211 : {
212 0 : OUString sNewName;
213 0 : if ( _pNewName )
214 0 : sNewName = *_pNewName;
215 0 : if ( !ExecuteEdit( sNewName ) )
216 0 : sNewName = "";
217 0 : bWasLastEditOK = !sNewName.isEmpty();
218 0 : if ( pImpl->m_aEndEditLink.IsSet() )
219 0 : pImpl->m_aEndEditLink.Call( this );
220 0 : return 0;
221 : }
222 :
223 :
224 :
225 0 : void SvBaseLink::SetObjType( sal_uInt16 nObjTypeP )
226 : {
227 : DBG_ASSERT( nObjType != OBJECT_CLIENT_DDE, "type already set" );
228 : DBG_ASSERT( !xObj.Is(), "object exist" );
229 :
230 0 : nObjType = nObjTypeP;
231 0 : }
232 :
233 :
234 :
235 0 : void SvBaseLink::SetName( const OUString & rNm )
236 : {
237 0 : aLinkName = rNm;
238 0 : }
239 :
240 :
241 :
242 0 : void SvBaseLink::SetObj( SvLinkSource * pObj )
243 : {
244 : DBG_ASSERT( (nObjType & OBJECT_CLIENT_SO &&
245 : pImplData->ClientType.bIntrnlLnk) ||
246 : nObjType == OBJECT_CLIENT_GRF,
247 : "no intern link" );
248 0 : xObj = pObj;
249 0 : }
250 :
251 :
252 :
253 0 : void SvBaseLink::SetLinkSourceName( const OUString & rLnkNm )
254 : {
255 0 : if( aLinkName == rLnkNm )
256 0 : return;
257 :
258 0 : AddNextRef(); // should be superfluous
259 : // remove old connection
260 0 : Disconnect();
261 :
262 0 : aLinkName = rLnkNm;
263 :
264 : // New Connection
265 0 : _GetRealObject();
266 0 : ReleaseRef(); // should be superfluous
267 : }
268 :
269 :
270 :
271 0 : OUString SvBaseLink::GetLinkSourceName() const
272 : {
273 0 : return aLinkName;
274 : }
275 :
276 :
277 :
278 0 : void SvBaseLink::SetUpdateMode( sal_uInt16 nMode )
279 : {
280 0 : if( ( OBJECT_CLIENT_SO & nObjType ) &&
281 0 : pImplData->ClientType.nUpdateMode != nMode )
282 : {
283 0 : AddNextRef();
284 0 : Disconnect();
285 :
286 0 : pImplData->ClientType.nUpdateMode = nMode;
287 0 : _GetRealObject();
288 0 : ReleaseRef();
289 : }
290 0 : }
291 :
292 : // #i88291#
293 0 : void SvBaseLink::clearStreamToLoadFrom()
294 : {
295 0 : m_xInputStreamToLoadFrom.clear();
296 0 : if( xObj.Is() )
297 : {
298 0 : xObj->clearStreamToLoadFrom();
299 : }
300 0 : }
301 :
302 0 : bool SvBaseLink::Update()
303 : {
304 0 : if( OBJECT_CLIENT_SO & nObjType )
305 : {
306 0 : AddNextRef();
307 0 : Disconnect();
308 :
309 0 : _GetRealObject();
310 0 : ReleaseRef();
311 0 : if( xObj.Is() )
312 : {
313 0 : xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly);
314 : OUString sMimeType( SotExchange::GetFormatMimeType(
315 0 : pImplData->ClientType.nCntntType ));
316 0 : Any aData;
317 :
318 0 : if( xObj->GetData( aData, sMimeType ) )
319 : {
320 0 : UpdateResult eRes = DataChanged(sMimeType, aData);
321 0 : bool bSuccess = eRes == SUCCESS;
322 : //for manual Updates there is no need to hold the ServerObject
323 0 : if( OBJECT_CLIENT_DDE == nObjType &&
324 0 : LINKUPDATE_ONCALL == GetUpdateMode() && xObj.Is() )
325 0 : xObj->RemoveAllDataAdvise( this );
326 0 : return bSuccess;
327 : }
328 0 : if( xObj.Is() )
329 : {
330 : // should be asynschron?
331 0 : if( xObj->IsPending() )
332 0 : return true;
333 :
334 : // we do not need the object anymore
335 0 : AddNextRef();
336 0 : Disconnect();
337 0 : ReleaseRef();
338 0 : }
339 : }
340 : }
341 0 : return false;
342 : }
343 :
344 :
345 0 : sal_uInt16 SvBaseLink::GetUpdateMode() const
346 : {
347 0 : return ( OBJECT_CLIENT_SO & nObjType )
348 : ? pImplData->ClientType.nUpdateMode
349 0 : : sal::static_int_cast< sal_uInt16 >( LINKUPDATE_ONCALL );
350 : }
351 :
352 :
353 0 : void SvBaseLink::_GetRealObject( bool bConnect)
354 : {
355 0 : if( !pImpl->m_pLinkMgr )
356 0 : return;
357 :
358 : DBG_ASSERT( !xObj.Is(), "object already exist" );
359 :
360 0 : if( OBJECT_CLIENT_DDE == nObjType )
361 : {
362 0 : OUString sServer;
363 0 : if( pImpl->m_pLinkMgr->GetDisplayNames( this, &sServer ) &&
364 0 : sServer == GetpApp()->GetAppName() ) // internal Link !!!
365 : {
366 : // so that the Internal link can be created!
367 0 : nObjType = OBJECT_INTERN;
368 0 : xObj = pImpl->m_pLinkMgr->CreateObj( this );
369 :
370 0 : pImplData->ClientType.bIntrnlLnk = true;
371 0 : nObjType = OBJECT_CLIENT_DDE; // so we know what it once was!
372 : }
373 : else
374 : {
375 0 : pImplData->ClientType.bIntrnlLnk = false;
376 0 : xObj = pImpl->m_pLinkMgr->CreateObj( this );
377 0 : }
378 : }
379 0 : else if( (OBJECT_CLIENT_SO & nObjType) )
380 0 : xObj = pImpl->m_pLinkMgr->CreateObj( this );
381 :
382 0 : if( bConnect && ( !xObj.Is() || !xObj->Connect( this ) ) )
383 0 : Disconnect();
384 : }
385 :
386 0 : sal_uIntPtr SvBaseLink::GetContentType() const
387 : {
388 0 : if( OBJECT_CLIENT_SO & nObjType )
389 0 : return pImplData->ClientType.nCntntType;
390 :
391 0 : return 0; // all Formats ?
392 : }
393 :
394 :
395 0 : bool SvBaseLink::SetContentType( sal_uIntPtr nType )
396 : {
397 0 : if( OBJECT_CLIENT_SO & nObjType )
398 : {
399 0 : pImplData->ClientType.nCntntType = nType;
400 0 : return true;
401 : }
402 0 : return false;
403 : }
404 :
405 0 : LinkManager* SvBaseLink::GetLinkManager()
406 : {
407 0 : return pImpl->m_pLinkMgr;
408 : }
409 :
410 0 : const LinkManager* SvBaseLink::GetLinkManager() const
411 : {
412 0 : return pImpl->m_pLinkMgr;
413 : }
414 :
415 0 : void SvBaseLink::SetLinkManager( LinkManager* _pMgr )
416 : {
417 0 : pImpl->m_pLinkMgr = _pMgr;
418 0 : }
419 :
420 0 : void SvBaseLink::Disconnect()
421 : {
422 0 : if( xObj.Is() )
423 : {
424 0 : xObj->RemoveAllDataAdvise( this );
425 0 : xObj->RemoveConnectAdvise( this );
426 0 : xObj.Clear();
427 : }
428 0 : }
429 :
430 0 : SvBaseLink::UpdateResult SvBaseLink::DataChanged( const OUString &, const ::com::sun::star::uno::Any & )
431 : {
432 0 : switch( nObjType )
433 : {
434 : case OBJECT_DDE_EXTERN:
435 0 : if( pImplData->DDEType.pItem )
436 0 : pImplData->DDEType.pItem->Notify();
437 0 : break;
438 : }
439 0 : return SUCCESS;
440 : }
441 :
442 0 : void SvBaseLink::Edit( Window* pParent, const Link& rEndEditHdl )
443 : {
444 0 : pImpl->m_pParentWin = pParent;
445 0 : pImpl->m_aEndEditLink = rEndEditHdl;
446 0 : pImpl->m_bIsConnect = xObj.Is();
447 0 : if( !pImpl->m_bIsConnect )
448 0 : _GetRealObject( xObj.Is() );
449 :
450 0 : bool bAsync = false;
451 0 : Link aLink = LINK( this, SvBaseLink, EndEditHdl );
452 :
453 0 : if( OBJECT_CLIENT_SO & nObjType && pImplData->ClientType.bIntrnlLnk )
454 : {
455 0 : if( pImpl->m_pLinkMgr )
456 : {
457 0 : SvLinkSourceRef ref = pImpl->m_pLinkMgr->CreateObj( this );
458 0 : if( ref.Is() )
459 : {
460 0 : ref->Edit( pParent, this, aLink );
461 0 : bAsync = true;
462 0 : }
463 0 : }
464 : }
465 : else
466 : {
467 0 : xObj->Edit( pParent, this, aLink );
468 0 : bAsync = true;
469 : }
470 :
471 0 : if ( !bAsync )
472 : {
473 0 : ExecuteEdit( OUString() );
474 0 : bWasLastEditOK = false;
475 0 : if ( pImpl->m_aEndEditLink.IsSet() )
476 0 : pImpl->m_aEndEditLink.Call( this );
477 : }
478 0 : }
479 :
480 0 : bool SvBaseLink::ExecuteEdit( const OUString& _rNewName )
481 : {
482 0 : if( !_rNewName.isEmpty() )
483 : {
484 0 : SetLinkSourceName( _rNewName );
485 0 : if( !Update() )
486 : {
487 0 : OUString sApp, sTopic, sItem, sError;
488 0 : pImpl->m_pLinkMgr->GetDisplayNames( this, &sApp, &sTopic, &sItem );
489 0 : if( nObjType == OBJECT_CLIENT_DDE )
490 : {
491 0 : sError = SFX2_RESSTR(STR_DDE_ERROR);
492 :
493 0 : sal_Int32 nFndPos = sError.indexOf( '%' );
494 0 : if( -1 != nFndPos )
495 : {
496 0 : sError = sError.replaceAt( nFndPos, 1, sApp );
497 0 : nFndPos = nFndPos + sApp.getLength();
498 :
499 0 : if( -1 != ( nFndPos = sError.indexOf( '%', nFndPos )))
500 : {
501 0 : sError = sError.replaceAt( nFndPos, 1, sTopic );
502 0 : nFndPos = nFndPos + sTopic.getLength();
503 :
504 0 : if( -1 != ( nFndPos = sError.indexOf( '%', nFndPos )))
505 0 : sError = sError.replaceAt( nFndPos, 1, sItem );
506 : }
507 : }
508 : }
509 : else
510 0 : return false;
511 :
512 0 : ErrorBox( pImpl->m_pParentWin, WB_OK, sError ).Execute();
513 : }
514 : }
515 0 : else if( !pImpl->m_bIsConnect )
516 0 : Disconnect();
517 0 : pImpl->m_bIsConnect = false;
518 0 : return true;
519 : }
520 :
521 0 : void SvBaseLink::Closed()
522 : {
523 0 : if( xObj.Is() )
524 0 : xObj->RemoveAllDataAdvise( this );
525 0 : }
526 :
527 0 : FileDialogHelper & SvBaseLink::GetInsertFileDialog(const OUString& rFactory) const
528 : {
529 0 : if ( pImpl->m_pFileDlg )
530 0 : delete pImpl->m_pFileDlg;
531 : pImpl->m_pFileDlg = new FileDialogHelper(
532 : ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
533 0 : SFXWB_INSERT, rFactory);
534 0 : return *pImpl->m_pFileDlg;
535 : }
536 :
537 0 : ImplDdeItem::~ImplDdeItem()
538 : {
539 0 : bIsInDTOR = true;
540 : // So that no-one gets the idea to delete the pointer when Disconnecting!
541 0 : SvBaseLinkRef aRef( pLink );
542 0 : aRef->Disconnect();
543 0 : }
544 :
545 0 : DdeData* ImplDdeItem::Get( sal_uIntPtr nFormat )
546 : {
547 0 : if( pLink->GetObj() )
548 : {
549 : // is it still valid?
550 0 : if( bIsValidData && nFormat == aData.GetFormat() )
551 0 : return &aData;
552 :
553 0 : Any aValue;
554 0 : OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
555 0 : if( pLink->GetObj()->GetData( aValue, sMimeType ) )
556 : {
557 0 : if( aValue >>= aSeq )
558 : {
559 0 : aData = DdeData( (const char *)aSeq.getConstArray(), aSeq.getLength(), nFormat );
560 :
561 0 : bIsValidData = true;
562 0 : return &aData;
563 : }
564 0 : }
565 : }
566 0 : aSeq.realloc( 0 );
567 0 : bIsValidData = false;
568 0 : return 0;
569 : }
570 :
571 :
572 0 : bool ImplDdeItem::Put( const DdeData* )
573 : {
574 : OSL_FAIL( "ImplDdeItem::Put not implemented" );
575 0 : return false;
576 : }
577 :
578 :
579 0 : void ImplDdeItem::AdviseLoop( bool bOpen )
580 : {
581 : // Connection is closed, so also unsubscribe link
582 0 : if( pLink->GetObj() )
583 : {
584 0 : if( bOpen )
585 : {
586 : // A connection is re-established
587 0 : if( OBJECT_DDE_EXTERN == pLink->GetObjType() )
588 : {
589 0 : pLink->GetObj()->AddDataAdvise( pLink, OUString("text/plain;charset=utf-16"), ADVISEMODE_NODATA );
590 0 : pLink->GetObj()->AddConnectAdvise( pLink );
591 : }
592 : }
593 : else
594 : {
595 : // So that no-one gets the idea to delete the pointer
596 : // when Disconnecting!
597 0 : SvBaseLinkRef aRef( pLink );
598 0 : aRef->Disconnect();
599 : }
600 : }
601 0 : }
602 :
603 :
604 0 : static DdeTopic* FindTopic( const OUString & rLinkName, sal_uInt16* pItemStt )
605 : {
606 0 : if( rLinkName.isEmpty() )
607 0 : return 0;
608 :
609 0 : OUString sNm( rLinkName );
610 0 : sal_Int32 nTokenPos = 0;
611 0 : OUString sService( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
612 :
613 0 : DdeServices& rSvc = DdeService::GetServices();
614 0 : for (DdeServices::iterator aI = rSvc.begin(); aI != rSvc.end(); ++aI)
615 : {
616 0 : DdeService* pService = *aI;
617 0 : if( pService->GetName() == sService )
618 : {
619 : // then we search for the Topic
620 0 : OUString sTopic( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
621 0 : if( pItemStt )
622 0 : *pItemStt = nTokenPos;
623 :
624 0 : std::vector<DdeTopic*>& rTopics = pService->GetTopics();
625 :
626 0 : for( int i = 0; i < 2; ++i )
627 : {
628 0 : for( std::vector<DdeTopic*>::iterator iterTopic = rTopics.begin();
629 0 : iterTopic != rTopics.end(); ++iterTopic )
630 0 : if( (*iterTopic)->GetName() == sTopic )
631 0 : return *iterTopic;
632 :
633 : // Topic not found?
634 : // then we try once to create it
635 0 : if( i || !pService->MakeTopic( sTopic ) )
636 0 : break; // did not work, exiting
637 : }
638 0 : break;
639 : }
640 : }
641 0 : return 0;
642 : }
643 :
644 : }
645 :
646 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|