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