xref: /trunk/main/sfx2/source/appl/linkmgr2.cxx (revision f1d3244ef953db9afc1728bb7d37a8d1a9c2d8bc)
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 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_sfx2.hxx"
24 
25 #include <sfx2/linkmgr.hxx>
26 #include <com/sun/star/document/UpdateDocMode.hpp>
27 #include <sfx2/objsh.hxx>
28 #include <svl/urihelper.hxx>
29 #include <sot/formats.hxx>
30 #include <tools/urlobj.hxx>
31 #include <sot/exchange.hxx>
32 #include <tools/debug.hxx>
33 #include <vcl/msgbox.hxx>
34 #include <sfx2/lnkbase.hxx>
35 #include <sfx2/app.hxx>
36 #include <vcl/graph.hxx>
37 #include <svl/stritem.hxx>
38 #include <svl/eitem.hxx>
39 #include <svl/intitem.hxx>
40 #include <unotools/localfilehelper.hxx>
41 #include <i18npool/mslangid.hxx>
42 #include <sfx2/request.hxx>
43 #include <vcl/dibtools.hxx>
44 
45 #include "fileobj.hxx"
46 #include "impldde.hxx"
47 #include "app.hrc"
48 #include "sfx2/sfxresid.hxx"
49 
50 #define _SVSTDARR_STRINGSDTOR
51 #include <svl/svstdarr.hxx>
52 
53 namespace sfx2
54 {
55 
56 class SvxInternalLink : public sfx2::SvLinkSource
57 {
58 public:
59     SvxInternalLink() {}
60 
61     virtual sal_Bool Connect( sfx2::SvBaseLink* );
62 };
63 
64 
65 SV_IMPL_PTRARR( SvBaseLinks, SvBaseLinkRefPtr )
66 
67 LinkManager::LinkManager(SfxObjectShell* p)
68     : pPersist(p),
69     mUpdateAsked(sal_False),
70     mAutoAskUpdateAllLinks(sal_False)
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 |*    Description
93 *************************************************************************/
94 
95 void LinkManager::Remove( SvBaseLink *pLink )
96 {
97     // do not insert links double
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         // if there are still some empty ones, get rid of them
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 
148     // do not insert links double
149     for( sal_uInt16 n = 0; n < aLinkTbl.Count(); ++n )
150     {
151         SvBaseLinkRef* pTmp = aLinkTbl[ n ];
152         if( !pTmp->Is() )
153             aLinkTbl.DeleteAndDestroy( n-- );
154 
155         if( pLink == *pTmp )
156             return sal_False;
157     }
158 
159     SvBaseLinkRef* pTmp = new SvBaseLinkRef( pLink );
160     pLink->SetLinkManager( this );
161     aLinkTbl.Insert( pTmp, aLinkTbl.Count() );
162     Window *parent = GetPersist()->GetDialogParent();
163     if (mAutoAskUpdateAllLinks)
164     {
165         SetUserAllowsLinkUpdate(pLink, GetUserAllowsLinkUpdate(parent));
166     }
167 
168     return sal_True;
169 }
170 
171 
172 sal_Bool LinkManager::InsertLink( SvBaseLink * pLink,
173                                 sal_uInt16 nObjType,
174                                 sal_uInt16 nUpdateMode,
175                                 const String* pName )
176 {
177     // in any case: do this first
178     pLink->SetObjType( nObjType );
179     if( pName )
180         pLink->SetName( *pName );
181     pLink->SetUpdateMode( nUpdateMode );
182     return Insert( pLink );
183 }
184 
185 
186 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink,
187                                     const String& rServer,
188                                     const String& rTopic,
189                                     const String& rItem )
190 {
191     if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
192         return sal_False;
193 
194     String sCmd;
195     ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem );
196 
197     pLink->SetObjType( OBJECT_CLIENT_DDE );
198     pLink->SetName( sCmd );
199     return Insert( pLink );
200 }
201 
202 
203 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink )
204 {
205     DBG_ASSERT( OBJECT_CLIENT_SO & pLink->GetObjType(), "no OBJECT_CLIENT_SO" );
206     if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
207         return sal_False;
208 
209     if( pLink->GetObjType() == OBJECT_CLIENT_SO )
210         pLink->SetObjType( OBJECT_CLIENT_DDE );
211 
212     return Insert( pLink );
213 }
214 
215 
216 // ask for the strings to be used in the dialog
217 sal_Bool LinkManager::GetDisplayNames( const SvBaseLink * pLink,
218                                         String* pType,
219                                         String* pFile,
220                                         String* pLinkStr,
221                                         String* pFilter ) const
222 {
223     sal_Bool bRet = sal_False;
224     const String sLNm( pLink->GetLinkSourceName() );
225     if( sLNm.Len() )
226     {
227         switch( pLink->GetObjType() )
228         {
229             case OBJECT_CLIENT_FILE:
230             case OBJECT_CLIENT_GRF:
231             case OBJECT_CLIENT_OLE:
232                 {
233                     sal_uInt16 nPos = 0;
234                     String sFile( sLNm.GetToken( 0, ::sfx2::cTokenSeperator, nPos ) );
235                     String sRange( sLNm.GetToken( 0, ::sfx2::cTokenSeperator, nPos ) );
236 
237                     if( pFile )
238                         *pFile = sFile;
239                     if( pLinkStr )
240                         *pLinkStr = sRange;
241                     if( pFilter )
242                         *pFilter = sLNm.Copy( nPos );
243 
244                     if( pType )
245                     {
246                         sal_uInt16 nObjType = pLink->GetObjType();
247                         *pType = String( SfxResId(
248                                     ( OBJECT_CLIENT_FILE == nObjType || OBJECT_CLIENT_OLE == nObjType )
249                                             ? RID_SVXSTR_FILELINK
250                                             : RID_SVXSTR_GRAFIKLINK ));
251                     }
252                     bRet = sal_True;
253                 }
254                 break;
255             case OBJECT_CLIENT_DDE:
256                 {
257                     sal_uInt16 nTmp = 0;
258                     String sCmd( sLNm );
259                     String sServer( sCmd.GetToken( 0, cTokenSeperator, nTmp ) );
260                     String sTopic( sCmd.GetToken( 0, cTokenSeperator, nTmp ) );
261 
262                     if( pType )
263                         *pType = sServer;
264                     if( pFile )
265                         *pFile = sTopic;
266                     if( pLinkStr )
267                         *pLinkStr = sCmd.Copy( nTmp );
268                     bRet = sal_True;
269                 }
270                 break;
271             default:
272                 break;
273         }
274     }
275 
276     return bRet;
277 }
278 
279 void LinkManager::SetAutoAskUpdateAllLinks()
280 {
281     mAutoAskUpdateAllLinks = sal_True;
282 }
283 
284 sal_Bool LinkManager::GetUserAllowsLinkUpdate(Window *pParentWin)
285 {
286     if (!mUpdateAsked)
287     {
288         if (QueryBox(pParentWin, WB_YES_NO | WB_DEF_NO, SfxResId(STR_QUERY_UPDATE_LINKS)).Execute() == RET_YES)
289             mAllowUpdate = sal_True;
290         else
291             mAllowUpdate = sal_False;
292         mUpdateAsked = sal_True;
293     }
294     return mAllowUpdate;
295 }
296 
297 void LinkManager::SetUserAllowsLinkUpdate(SvBaseLink *pLink, sal_Bool allows)
298 {
299     SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
300 
301     if (pShell)
302     {
303         comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer();
304         rEmbeddedObjectContainer.setUserAllowsLinkUpdate(allows);
305     }
306 }
307 
308 
309 void LinkManager::UpdateAllLinks(
310     sal_Bool bAskUpdate,
311     sal_Bool /*bCallErrHdl*/,
312     sal_Bool bUpdateGrfLinks,
313     Window* pParentWin )
314 {
315     SvStringsDtor aApps, aTopics, aItems;
316     String sApp, sTopic, sItem;
317 
318     // first create a copy of the array, so that updated links to not interfere with ... in between!!
319     SvPtrarr aTmpArr( 255, 50 );
320     sal_uInt16 n;
321     for( n = 0; n < aLinkTbl.Count(); ++n )
322     {
323         SvBaseLink* pLink = *aLinkTbl[ n ];
324         if( !pLink )
325         {
326             Remove( n-- );
327             continue;
328         }
329         aTmpArr.Insert( pLink, aTmpArr.Count() );
330     }
331 
332     for( n = 0; n < aTmpArr.Count(); ++n )
333     {
334         SvBaseLink* pLink = (SvBaseLink*)aTmpArr[ n ];
335 
336         // first search the entry in the array
337         sal_uInt16 nFndPos = USHRT_MAX;
338         for( sal_uInt16 i = 0; i < aLinkTbl.Count(); ++i )
339             if( pLink == *aLinkTbl[ i ] )
340             {
341                 nFndPos = i;
342                 break;
343             }
344 
345         if( USHRT_MAX == nFndPos )
346             continue;                   // was not already existing!
347 
348         // do not update graphic links yet
349         if( !pLink->IsVisible() ||
350             ( !bUpdateGrfLinks && OBJECT_CLIENT_GRF == pLink->GetObjType() ))
351             continue;
352 
353         sal_Bool allows = sal_True;
354 
355         if (bAskUpdate)
356         {
357             allows = GetUserAllowsLinkUpdate(pParentWin);
358         }
359 
360         SetUserAllowsLinkUpdate(pLink, allows);
361         bAskUpdate = sal_False;     // one time is OK
362 
363         if (allows)
364             pLink->Update();
365     }
366 }
367 
368 /************************************************************************
369 |*    SvBaseLink::CreateObject()
370 |*
371 |*    Description
372 *************************************************************************/
373 
374 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink * pLink )
375 {
376     switch( pLink->GetObjType() )
377     {
378         case OBJECT_CLIENT_FILE:
379         case OBJECT_CLIENT_GRF:
380         case OBJECT_CLIENT_OLE:
381             return new SvFileObject;
382         case OBJECT_INTERN:
383             return new SvxInternalLink;
384         case OBJECT_CLIENT_DDE:
385             return new SvDDEObject;
386         default:
387             return SvLinkSourceRef();
388     }
389 }
390 
391 sal_Bool LinkManager::InsertServer( SvLinkSource* pObj )
392 {
393     // do not insert double
394     if( !pObj || USHRT_MAX != aServerTbl.GetPos( pObj ) )
395         return sal_False;
396 
397     aServerTbl.Insert( pObj, aServerTbl.Count() );
398     return sal_True;
399 }
400 
401 
402 void LinkManager::RemoveServer( SvLinkSource* pObj )
403 {
404     sal_uInt16 nPos = aServerTbl.GetPos( pObj );
405     if( USHRT_MAX != nPos )
406         aServerTbl.Remove( nPos, 1 );
407 }
408 
409 
410 void MakeLnkName( String& rName, const String* pType, const String& rFile,
411                     const String& rLink, const String* pFilter )
412 {
413     if( pType )
414         (rName = *pType).EraseLeadingChars().EraseTrailingChars() += cTokenSeperator;
415     else if( rName.Len() )
416         rName.Erase();
417 
418     ((rName += rFile).EraseLeadingChars().EraseTrailingChars() +=
419         cTokenSeperator ).EraseLeadingChars().EraseTrailingChars() += rLink;
420     if( pFilter )
421         ((rName += cTokenSeperator ) += *pFilter).EraseLeadingChars().EraseTrailingChars();
422 }
423 
424 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink,
425                                     sal_uInt16 nFileType,
426                                     const String& rFileNm,
427                                     const String* pFilterNm,
428                                     const String* pRange )
429 {
430     if( !( OBJECT_CLIENT_SO & rLink.GetObjType() ))
431         return sal_False;
432 
433     String sCmd( rFileNm );
434     sCmd += ::sfx2::cTokenSeperator;
435     if( pRange )
436         sCmd += *pRange;
437     if( pFilterNm )
438         ( sCmd += ::sfx2::cTokenSeperator ) += *pFilterNm;
439 
440     return InsertLink( &rLink, nFileType, sfx2::LINKUPDATE_ONCALL, &sCmd );
441 }
442 
443 sal_Bool LinkManager::InsertFileLink( sfx2::SvBaseLink& rLink )
444 {
445     if( OBJECT_CLIENT_FILE == ( OBJECT_CLIENT_FILE & rLink.GetObjType() ))
446         return InsertLink( &rLink, rLink.GetObjType(), sfx2::LINKUPDATE_ONCALL );
447     return sal_False;
448 }
449 
450 // a transfer will be discontinued, therefore cancel all DownloadMedia
451 // (at the moment only interesting for the FileLinks!)
452 void LinkManager::CancelTransfers()
453 {
454     SvFileObject* pFileObj;
455     sfx2::SvBaseLink* pLnk;
456 
457     const sfx2::SvBaseLinks& rLnks = GetLinks();
458     for( sal_uInt16 n = rLnks.Count(); n; )
459         if( 0 != ( pLnk = &(*rLnks[ --n ])) &&
460             OBJECT_CLIENT_FILE == (OBJECT_CLIENT_FILE & pLnk->GetObjType()) &&
461             0 != ( pFileObj = (SvFileObject*)pLnk->GetObj() ) )
462 //          0 != ( pFileObj = (SvFileObject*)SvFileObject::ClassFactory()->
463 //                                  CastAndAddRef( pLnk->GetObj() )) )
464             pFileObj->CancelTransfers();
465 }
466 
467     // to send status information from the FileObject to the BaseLink, there is an own ClipboardId.
468     // The SvData object has then the respective information as string.
469     // Currently this will be used for FileObject in connection with JavaScript
470     // - that needs information about Load/Abort/Error
471 sal_uIntPtr LinkManager::RegisterStatusInfoId()
472 {
473     static sal_uIntPtr nFormat = 0;
474 
475     if( !nFormat )
476     {
477 // how does the new interface look like?
478 //      nFormat = Exchange::RegisterFormatName( "StatusInfo vom SvxInternalLink" );
479         nFormat = SotExchange::RegisterFormatName(
480                     String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM(
481                                 "StatusInfo vom SvxInternalLink" )));
482     }
483     return nFormat;
484 }
485 
486 // ----------------------------------------------------------------------
487 
488 sal_Bool LinkManager::GetGraphicFromAny( const String& rMimeType,
489                                 const ::com::sun::star::uno::Any & rValue,
490                                 Graphic& rGrf )
491 {
492     sal_Bool bRet = sal_False;
493     ::com::sun::star::uno::Sequence< sal_Int8 > aSeq;
494     if( rValue.hasValue() && ( rValue >>= aSeq ) )
495     {
496         SvMemoryStream aMemStm( (void*)aSeq.getConstArray(), aSeq.getLength(),
497                                 STREAM_READ );
498         aMemStm.Seek( 0 );
499 
500         switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
501         {
502         case SOT_FORMATSTR_ID_SVXB:
503             {
504                 aMemStm >> rGrf;
505                 bRet = sal_True;
506             }
507             break;
508         case FORMAT_GDIMETAFILE:
509             {
510                 GDIMetaFile aMtf;
511                 aMtf.Read( aMemStm );
512                 rGrf = aMtf;
513                 bRet = sal_True;
514             }
515             break;
516         case FORMAT_BITMAP:
517             {
518                 Bitmap aBmp;
519                 ReadDIB(aBmp, aMemStm, true);
520                 rGrf = aBmp;
521                 bRet = sal_True;
522             }
523             break;
524         }
525     }
526     return bRet;
527 }
528 
529 
530 // ----------------------------------------------------------------------
531 String lcl_DDE_RelToAbs( const String& rTopic, const String& rBaseURL )
532 {
533     String sRet;
534     INetURLObject aURL( rTopic );
535     if( INET_PROT_NOT_VALID == aURL.GetProtocol() )
536         utl::LocalFileHelper::ConvertSystemPathToURL( rTopic, rBaseURL, sRet );
537     if( !sRet.Len() )
538         sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl(), true );
539     return sRet;
540 }
541 
542 sal_Bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink )
543 {
544     SfxObjectShell* pFndShell = 0;
545     sal_uInt16 nUpdateMode = com::sun::star::document::UpdateDocMode::NO_UPDATE;
546     String sTopic, sItem, sReferer;
547     if( pLink->GetLinkManager() &&
548         pLink->GetLinkManager()->GetDisplayNames( pLink, 0, &sTopic, &sItem )
549         && sTopic.Len() )
550     {
551         // for the moment run through the DocumentShells and search for the ones with names:
552 
553         com::sun::star::lang::Locale aLocale;
554         MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aLocale );
555         CharClass aCC( aLocale );
556 
557         String sNm( sTopic ), sTmp;
558         aCC.toLower( sNm );
559 
560         TypeId aType( TYPE(SfxObjectShell) );
561 
562         sal_Bool bFirst = sal_True;
563         SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
564         if( pShell && pShell->GetMedium() )
565         {
566             sReferer = pShell->GetMedium()->GetBaseURL();
567             SFX_ITEMSET_ARG( pShell->GetMedium()->GetItemSet(), pItem, SfxUInt16Item, SID_UPDATEDOCMODE, sal_False );
568             if ( pItem )
569                 nUpdateMode = pItem->GetValue();
570         }
571 
572         String sNmURL( lcl_DDE_RelToAbs( sTopic, sReferer ) );
573         aCC.toLower( sNmURL );
574 
575         if ( !pShell )
576         {
577             bFirst = sal_False;
578             pShell = SfxObjectShell::GetFirst( &aType, sal_False );
579         }
580 
581         while( pShell )
582         {
583             if( !sTmp.Len() )
584             {
585                 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME );
586                 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer );
587             }
588 
589 
590             aCC.toLower( sTmp );
591             if( sTmp == sNmURL )        // these we want to have
592             {
593                 pFndShell = pShell;
594                 break;
595             }
596 
597             if( bFirst )
598             {
599                 bFirst = sal_False;
600                 pShell = SfxObjectShell::GetFirst( &aType, sal_False );
601             }
602             else
603                 pShell = SfxObjectShell::GetNext( *pShell, &aType, sal_False );
604 
605             sTmp.Erase();
606         }
607     }
608 
609     // empty topics are not allowed - which document is it
610     if( !sTopic.Len() )
611         return sal_False;
612 
613     if( !pFndShell )
614     {
615         // try to load the file:
616         INetURLObject aURL( sTopic );
617         INetProtocol eOld = aURL.GetProtocol();
618         aURL.SetURL( sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ) );
619         if( INET_PROT_NOT_VALID != eOld ||
620             INET_PROT_HTTP != aURL.GetProtocol() )
621         {
622             SfxStringItem aName( SID_FILE_NAME, sTopic );
623             SfxBoolItem aMinimized(SID_MINIMIZED, sal_True);
624             SfxBoolItem aHidden(SID_HIDDEN, sal_True);
625             SfxStringItem aTarget( SID_TARGETNAME, String::CreateFromAscii("_blank") );
626             SfxStringItem aReferer( SID_REFERER, sReferer );
627             SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode );
628             SfxBoolItem aReadOnly(SID_DOC_READONLY, sal_True);
629 
630             // #i14200# (DDE-link crashes wordprocessor)
631             SfxAllItemSet aArgs( SFX_APP()->GetPool() );
632             aArgs.Put(aReferer);
633             aArgs.Put(aTarget);
634             aArgs.Put(aHidden);
635             aArgs.Put(aMinimized);
636             aArgs.Put(aName);
637             aArgs.Put(aUpdate);
638             aArgs.Put(aReadOnly);
639             pFndShell = SfxObjectShell::CreateAndLoadObject( aArgs );
640         }
641     }
642 
643     sal_Bool bRet = sal_False;
644     if( pFndShell )
645     {
646         sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem );
647         if( pNewSrc )
648         {
649             bRet = sal_True;
650 
651             ::com::sun::star::datatransfer::DataFlavor aFl;
652             SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl );
653 
654             pLink->SetObj( pNewSrc );
655             pNewSrc->AddDataAdvise( pLink, aFl.MimeType,
656                                 sfx2::LINKUPDATE_ONCALL == pLink->GetUpdateMode()
657                                     ? ADVISEMODE_ONLYONCE
658                                     : 0 );
659         }
660     }
661     return bRet;
662 }
663 
664 
665 }
666 
667