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