xref: /AOO41X/main/sfx2/source/appl/linkmgr2.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 #include <sfx2/linkmgr.hxx>
28 #include <com/sun/star/document/UpdateDocMode.hpp>
29 #include <sfx2/objsh.hxx>
30 #include <svl/urihelper.hxx>
31 #include <sot/formats.hxx>
32 #include <tools/urlobj.hxx>
33 #include <sot/exchange.hxx>
34 #include <tools/debug.hxx>
35 #include <vcl/msgbox.hxx>
36 #include <sfx2/lnkbase.hxx>
37 #include <sfx2/app.hxx>
38 #include <vcl/graph.hxx>
39 #include <svl/stritem.hxx>
40 #include <svl/eitem.hxx>
41 #include <svl/intitem.hxx>
42 #include <unotools/localfilehelper.hxx>
43 #include <i18npool/mslangid.hxx>
44 #include <sfx2/request.hxx>
45 
46 #include "fileobj.hxx"
47 #include "impldde.hxx"
48 #include "app.hrc"
49 #include "sfx2/sfxresid.hxx"
50 
51 #define _SVSTDARR_STRINGSDTOR
52 #include <svl/svstdarr.hxx>
53 
54 namespace sfx2
55 {
56 
57 class SvxInternalLink : public sfx2::SvLinkSource
58 {
59 public:
60     SvxInternalLink() {}
61 
62     virtual sal_Bool Connect( sfx2::SvBaseLink* );
63 };
64 
65 
66 SV_IMPL_PTRARR( SvBaseLinks, SvBaseLinkRefPtr )
67 
68 LinkManager::LinkManager(SfxObjectShell* p)
69     : pPersist( p )
70 {
71 }
72 
73 
74 LinkManager::~LinkManager()
75 {
76     SvBaseLinkRef** ppRef = (SvBaseLinkRef**)aLinkTbl.GetData();
77     for( sal_uInt16 n = aLinkTbl.Count(); n; --n, ++ppRef )
78     {
79         if( (*ppRef)->Is() )
80         {
81             (*(*ppRef))->Disconnect();
82             (*(*ppRef))->SetLinkManager( NULL );
83         }
84         delete *ppRef;
85     }
86 }
87 
88 
89 /************************************************************************
90 |*    LinkManager::Remove()
91 |*
92 |*    Beschreibung
93 *************************************************************************/
94 
95 void LinkManager::Remove( SvBaseLink *pLink )
96 {
97     // keine Links doppelt einfuegen
98     int bFound = sal_False;
99     SvBaseLinkRef** ppRef = (SvBaseLinkRef**)aLinkTbl.GetData();
100     for( sal_uInt16 n = aLinkTbl.Count(); n; --n, ++ppRef )
101     {
102         if( pLink == *(*ppRef) )
103         {
104             (*(*ppRef))->Disconnect();
105             (*(*ppRef))->SetLinkManager( NULL );
106             (*(*ppRef)).Clear();
107             bFound = sal_True;
108         }
109 
110         // falls noch leere rum stehen sollten, weg damit
111         if( !(*ppRef)->Is() )
112         {
113             delete *ppRef;
114             aLinkTbl.Remove( aLinkTbl.Count() - n, 1 );
115             if( bFound )
116                 return ;
117             --ppRef;
118         }
119     }
120 }
121 
122 
123 void LinkManager::Remove( sal_uInt16 nPos, sal_uInt16 nCnt )
124 {
125     if( nCnt && nPos < aLinkTbl.Count() )
126     {
127         if( nPos + nCnt > aLinkTbl.Count() )
128             nCnt = aLinkTbl.Count() - nPos;
129 
130         SvBaseLinkRef** ppRef = (SvBaseLinkRef**)aLinkTbl.GetData() + nPos;
131         for( sal_uInt16 n = nCnt; n; --n, ++ppRef )
132         {
133             if( (*ppRef)->Is() )
134             {
135                 (*(*ppRef))->Disconnect();
136                 (*(*ppRef))->SetLinkManager( NULL );
137             }
138             delete *ppRef;
139         }
140         aLinkTbl.Remove( nPos, nCnt );
141     }
142 }
143 
144 
145 sal_Bool LinkManager::Insert( SvBaseLink* pLink )
146 {
147     // keine Links doppelt einfuegen
148     for( sal_uInt16 n = 0; n < aLinkTbl.Count(); ++n )
149     {
150         SvBaseLinkRef* pTmp = aLinkTbl[ n ];
151         if( !pTmp->Is() )
152             aLinkTbl.DeleteAndDestroy( n-- );
153 
154         if( pLink == *pTmp )
155             return sal_False;
156     }
157 
158     SvBaseLinkRef* pTmp = new SvBaseLinkRef( pLink );
159     pLink->SetLinkManager( this );
160     aLinkTbl.Insert( pTmp, aLinkTbl.Count() );
161     return sal_True;
162 }
163 
164 
165 sal_Bool LinkManager::InsertLink( SvBaseLink * pLink,
166                                 sal_uInt16 nObjType,
167                                 sal_uInt16 nUpdateMode,
168                                 const String* pName )
169 {
170     // unbedingt zuerst
171     pLink->SetObjType( nObjType );
172     if( pName )
173         pLink->SetName( *pName );
174     pLink->SetUpdateMode( nUpdateMode );
175     return Insert( pLink );
176 }
177 
178 
179 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink,
180                                     const String& rServer,
181                                     const String& rTopic,
182                                     const String& rItem )
183 {
184     if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
185         return sal_False;
186 
187     String sCmd;
188     ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem );
189 
190     pLink->SetObjType( OBJECT_CLIENT_DDE );
191     pLink->SetName( sCmd );
192     return Insert( pLink );
193 }
194 
195 
196 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink )
197 {
198     DBG_ASSERT( OBJECT_CLIENT_SO & pLink->GetObjType(), "no OBJECT_CLIENT_SO" );
199     if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
200         return sal_False;
201 
202     if( pLink->GetObjType() == OBJECT_CLIENT_SO )
203         pLink->SetObjType( OBJECT_CLIENT_DDE );
204 
205     return Insert( pLink );
206 }
207 
208 
209 // erfrage die Strings fuer den Dialog
210 sal_Bool LinkManager::GetDisplayNames( const SvBaseLink * pLink,
211                                         String* pType,
212                                         String* pFile,
213                                         String* pLinkStr,
214                                         String* pFilter ) const
215 {
216     sal_Bool bRet = sal_False;
217     const String sLNm( pLink->GetLinkSourceName() );
218     if( sLNm.Len() )
219     {
220         switch( pLink->GetObjType() )
221         {
222             case OBJECT_CLIENT_FILE:
223             case OBJECT_CLIENT_GRF:
224             case OBJECT_CLIENT_OLE:
225                 {
226                     sal_uInt16 nPos = 0;
227                     String sFile( sLNm.GetToken( 0, ::sfx2::cTokenSeperator, nPos ) );
228                     String sRange( sLNm.GetToken( 0, ::sfx2::cTokenSeperator, nPos ) );
229 
230                     if( pFile )
231                         *pFile = sFile;
232                     if( pLinkStr )
233                         *pLinkStr = sRange;
234                     if( pFilter )
235                         *pFilter = sLNm.Copy( nPos );
236 
237                     if( pType )
238                     {
239                         sal_uInt16 nObjType = pLink->GetObjType();
240                         *pType = String( SfxResId(
241                                     ( OBJECT_CLIENT_FILE == nObjType || OBJECT_CLIENT_OLE == nObjType )
242                                             ? RID_SVXSTR_FILELINK
243                                             : RID_SVXSTR_GRAFIKLINK ));
244                     }
245                     bRet = sal_True;
246                 }
247                 break;
248             case OBJECT_CLIENT_DDE:
249                 {
250                     sal_uInt16 nTmp = 0;
251                     String sCmd( sLNm );
252                     String sServer( sCmd.GetToken( 0, cTokenSeperator, nTmp ) );
253                     String sTopic( sCmd.GetToken( 0, cTokenSeperator, nTmp ) );
254 
255                     if( pType )
256                         *pType = sServer;
257                     if( pFile )
258                         *pFile = sTopic;
259                     if( pLinkStr )
260                         *pLinkStr = sCmd.Copy( nTmp );
261                     bRet = sal_True;
262                 }
263                 break;
264             default:
265                 break;
266         }
267     }
268 
269     return bRet;
270 }
271 
272 
273 void LinkManager::UpdateAllLinks(
274     sal_Bool bAskUpdate,
275     sal_Bool /*bCallErrHdl*/,
276     sal_Bool bUpdateGrfLinks,
277     Window* pParentWin )
278 {
279     SvStringsDtor aApps, aTopics, aItems;
280     String sApp, sTopic, sItem;
281 
282     // erstmal eine Kopie vom Array machen, damit sich updatende Links in
283     // Links in ... nicht dazwischen funken!!
284     SvPtrarr aTmpArr( 255, 50 );
285     sal_uInt16 n;
286     for( n = 0; n < aLinkTbl.Count(); ++n )
287     {
288         SvBaseLink* pLink = *aLinkTbl[ n ];
289         if( !pLink )
290         {
291             Remove( n-- );
292             continue;
293         }
294         aTmpArr.Insert( pLink, aTmpArr.Count() );
295     }
296 
297     for( n = 0; n < aTmpArr.Count(); ++n )
298     {
299         SvBaseLink* pLink = (SvBaseLink*)aTmpArr[ n ];
300 
301         // suche erstmal im Array nach dem Eintrag
302         sal_uInt16 nFndPos = USHRT_MAX;
303         for( sal_uInt16 i = 0; i < aLinkTbl.Count(); ++i )
304             if( pLink == *aLinkTbl[ i ] )
305             {
306                 nFndPos = i;
307                 break;
308             }
309 
310         if( USHRT_MAX == nFndPos )
311             continue;                   // war noch nicht vorhanden!
312 
313         // Graphic-Links noch nicht updaten
314         if( !pLink->IsVisible() ||
315             ( !bUpdateGrfLinks && OBJECT_CLIENT_GRF == pLink->GetObjType() ))
316             continue;
317 
318         if( bAskUpdate )
319         {
320             int nRet = QueryBox( pParentWin, WB_YES_NO | WB_DEF_YES, SfxResId( STR_QUERY_UPDATE_LINKS ) ).Execute();
321             if( RET_YES != nRet )
322                 return ;        // es soll nichts geupdatet werden
323             bAskUpdate = sal_False;     // einmal reicht
324         }
325 
326         pLink->Update();
327     }
328 }
329 
330 /************************************************************************
331 |*    SvBaseLink::CreateObject()
332 |*
333 |*    Beschreibung
334 *************************************************************************/
335 
336 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink * pLink )
337 {
338     switch( pLink->GetObjType() )
339     {
340         case OBJECT_CLIENT_FILE:
341         case OBJECT_CLIENT_GRF:
342         case OBJECT_CLIENT_OLE:
343             return new SvFileObject;
344         case OBJECT_INTERN:
345             return new SvxInternalLink;
346         case OBJECT_CLIENT_DDE:
347             return new SvDDEObject;
348         default:
349             return SvLinkSourceRef();
350     }
351 }
352 
353 sal_Bool LinkManager::InsertServer( SvLinkSource* pObj )
354 {
355     // keine doppelt einfuegen
356     if( !pObj || USHRT_MAX != aServerTbl.GetPos( pObj ) )
357         return sal_False;
358 
359     aServerTbl.Insert( pObj, aServerTbl.Count() );
360     return sal_True;
361 }
362 
363 
364 void LinkManager::RemoveServer( SvLinkSource* pObj )
365 {
366     sal_uInt16 nPos = aServerTbl.GetPos( pObj );
367     if( USHRT_MAX != nPos )
368         aServerTbl.Remove( nPos, 1 );
369 }
370 
371 
372 void MakeLnkName( String& rName, const String* pType, const String& rFile,
373                     const String& rLink, const String* pFilter )
374 {
375     if( pType )
376         (rName = *pType).EraseLeadingChars().EraseTrailingChars() += cTokenSeperator;
377     else if( rName.Len() )
378         rName.Erase();
379 
380     ((rName += rFile).EraseLeadingChars().EraseTrailingChars() +=
381         cTokenSeperator ).EraseLeadingChars().EraseTrailingChars() += rLink;
382     if( pFilter )
383         ((rName += cTokenSeperator ) += *pFilter).EraseLeadingChars().EraseTrailingChars();
384 }
385 
386 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink,
387                                     sal_uInt16 nFileType,
388                                     const String& rFileNm,
389                                     const String* pFilterNm,
390                                     const String* pRange )
391 {
392     if( !( OBJECT_CLIENT_SO & rLink.GetObjType() ))
393         return sal_False;
394 
395     String sCmd( rFileNm );
396     sCmd += ::sfx2::cTokenSeperator;
397     if( pRange )
398         sCmd += *pRange;
399     if( pFilterNm )
400         ( sCmd += ::sfx2::cTokenSeperator ) += *pFilterNm;
401 
402     return InsertLink( &rLink, nFileType, sfx2::LINKUPDATE_ONCALL, &sCmd );
403 }
404 
405 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink )
406 {
407     if( OBJECT_CLIENT_FILE == ( OBJECT_CLIENT_FILE & rLink.GetObjType() ))
408         return InsertLink( &rLink, rLink.GetObjType(), sfx2::LINKUPDATE_ONCALL );
409     return sal_False;
410 }
411 
412 // eine Uebertragung wird abgebrochen, also alle DownloadMedien canceln
413 // (ist zur Zeit nur fuer die FileLinks interressant!)
414 void LinkManager::CancelTransfers()
415 {
416     SvFileObject* pFileObj;
417     sfx2::SvBaseLink* pLnk;
418 
419     const sfx2::SvBaseLinks& rLnks = GetLinks();
420     for( sal_uInt16 n = rLnks.Count(); n; )
421         if( 0 != ( pLnk = &(*rLnks[ --n ])) &&
422             OBJECT_CLIENT_FILE == (OBJECT_CLIENT_FILE & pLnk->GetObjType()) &&
423             0 != ( pFileObj = (SvFileObject*)pLnk->GetObj() ) )
424 //          0 != ( pFileObj = (SvFileObject*)SvFileObject::ClassFactory()->
425 //                                  CastAndAddRef( pLnk->GetObj() )) )
426             pFileObj->CancelTransfers();
427 }
428 
429     // um Status Informationen aus dem FileObject an den BaseLink zu
430     // senden, gibt es eine eigene ClipBoardId. Das SvData-Object hat
431     // dann die entsprechenden Informationen als String.
432     // Wird zur Zeit fuer FileObject in Verbindung mit JavaScript benoetigt
433     // - das braucht Informationen ueber Load/Abort/Error
434 sal_uIntPtr LinkManager::RegisterStatusInfoId()
435 {
436     static sal_uIntPtr nFormat = 0;
437 
438     if( !nFormat )
439     {
440 // wie sieht die neue Schnittstelle aus?
441 //      nFormat = Exchange::RegisterFormatName( "StatusInfo vom SvxInternalLink" );
442         nFormat = SotExchange::RegisterFormatName(
443                     String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM(
444                                 "StatusInfo vom SvxInternalLink" )));
445     }
446     return nFormat;
447 }
448 
449 // ----------------------------------------------------------------------
450 
451 sal_Bool LinkManager::GetGraphicFromAny( const String& rMimeType,
452                                 const ::com::sun::star::uno::Any & rValue,
453                                 Graphic& rGrf )
454 {
455     sal_Bool bRet = sal_False;
456     ::com::sun::star::uno::Sequence< sal_Int8 > aSeq;
457     if( rValue.hasValue() && ( rValue >>= aSeq ) )
458     {
459         SvMemoryStream aMemStm( (void*)aSeq.getConstArray(), aSeq.getLength(),
460                                 STREAM_READ );
461         aMemStm.Seek( 0 );
462 
463         switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
464         {
465         case SOT_FORMATSTR_ID_SVXB:
466             {
467                 aMemStm >> rGrf;
468                 bRet = sal_True;
469             }
470             break;
471         case FORMAT_GDIMETAFILE:
472             {
473                 GDIMetaFile aMtf;
474                 aMtf.Read( aMemStm );
475                 rGrf = aMtf;
476                 bRet = sal_True;
477             }
478             break;
479         case FORMAT_BITMAP:
480             {
481                 Bitmap aBmp;
482                 aMemStm >> aBmp;
483                 rGrf = aBmp;
484                 bRet = sal_True;
485             }
486             break;
487         }
488     }
489     return bRet;
490 }
491 
492 
493 // ----------------------------------------------------------------------
494 String lcl_DDE_RelToAbs( const String& rTopic, const String& rBaseURL )
495 {
496     String sRet;
497     INetURLObject aURL( rTopic );
498     if( INET_PROT_NOT_VALID == aURL.GetProtocol() )
499         utl::LocalFileHelper::ConvertSystemPathToURL( rTopic, rBaseURL, sRet );
500     if( !sRet.Len() )
501         sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl(), true );
502     return sRet;
503 }
504 
505 sal_Bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink )
506 {
507     SfxObjectShell* pFndShell = 0;
508     sal_uInt16 nUpdateMode = com::sun::star::document::UpdateDocMode::NO_UPDATE;
509     String sTopic, sItem, sReferer;
510     if( pLink->GetLinkManager() &&
511         pLink->GetLinkManager()->GetDisplayNames( pLink, 0, &sTopic, &sItem )
512         && sTopic.Len() )
513     {
514         // erstmal nur ueber die DocumentShells laufen und die mit dem
515         // Namen heraussuchen:
516 
517         com::sun::star::lang::Locale aLocale;
518         MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aLocale );
519         CharClass aCC( aLocale );
520 
521         String sNm( sTopic ), sTmp;
522         aCC.toLower( sNm );
523 
524         TypeId aType( TYPE(SfxObjectShell) );
525 
526         sal_Bool bFirst = sal_True;
527         SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
528         if( pShell && pShell->GetMedium() )
529         {
530             sReferer = pShell->GetMedium()->GetBaseURL();
531             SFX_ITEMSET_ARG( pShell->GetMedium()->GetItemSet(), pItem, SfxUInt16Item, SID_UPDATEDOCMODE, sal_False );
532             if ( pItem )
533                 nUpdateMode = pItem->GetValue();
534         }
535 
536         String sNmURL( lcl_DDE_RelToAbs( sTopic, sReferer ) );
537         aCC.toLower( sNmURL );
538 
539         if ( !pShell )
540         {
541             bFirst = sal_False;
542             pShell = SfxObjectShell::GetFirst( &aType, sal_False );
543         }
544 
545         while( pShell )
546         {
547             if( !sTmp.Len() )
548             {
549                 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME );
550                 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer );
551             }
552 
553 
554             aCC.toLower( sTmp );
555             if( sTmp == sNmURL )        // die wollen wir haben
556             {
557                 pFndShell = pShell;
558                 break;
559             }
560 
561             if( bFirst )
562             {
563                 bFirst = sal_False;
564                 pShell = SfxObjectShell::GetFirst( &aType, sal_False );
565             }
566             else
567                 pShell = SfxObjectShell::GetNext( *pShell, &aType, sal_False );
568 
569             sTmp.Erase();
570         }
571     }
572 
573     // empty topics are not allowed - which document is it
574     if( !sTopic.Len() )
575         return sal_False;
576 
577     if( !pFndShell )
578     {
579         // dann versuche die Datei zu laden:
580         INetURLObject aURL( sTopic );
581         INetProtocol eOld = aURL.GetProtocol();
582         aURL.SetURL( sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ) );
583         if( INET_PROT_NOT_VALID != eOld ||
584             INET_PROT_HTTP != aURL.GetProtocol() )
585         {
586             SfxStringItem aName( SID_FILE_NAME, sTopic );
587             SfxBoolItem aMinimized(SID_MINIMIZED, sal_True);
588             SfxBoolItem aHidden(SID_HIDDEN, sal_True);
589             SfxStringItem aTarget( SID_TARGETNAME, String::CreateFromAscii("_blank") );
590             SfxStringItem aReferer( SID_REFERER, sReferer );
591             SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode );
592             SfxBoolItem aReadOnly(SID_DOC_READONLY, sal_True);
593 
594             // #i14200# (DDE-link crashes wordprocessor)
595             SfxAllItemSet aArgs( SFX_APP()->GetPool() );
596             aArgs.Put(aReferer);
597             aArgs.Put(aTarget);
598             aArgs.Put(aHidden);
599             aArgs.Put(aMinimized);
600             aArgs.Put(aName);
601             aArgs.Put(aUpdate);
602             aArgs.Put(aReadOnly);
603             pFndShell = SfxObjectShell::CreateAndLoadObject( aArgs );
604         }
605     }
606 
607     sal_Bool bRet = sal_False;
608     if( pFndShell )
609     {
610         sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem );
611         if( pNewSrc )
612         {
613             bRet = sal_True;
614 
615             ::com::sun::star::datatransfer::DataFlavor aFl;
616             SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl );
617 
618             pLink->SetObj( pNewSrc );
619             pNewSrc->AddDataAdvise( pLink, aFl.MimeType,
620                                 sfx2::LINKUPDATE_ONCALL == pLink->GetUpdateMode()
621                                     ? ADVISEMODE_ONLYONCE
622                                     : 0 );
623         }
624     }
625     return bRet;
626 }
627 
628 
629 }
630 
631 
632 
633