xref: /trunk/main/sfx2/source/appl/lnkbase2.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sfx2.hxx"
30 
31 
32 #include <sfx2/lnkbase.hxx>
33 #include <sot/exchange.hxx>
34 #include <com/sun/star/uno/Any.hxx>
35 #include <com/sun/star/uno/Sequence.hxx>
36 #include <vcl/msgbox.hxx>
37 #include <sfx2/linkmgr.hxx>
38 #include <vcl/svapp.hxx>
39 #include "app.hrc"
40 #include "sfx2/sfxresid.hxx"
41 #include <sfx2/filedlghelper.hxx>
42 #include <tools/debug.hxx>
43 #include <svl/svdde.hxx>
44 
45 using namespace ::com::sun::star::uno;
46 
47 namespace sfx2
48 {
49 
50 TYPEINIT0( SvBaseLink )
51 
52 static DdeTopic* FindTopic( const String &, sal_uInt16* = 0 );
53 
54 class  ImplDdeItem;
55 
56 struct BaseLink_Impl
57 {
58     Link                m_aEndEditLink;
59     LinkManager*      m_pLinkMgr;
60     Window*             m_pParentWin;
61     FileDialogHelper*   m_pFileDlg;
62     bool                m_bIsConnect;
63 
64     BaseLink_Impl() :
65           m_pLinkMgr( NULL )
66         , m_pParentWin( NULL )
67         , m_pFileDlg( NULL )
68         , m_bIsConnect( false )
69         {}
70 
71     ~BaseLink_Impl()
72         { delete m_pFileDlg; }
73 };
74 
75 // nur fuer die interne Verwaltung
76 struct ImplBaseLinkData
77 {
78     struct tClientType
79     {
80         // gilt fuer alle Links
81         sal_uIntPtr             nCntntType; // Update Format
82         // nicht Ole-Links
83         sal_Bool            bIntrnlLnk; // ist es ein interner Link
84         sal_uInt16          nUpdateMode;// UpdateMode
85     };
86 
87     struct tDDEType
88     {
89         ImplDdeItem* pItem;
90     };
91 
92     union {
93         tClientType ClientType;
94         tDDEType DDEType;
95     };
96     ImplBaseLinkData()
97     {
98         ClientType.nCntntType = 0;
99         ClientType.bIntrnlLnk = sal_False;
100         ClientType.nUpdateMode = 0;
101         DDEType.pItem = NULL;
102     }
103 };
104 
105 
106 class ImplDdeItem : public DdeGetPutItem
107 {
108     SvBaseLink* pLink;
109     DdeData aData;
110     Sequence< sal_Int8 > aSeq;          // Datacontainer for DdeData !!!
111     sal_Bool bIsValidData : 1;
112     sal_Bool bIsInDTOR : 1;
113 public:
114     ImplDdeItem( SvBaseLink& rLink, const String& rStr )
115         : DdeGetPutItem( rStr ), pLink( &rLink ), bIsValidData( sal_False ),
116         bIsInDTOR( sal_False )
117     {}
118     virtual ~ImplDdeItem();
119 
120     virtual DdeData* Get( sal_uIntPtr );
121     virtual sal_Bool Put( const DdeData* );
122     virtual void AdviseLoop( sal_Bool );
123 
124     void Notify()
125     {
126         bIsValidData = sal_False;
127         DdeGetPutItem::NotifyClient();
128     }
129 
130     sal_Bool IsInDTOR() const { return bIsInDTOR; }
131 };
132 
133 
134 /************************************************************************
135 |*    SvBaseLink::SvBaseLink()
136 |*
137 |*    Beschreibung
138 *************************************************************************/
139 
140 SvBaseLink::SvBaseLink()
141 {
142     pImpl = new BaseLink_Impl();
143     nObjType = OBJECT_CLIENT_SO;
144     pImplData = new ImplBaseLinkData;
145     bVisible = bSynchron = bUseCache = sal_True;
146     bWasLastEditOK = sal_False;
147 }
148 
149 /************************************************************************
150 |*    SvBaseLink::SvBaseLink()
151 |*
152 |*    Beschreibung
153 *************************************************************************/
154 
155 SvBaseLink::SvBaseLink( sal_uInt16 nUpdateMode, sal_uIntPtr nContentType )
156 {
157     pImpl = new BaseLink_Impl();
158     nObjType = OBJECT_CLIENT_SO;
159     pImplData = new ImplBaseLinkData;
160     bVisible = bSynchron = bUseCache = sal_True;
161     bWasLastEditOK = sal_False;
162 
163     // falls es ein Ole-Link wird,
164     pImplData->ClientType.nUpdateMode = nUpdateMode;
165     pImplData->ClientType.nCntntType = nContentType;
166     pImplData->ClientType.bIntrnlLnk = sal_False;
167 }
168 
169 /************************************************************************
170 |*    SvBaseLink::SvBaseLink()
171 |*
172 |*    Beschreibung
173 *************************************************************************/
174 
175 SvBaseLink::SvBaseLink( const String& rLinkName, sal_uInt16 nObjectType, SvLinkSource* pObj )
176 {
177     bVisible = bSynchron = bUseCache = sal_True;
178     bWasLastEditOK = sal_False;
179     aLinkName = rLinkName;
180     pImplData = new ImplBaseLinkData;
181     nObjType = nObjectType;
182 
183     if( !pObj )
184     {
185         DBG_ASSERT( pObj, "Wo ist mein zu linkendes Object" );
186         return;
187     }
188 
189     if( OBJECT_DDE_EXTERN == nObjType )
190     {
191         sal_uInt16 nItemStt = 0;
192         DdeTopic* pTopic = FindTopic( aLinkName, &nItemStt );
193         if( pTopic )
194         {
195             // dann haben wir alles zusammen
196             // MM hat gefummelt ???
197             // MM_TODO wie kriege ich den Namen
198             String aStr = aLinkName; // xLinkName->GetDisplayName();
199             aStr = aStr.Copy( nItemStt );
200             pImplData->DDEType.pItem = new ImplDdeItem( *this, aStr );
201             pTopic->InsertItem( pImplData->DDEType.pItem );
202 
203             // dann koennen wir uns auch das Advise merken
204             xObj = pObj;
205         }
206     }
207     else if( pObj->Connect( this ) )
208         xObj = pObj;
209 }
210 
211 /************************************************************************
212 |*    SvBaseLink::~SvBaseLink()
213 |*
214 |*    Beschreibung
215 *************************************************************************/
216 
217 SvBaseLink::~SvBaseLink()
218 {
219     Disconnect();
220 
221     switch( nObjType )
222     {
223     case OBJECT_DDE_EXTERN:
224         if( !pImplData->DDEType.pItem->IsInDTOR() )
225             delete pImplData->DDEType.pItem;
226         break;
227     }
228 
229     delete pImplData;
230 }
231 
232 IMPL_LINK( SvBaseLink, EndEditHdl, String*, _pNewName )
233 {
234     String sNewName;
235     if ( _pNewName )
236         sNewName = *_pNewName;
237     if ( !ExecuteEdit( sNewName ) )
238         sNewName.Erase();
239     bWasLastEditOK = ( sNewName.Len() > 0 );
240     if ( pImpl->m_aEndEditLink.IsSet() )
241         pImpl->m_aEndEditLink.Call( this );
242     return 0;
243 }
244 
245 /************************************************************************
246 |*    SvBaseLink::SetObjType()
247 |*
248 |*    Beschreibung
249 *************************************************************************/
250 
251 void SvBaseLink::SetObjType( sal_uInt16 nObjTypeP )
252 {
253     DBG_ASSERT( nObjType != OBJECT_CLIENT_DDE, "type already set" );
254     DBG_ASSERT( !xObj.Is(), "object exist" );
255 
256     nObjType = nObjTypeP;
257 }
258 
259 /************************************************************************
260 |*    SvBaseLink::SetName()
261 |*
262 |*    Beschreibung
263 *************************************************************************/
264 
265 void SvBaseLink::SetName( const String & rNm )
266 {
267     aLinkName = rNm;
268 }
269 
270 /************************************************************************
271 |*    SvBaseLink::GetName()
272 |*
273 |*    Beschreibung
274 *************************************************************************/
275 
276 String SvBaseLink::GetName() const
277 {
278     return aLinkName;
279 }
280 
281 /************************************************************************
282 |*    SvBaseLink::SetObj()
283 |*
284 |*    Beschreibung
285 *************************************************************************/
286 
287 void SvBaseLink::SetObj( SvLinkSource * pObj )
288 {
289     DBG_ASSERT( (nObjType & OBJECT_CLIENT_SO &&
290                 pImplData->ClientType.bIntrnlLnk) ||
291                 nObjType == OBJECT_CLIENT_GRF,
292                 "no intern link" );
293     xObj = pObj;
294 }
295 
296 /************************************************************************
297 |*    SvBaseLink::SetLinkSourceName()
298 |*
299 |*    Beschreibung
300 *************************************************************************/
301 
302 void SvBaseLink::SetLinkSourceName( const String & rLnkNm )
303 {
304     if( aLinkName == rLnkNm )
305         return;
306 
307     AddNextRef(); // sollte ueberfluessig sein
308     // Alte Verbindung weg
309     Disconnect();
310 
311     aLinkName = rLnkNm;
312 
313     // Neu verbinden
314     _GetRealObject();
315     ReleaseRef(); // sollte ueberfluessig sein
316 }
317 
318 /************************************************************************
319 |*    SvBaseLink::GetLinkSourceName()
320 |*
321 |*    Beschreibung
322 *************************************************************************/
323 
324 String  SvBaseLink::GetLinkSourceName() const
325 {
326     return aLinkName;
327 }
328 
329 
330 /************************************************************************
331 |*    SvBaseLink::SetUpdateMode()
332 |*
333 |*    Beschreibung
334 *************************************************************************/
335 
336 void SvBaseLink::SetUpdateMode( sal_uInt16 nMode )
337 {
338     if( ( OBJECT_CLIENT_SO & nObjType ) &&
339         pImplData->ClientType.nUpdateMode != nMode )
340     {
341         AddNextRef();
342         Disconnect();
343 
344         pImplData->ClientType.nUpdateMode = nMode;
345         _GetRealObject();
346         ReleaseRef();
347     }
348 }
349 
350 // --> OD 2008-06-19 #i88291#
351 void SvBaseLink::clearStreamToLoadFrom()
352 {
353     m_xInputStreamToLoadFrom.clear();
354     if( xObj.Is() )
355     {
356         xObj->clearStreamToLoadFrom();
357     }
358 }
359 // <--
360 
361 sal_Bool SvBaseLink::Update()
362 {
363     if( OBJECT_CLIENT_SO & nObjType )
364     {
365         AddNextRef();
366         Disconnect();
367 
368         _GetRealObject();
369         ReleaseRef();
370         if( xObj.Is() )
371         {
372             xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly);
373             // m_xInputStreamToLoadFrom = 0;
374             String sMimeType( SotExchange::GetFormatMimeType(
375                             pImplData->ClientType.nCntntType ));
376             Any aData;
377 
378             if( xObj->GetData( aData, sMimeType ) )
379             {
380                 DataChanged( sMimeType, aData );
381                 //JP 13.07.00: Bug 76817 - for manual Updates there is no
382                 //              need to hold the ServerObject
383                 if( OBJECT_CLIENT_DDE == nObjType &&
384                     LINKUPDATE_ONCALL == GetUpdateMode() && xObj.Is() )
385                     xObj->RemoveAllDataAdvise( this );
386                 return sal_True;
387             }
388             if( xObj.Is() )
389             {
390                 // sollten wir asynschron sein?
391                 if( xObj->IsPending() )
392                     return sal_True;
393 
394                 // dann brauchen wir das Object auch nicht mehr
395                 AddNextRef();
396                 Disconnect();
397                 ReleaseRef();
398             }
399         }
400     }
401     return sal_False;
402 }
403 
404 
405 sal_uInt16 SvBaseLink::GetUpdateMode() const
406 {
407     return ( OBJECT_CLIENT_SO & nObjType )
408             ? pImplData->ClientType.nUpdateMode
409             : sal::static_int_cast< sal_uInt16 >( LINKUPDATE_ONCALL );
410 }
411 
412 
413 void SvBaseLink::_GetRealObject( sal_Bool bConnect)
414 {
415     if( !pImpl->m_pLinkMgr )
416         return;
417 
418     DBG_ASSERT( !xObj.Is(), "object already exist" );
419 
420     if( OBJECT_CLIENT_DDE == nObjType )
421     {
422         String sServer;
423         if( pImpl->m_pLinkMgr->GetDisplayNames( this, &sServer ) &&
424             sServer == GetpApp()->GetAppName() )        // interner Link !!!
425         {
426             // damit der Internal - Link erzeugt werden kann !!!
427             nObjType = OBJECT_INTERN;
428             xObj = pImpl->m_pLinkMgr->CreateObj( this );
429 
430             pImplData->ClientType.bIntrnlLnk = sal_True;
431             nObjType = OBJECT_CLIENT_DDE;       // damit wir wissen was es mal war !!
432         }
433         else
434         {
435             pImplData->ClientType.bIntrnlLnk = sal_False;
436             xObj = pImpl->m_pLinkMgr->CreateObj( this );
437         }
438     }
439     else if( (OBJECT_CLIENT_SO & nObjType) )
440         xObj = pImpl->m_pLinkMgr->CreateObj( this );
441 
442     if( bConnect && ( !xObj.Is() || !xObj->Connect( this ) ) )
443         Disconnect();
444 }
445 
446 sal_uIntPtr SvBaseLink::GetContentType() const
447 {
448     if( OBJECT_CLIENT_SO & nObjType )
449         return pImplData->ClientType.nCntntType;
450 
451     return 0;       // alle Formate ?
452 }
453 
454 
455 sal_Bool SvBaseLink::SetContentType( sal_uIntPtr nType )
456 {
457     if( OBJECT_CLIENT_SO & nObjType )
458     {
459         pImplData->ClientType.nCntntType = nType;
460         return sal_True;
461     }
462     return sal_False;
463 }
464 
465 LinkManager* SvBaseLink::GetLinkManager()
466 {
467     return pImpl->m_pLinkMgr;
468 }
469 
470 const LinkManager* SvBaseLink::GetLinkManager() const
471 {
472     return pImpl->m_pLinkMgr;
473 }
474 
475 void SvBaseLink::SetLinkManager( LinkManager* _pMgr )
476 {
477     pImpl->m_pLinkMgr = _pMgr;
478 }
479 
480 void SvBaseLink::Disconnect()
481 {
482     if( xObj.Is() )
483     {
484         xObj->RemoveAllDataAdvise( this );
485         xObj->RemoveConnectAdvise( this );
486         xObj.Clear();
487     }
488 }
489 
490 void SvBaseLink::DataChanged( const String &, const ::com::sun::star::uno::Any & )
491 {
492     switch( nObjType )
493     {
494     case OBJECT_DDE_EXTERN:
495         if( pImplData->DDEType.pItem )
496             pImplData->DDEType.pItem->Notify();
497         break;
498     }
499 }
500 
501 void SvBaseLink::Edit( Window* pParent, const Link& rEndEditHdl )
502 {
503     pImpl->m_pParentWin = pParent;
504     pImpl->m_aEndEditLink = rEndEditHdl;
505     pImpl->m_bIsConnect = ( xObj.Is() != sal_False );
506     if( !pImpl->m_bIsConnect )
507         _GetRealObject( xObj.Is() );
508 
509     bool bAsync = false;
510     Link aLink = LINK( this, SvBaseLink, EndEditHdl );
511 
512     if( OBJECT_CLIENT_SO & nObjType && pImplData->ClientType.bIntrnlLnk )
513     {
514         if( pImpl->m_pLinkMgr )
515         {
516             SvLinkSourceRef ref = pImpl->m_pLinkMgr->CreateObj( this );
517             if( ref.Is() )
518             {
519                 ref->Edit( pParent, this, aLink );
520                 bAsync = true;
521             }
522         }
523     }
524     else
525     {
526         xObj->Edit( pParent, this, aLink );
527         bAsync = true;
528     }
529 
530     if ( !bAsync )
531     {
532         ExecuteEdit( String() );
533         bWasLastEditOK = sal_False;
534         if ( pImpl->m_aEndEditLink.IsSet() )
535             pImpl->m_aEndEditLink.Call( this );
536     }
537 }
538 
539 bool SvBaseLink::ExecuteEdit( const String& _rNewName )
540 {
541     if( _rNewName.Len() != 0 )
542     {
543         SetLinkSourceName( _rNewName );
544         if( !Update() )
545         {
546             String sApp, sTopic, sItem, sError;
547             pImpl->m_pLinkMgr->GetDisplayNames( this, &sApp, &sTopic, &sItem );
548             if( nObjType == OBJECT_CLIENT_DDE )
549             {
550                 sError = SfxResId( STR_DDE_ERROR );
551 
552                 sal_uInt16 nFndPos = sError.Search( '%' );
553                 if( STRING_NOTFOUND != nFndPos )
554                 {
555                     sError.Erase( nFndPos, 1 ).Insert( sApp, nFndPos );
556                     nFndPos = nFndPos + sApp.Len();
557                 }
558                 if( STRING_NOTFOUND != ( nFndPos = sError.Search( '%', nFndPos )))
559                 {
560                     sError.Erase( nFndPos, 1 ).Insert( sTopic, nFndPos );
561                     nFndPos = nFndPos + sTopic.Len();
562                 }
563                 if( STRING_NOTFOUND != ( nFndPos = sError.Search( '%', nFndPos )))
564                     sError.Erase( nFndPos, 1 ).Insert( sItem, nFndPos );
565             }
566             else
567                 return false;
568 
569             ErrorBox( pImpl->m_pParentWin, WB_OK, sError ).Execute();
570         }
571     }
572     else if( !pImpl->m_bIsConnect )
573         Disconnect();
574     pImpl->m_bIsConnect = false;
575     return true;
576 }
577 
578 void SvBaseLink::Closed()
579 {
580     if( xObj.Is() )
581         // beim Advise Abmelden
582         xObj->RemoveAllDataAdvise( this );
583 }
584 
585 FileDialogHelper* SvBaseLink::GetFileDialog( sal_uInt32 nFlags, const String& rFactory ) const
586 {
587     if ( pImpl->m_pFileDlg )
588         delete pImpl->m_pFileDlg;
589     pImpl->m_pFileDlg = new FileDialogHelper( nFlags, rFactory );
590     return pImpl->m_pFileDlg;
591 }
592 
593 ImplDdeItem::~ImplDdeItem()
594 {
595     bIsInDTOR = sal_True;
596     // damit im Disconnect nicht jemand auf die Idee kommt, den Pointer zu
597     // loeschen!!
598     SvBaseLinkRef aRef( pLink );
599     aRef->Disconnect();
600 }
601 
602 DdeData* ImplDdeItem::Get( sal_uIntPtr nFormat )
603 {
604     if( pLink->GetObj() )
605     {
606         // ist das noch gueltig?
607         if( bIsValidData && nFormat == aData.GetFormat() )
608             return &aData;
609 
610         Any aValue;
611         String sMimeType( SotExchange::GetFormatMimeType( nFormat ));
612         if( pLink->GetObj()->GetData( aValue, sMimeType ) )
613         {
614             if( aValue >>= aSeq )
615             {
616                 aData = DdeData( (const char *)aSeq.getConstArray(), aSeq.getLength(), nFormat );
617 
618                 bIsValidData = sal_True;
619                 return &aData;
620             }
621         }
622     }
623     aSeq.realloc( 0 );
624     bIsValidData = sal_False;
625     return 0;
626 }
627 
628 
629 sal_Bool ImplDdeItem::Put( const DdeData*  )
630 {
631     DBG_ERROR( "ImplDdeItem::Put not implemented" );
632     return sal_False;
633 }
634 
635 
636 void ImplDdeItem::AdviseLoop( sal_Bool bOpen )
637 {
638     // Verbindung wird geschlossen, also Link abmelden
639     if( pLink->GetObj() )
640     {
641         if( bOpen )
642         {
643             // es wird wieder eine Verbindung hergestellt
644             if( OBJECT_DDE_EXTERN == pLink->GetObjType() )
645             {
646                 pLink->GetObj()->AddDataAdvise( pLink, String::CreateFromAscii( "text/plain;charset=utf-16" ),  ADVISEMODE_NODATA );
647                 pLink->GetObj()->AddConnectAdvise( pLink );
648             }
649         }
650         else
651         {
652             // damit im Disconnect nicht jemand auf die Idee kommt,
653             // den Pointer zu loeschen!!
654             SvBaseLinkRef aRef( pLink );
655             aRef->Disconnect();
656         }
657     }
658 }
659 
660 
661 static DdeTopic* FindTopic( const String & rLinkName, sal_uInt16* pItemStt )
662 {
663     if( 0 == rLinkName.Len() )
664         return 0;
665 
666     String sNm( rLinkName );
667     sal_uInt16 nTokenPos = 0;
668     String sService( sNm.GetToken( 0, cTokenSeperator, nTokenPos ) );
669 
670     DdeServices& rSvc = DdeService::GetServices();
671     for( DdeService* pService = rSvc.First(); pService;
672                                                 pService = rSvc.Next() )
673         if( pService->GetName() == sService )
674         {
675             // dann suchen wir uns das Topic
676             String sTopic( sNm.GetToken( 0, cTokenSeperator, nTokenPos ) );
677             if( pItemStt )
678                 *pItemStt = nTokenPos;
679 
680             DdeTopics& rTopics = pService->GetTopics();
681 
682             for( int i = 0; i < 2; ++i )
683             {
684                 for( DdeTopic* pTopic = rTopics.First(); pTopic;
685                                                 pTopic = rTopics.Next() )
686                     if( pTopic->GetName() == sTopic )
687                         return pTopic;
688 
689                 // Topic nicht gefunden ?
690                 // dann versuchen wir ihn mal anzulegen
691                 if( i || !pService->MakeTopic( sTopic ) )
692                     break;  // hat nicht geklappt, also raus
693             }
694             break;
695         }
696     return 0;
697 }
698 
699 }
700