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