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/layout.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 42 : 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 : vcl::Window* m_pParentWin;
52 : FileDialogHelper* m_pFileDlg;
53 : bool m_bIsConnect;
54 :
55 74 : BaseLink_Impl() :
56 : m_pLinkMgr( NULL )
57 : , m_pParentWin( NULL )
58 : , m_pFileDlg( NULL )
59 74 : , m_bIsConnect( false )
60 74 : {}
61 :
62 72 : ~BaseLink_Impl()
63 72 : { 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 74 : ImplBaseLinkData()
88 : {
89 74 : ClientType.nCntntType = 0;
90 74 : ClientType.bIntrnlLnk = false;
91 74 : ClientType.nUpdateMode = 0;
92 74 : DDEType.pItem = NULL;
93 74 : }
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 2 : SvBaseLink::SvBaseLink()
127 2 : : m_bIsReadOnly(false)
128 : {
129 2 : pImpl = new BaseLink_Impl();
130 2 : nObjType = OBJECT_CLIENT_SO;
131 2 : pImplData = new ImplBaseLinkData;
132 2 : bVisible = bSynchron = bUseCache = true;
133 2 : bWasLastEditOK = false;
134 2 : }
135 :
136 :
137 :
138 72 : SvBaseLink::SvBaseLink( sal_uInt16 nUpdateMode, sal_uIntPtr nContentType )
139 72 : : m_bIsReadOnly(false)
140 : {
141 72 : pImpl = new BaseLink_Impl();
142 72 : nObjType = OBJECT_CLIENT_SO;
143 72 : pImplData = new ImplBaseLinkData;
144 72 : bVisible = bSynchron = bUseCache = true;
145 72 : bWasLastEditOK = false;
146 :
147 : // It it going to be a Ole-Link,
148 72 : pImplData->ClientType.nUpdateMode = nUpdateMode;
149 72 : pImplData->ClientType.nCntntType = nContentType;
150 72 : pImplData->ClientType.bIntrnlLnk = false;
151 72 : }
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 144 : SvBaseLink::~SvBaseLink()
195 : {
196 72 : Disconnect();
197 :
198 72 : 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 72 : delete pImplData;
207 72 : delete pImpl;
208 72 : }
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 76 : 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 76 : nObjType = nObjTypeP;
231 76 : }
232 :
233 :
234 :
235 76 : void SvBaseLink::SetName( const OUString & rNm )
236 : {
237 76 : aLinkName = rNm;
238 76 : }
239 :
240 :
241 :
242 10 : 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 10 : xObj = pObj;
249 10 : }
250 :
251 :
252 :
253 16 : void SvBaseLink::SetLinkSourceName( const OUString & rLnkNm )
254 : {
255 16 : if( aLinkName == rLnkNm )
256 16 : return;
257 :
258 16 : AddNextRef(); // should be superfluous
259 : // remove old connection
260 16 : Disconnect();
261 :
262 16 : aLinkName = rLnkNm;
263 :
264 : // New Connection
265 16 : _GetRealObject();
266 16 : ReleaseRef(); // should be superfluous
267 : }
268 :
269 :
270 :
271 :
272 :
273 :
274 88 : void SvBaseLink::SetUpdateMode( sal_uInt16 nMode )
275 : {
276 176 : if( ( OBJECT_CLIENT_SO & nObjType ) &&
277 88 : pImplData->ClientType.nUpdateMode != nMode )
278 : {
279 12 : AddNextRef();
280 12 : Disconnect();
281 :
282 12 : pImplData->ClientType.nUpdateMode = nMode;
283 12 : _GetRealObject();
284 12 : ReleaseRef();
285 : }
286 88 : }
287 :
288 : // #i88291#
289 0 : void SvBaseLink::clearStreamToLoadFrom()
290 : {
291 0 : m_xInputStreamToLoadFrom.clear();
292 0 : if( xObj.Is() )
293 : {
294 0 : xObj->clearStreamToLoadFrom();
295 : }
296 0 : }
297 :
298 88 : bool SvBaseLink::Update()
299 : {
300 88 : if( OBJECT_CLIENT_SO & nObjType )
301 : {
302 88 : AddNextRef();
303 88 : Disconnect();
304 :
305 88 : _GetRealObject();
306 88 : ReleaseRef();
307 88 : if( xObj.Is() )
308 : {
309 88 : xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly);
310 : OUString sMimeType( SotExchange::GetFormatMimeType(
311 88 : pImplData->ClientType.nCntntType ));
312 88 : Any aData;
313 :
314 88 : if( xObj->GetData( aData, sMimeType ) )
315 : {
316 88 : UpdateResult eRes = DataChanged(sMimeType, aData);
317 88 : bool bSuccess = eRes == SUCCESS;
318 : //for manual Updates there is no need to hold the ServerObject
319 186 : if( OBJECT_CLIENT_DDE == nObjType &&
320 88 : LINKUPDATE_ONCALL == GetUpdateMode() && xObj.Is() )
321 0 : xObj->RemoveAllDataAdvise( this );
322 88 : return bSuccess;
323 : }
324 0 : if( xObj.Is() )
325 : {
326 : // should be asynschron?
327 0 : if( xObj->IsPending() )
328 0 : return true;
329 :
330 : // we do not need the object anymore
331 0 : AddNextRef();
332 0 : Disconnect();
333 0 : ReleaseRef();
334 0 : }
335 : }
336 : }
337 0 : return false;
338 : }
339 :
340 :
341 24 : sal_uInt16 SvBaseLink::GetUpdateMode() const
342 : {
343 24 : return ( OBJECT_CLIENT_SO & nObjType )
344 : ? pImplData->ClientType.nUpdateMode
345 24 : : sal::static_int_cast< sal_uInt16 >( LINKUPDATE_ONCALL );
346 : }
347 :
348 :
349 122 : void SvBaseLink::_GetRealObject( bool bConnect)
350 : {
351 122 : if( !pImpl->m_pLinkMgr )
352 146 : return;
353 :
354 : DBG_ASSERT( !xObj.Is(), "object already exist" );
355 :
356 98 : if( OBJECT_CLIENT_DDE == nObjType )
357 : {
358 10 : OUString sServer;
359 40 : if( pImpl->m_pLinkMgr->GetDisplayNames( this, &sServer ) &&
360 40 : sServer == Application::GetAppName() ) // internal Link !!!
361 : {
362 : // so that the Internal link can be created!
363 10 : nObjType = OBJECT_INTERN;
364 10 : xObj = pImpl->m_pLinkMgr->CreateObj( this );
365 :
366 10 : pImplData->ClientType.bIntrnlLnk = true;
367 10 : nObjType = OBJECT_CLIENT_DDE; // so we know what it once was!
368 : }
369 : else
370 : {
371 0 : pImplData->ClientType.bIntrnlLnk = false;
372 0 : xObj = pImpl->m_pLinkMgr->CreateObj( this );
373 10 : }
374 : }
375 88 : else if( (OBJECT_CLIENT_SO & nObjType) )
376 88 : xObj = pImpl->m_pLinkMgr->CreateObj( this );
377 :
378 98 : if( bConnect && ( !xObj.Is() || !xObj->Connect( this ) ) )
379 0 : Disconnect();
380 : }
381 :
382 112 : sal_uIntPtr SvBaseLink::GetContentType() const
383 : {
384 112 : if( OBJECT_CLIENT_SO & nObjType )
385 112 : return pImplData->ClientType.nCntntType;
386 :
387 0 : return 0; // all Formats ?
388 : }
389 :
390 :
391 22 : bool SvBaseLink::SetContentType( sal_uIntPtr nType )
392 : {
393 22 : if( OBJECT_CLIENT_SO & nObjType )
394 : {
395 22 : pImplData->ClientType.nCntntType = nType;
396 22 : return true;
397 : }
398 0 : return false;
399 : }
400 :
401 600 : LinkManager* SvBaseLink::GetLinkManager()
402 : {
403 600 : return pImpl->m_pLinkMgr;
404 : }
405 :
406 0 : const LinkManager* SvBaseLink::GetLinkManager() const
407 : {
408 0 : return pImpl->m_pLinkMgr;
409 : }
410 :
411 176 : void SvBaseLink::SetLinkManager( LinkManager* _pMgr )
412 : {
413 176 : pImpl->m_pLinkMgr = _pMgr;
414 176 : }
415 :
416 292 : void SvBaseLink::Disconnect()
417 : {
418 292 : if( xObj.Is() )
419 : {
420 98 : xObj->RemoveAllDataAdvise( this );
421 98 : xObj->RemoveConnectAdvise( this );
422 98 : xObj.Clear();
423 : }
424 292 : }
425 :
426 0 : SvBaseLink::UpdateResult SvBaseLink::DataChanged( const OUString &, const ::com::sun::star::uno::Any & )
427 : {
428 0 : switch( nObjType )
429 : {
430 : case OBJECT_DDE_EXTERN:
431 0 : if( pImplData->DDEType.pItem )
432 0 : pImplData->DDEType.pItem->Notify();
433 0 : break;
434 : }
435 0 : return SUCCESS;
436 : }
437 :
438 0 : void SvBaseLink::Edit( vcl::Window* pParent, const Link& rEndEditHdl )
439 : {
440 0 : pImpl->m_pParentWin = pParent;
441 0 : pImpl->m_aEndEditLink = rEndEditHdl;
442 0 : pImpl->m_bIsConnect = xObj.Is();
443 0 : if( !pImpl->m_bIsConnect )
444 0 : _GetRealObject( xObj.Is() );
445 :
446 0 : bool bAsync = false;
447 0 : Link aLink = LINK( this, SvBaseLink, EndEditHdl );
448 :
449 0 : if( OBJECT_CLIENT_SO & nObjType && pImplData->ClientType.bIntrnlLnk )
450 : {
451 0 : if( pImpl->m_pLinkMgr )
452 : {
453 0 : SvLinkSourceRef ref = pImpl->m_pLinkMgr->CreateObj( this );
454 0 : if( ref.Is() )
455 : {
456 0 : ref->Edit( pParent, this, aLink );
457 0 : bAsync = true;
458 0 : }
459 0 : }
460 : }
461 : else
462 : {
463 0 : xObj->Edit( pParent, this, aLink );
464 0 : bAsync = true;
465 : }
466 :
467 0 : if ( !bAsync )
468 : {
469 0 : ExecuteEdit( OUString() );
470 0 : bWasLastEditOK = false;
471 0 : if ( pImpl->m_aEndEditLink.IsSet() )
472 0 : pImpl->m_aEndEditLink.Call( this );
473 : }
474 0 : }
475 :
476 0 : bool SvBaseLink::ExecuteEdit( const OUString& _rNewName )
477 : {
478 0 : if( !_rNewName.isEmpty() )
479 : {
480 0 : SetLinkSourceName( _rNewName );
481 0 : if( !Update() )
482 : {
483 0 : OUString sApp, sTopic, sItem, sError;
484 0 : pImpl->m_pLinkMgr->GetDisplayNames( this, &sApp, &sTopic, &sItem );
485 0 : if( nObjType == OBJECT_CLIENT_DDE )
486 : {
487 0 : sError = SFX2_RESSTR(STR_DDE_ERROR);
488 :
489 0 : sal_Int32 nFndPos = sError.indexOf( '%' );
490 0 : if( -1 != nFndPos )
491 : {
492 0 : sError = sError.replaceAt( nFndPos, 1, sApp );
493 0 : nFndPos = nFndPos + sApp.getLength();
494 :
495 0 : if( -1 != ( nFndPos = sError.indexOf( '%', nFndPos )))
496 : {
497 0 : sError = sError.replaceAt( nFndPos, 1, sTopic );
498 0 : nFndPos = nFndPos + sTopic.getLength();
499 :
500 0 : if( -1 != ( nFndPos = sError.indexOf( '%', nFndPos )))
501 0 : sError = sError.replaceAt( nFndPos, 1, sItem );
502 : }
503 : }
504 : }
505 : else
506 0 : return false;
507 :
508 0 : MessageDialog(pImpl->m_pParentWin, sError).Execute();
509 : }
510 : }
511 0 : else if( !pImpl->m_bIsConnect )
512 0 : Disconnect();
513 0 : pImpl->m_bIsConnect = false;
514 0 : return true;
515 : }
516 :
517 0 : void SvBaseLink::Closed()
518 : {
519 0 : if( xObj.Is() )
520 0 : xObj->RemoveAllDataAdvise( this );
521 0 : }
522 :
523 0 : FileDialogHelper & SvBaseLink::GetInsertFileDialog(const OUString& rFactory) const
524 : {
525 0 : if ( pImpl->m_pFileDlg )
526 0 : delete pImpl->m_pFileDlg;
527 : pImpl->m_pFileDlg = new FileDialogHelper(
528 : ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
529 0 : SFXWB_INSERT, rFactory);
530 0 : return *pImpl->m_pFileDlg;
531 : }
532 :
533 0 : ImplDdeItem::~ImplDdeItem()
534 : {
535 0 : bIsInDTOR = true;
536 : // So that no-one gets the idea to delete the pointer when Disconnecting!
537 0 : SvBaseLinkRef aRef( pLink );
538 0 : aRef->Disconnect();
539 0 : }
540 :
541 0 : DdeData* ImplDdeItem::Get( sal_uIntPtr nFormat )
542 : {
543 0 : if( pLink->GetObj() )
544 : {
545 : // is it still valid?
546 0 : if( bIsValidData && nFormat == aData.GetFormat() )
547 0 : return &aData;
548 :
549 0 : Any aValue;
550 0 : OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
551 0 : if( pLink->GetObj()->GetData( aValue, sMimeType ) )
552 : {
553 0 : if( aValue >>= aSeq )
554 : {
555 0 : aData = DdeData( (const char *)aSeq.getConstArray(), aSeq.getLength(), nFormat );
556 :
557 0 : bIsValidData = true;
558 0 : return &aData;
559 : }
560 0 : }
561 : }
562 0 : aSeq.realloc( 0 );
563 0 : bIsValidData = false;
564 0 : return 0;
565 : }
566 :
567 :
568 0 : bool ImplDdeItem::Put( const DdeData* )
569 : {
570 : OSL_FAIL( "ImplDdeItem::Put not implemented" );
571 0 : return false;
572 : }
573 :
574 :
575 0 : void ImplDdeItem::AdviseLoop( bool bOpen )
576 : {
577 : // Connection is closed, so also unsubscribe link
578 0 : if( pLink->GetObj() )
579 : {
580 0 : if( bOpen )
581 : {
582 : // A connection is re-established
583 0 : if( OBJECT_DDE_EXTERN == pLink->GetObjType() )
584 : {
585 0 : pLink->GetObj()->AddDataAdvise( pLink, OUString("text/plain;charset=utf-16"), ADVISEMODE_NODATA );
586 0 : pLink->GetObj()->AddConnectAdvise( pLink );
587 : }
588 : }
589 : else
590 : {
591 : // So that no-one gets the idea to delete the pointer
592 : // when Disconnecting!
593 0 : SvBaseLinkRef aRef( pLink );
594 0 : aRef->Disconnect();
595 : }
596 : }
597 0 : }
598 :
599 :
600 0 : static DdeTopic* FindTopic( const OUString & rLinkName, sal_uInt16* pItemStt )
601 : {
602 0 : if( rLinkName.isEmpty() )
603 0 : return 0;
604 :
605 0 : OUString sNm( rLinkName );
606 0 : sal_Int32 nTokenPos = 0;
607 0 : OUString sService( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
608 :
609 0 : DdeServices& rSvc = DdeService::GetServices();
610 0 : for (DdeServices::iterator aI = rSvc.begin(); aI != rSvc.end(); ++aI)
611 : {
612 0 : DdeService* pService = *aI;
613 0 : if( pService->GetName() == sService )
614 : {
615 : // then we search for the Topic
616 0 : OUString sTopic( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
617 0 : if( pItemStt )
618 0 : *pItemStt = nTokenPos;
619 :
620 0 : std::vector<DdeTopic*>& rTopics = pService->GetTopics();
621 :
622 0 : for( int i = 0; i < 2; ++i )
623 : {
624 0 : for( std::vector<DdeTopic*>::iterator iterTopic = rTopics.begin();
625 0 : iterTopic != rTopics.end(); ++iterTopic )
626 0 : if( (*iterTopic)->GetName() == sTopic )
627 0 : return *iterTopic;
628 :
629 : // Topic not found?
630 : // then we try once to create it
631 0 : if( i || !pService->MakeTopic( sTopic ) )
632 0 : break; // did not work, exiting
633 : }
634 0 : break;
635 : }
636 : }
637 0 : return 0;
638 : }
639 :
640 951 : }
641 :
642 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|