bump product version to 4.2.0.1
[LibreOffice.git] / sfx2 / source / appl / linkmgr2.cxx
blobe1e460616a577ca8742e4e4b7041eb093d8ba7a4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/string.hxx>
21 #include <sfx2/linkmgr.hxx>
22 #include <com/sun/star/document/UpdateDocMode.hpp>
23 #include <sfx2/objsh.hxx>
24 #include <svl/urihelper.hxx>
25 #include <sot/formats.hxx>
26 #include <tools/urlobj.hxx>
27 #include <sot/exchange.hxx>
28 #include <tools/debug.hxx>
29 #include <vcl/msgbox.hxx>
30 #include <sfx2/lnkbase.hxx>
31 #include <sfx2/app.hxx>
32 #include <vcl/graph.hxx>
33 #include <svl/stritem.hxx>
34 #include <svl/eitem.hxx>
35 #include <svl/intitem.hxx>
36 #include <unotools/localfilehelper.hxx>
37 #include <i18nlangtag/languagetag.hxx>
38 #include <sfx2/request.hxx>
39 #include <vcl/dibtools.hxx>
41 #include "fileobj.hxx"
42 #include "impldde.hxx"
43 #include "app.hrc"
44 #include <sfx2/sfxresid.hxx>
46 #include <com/sun/star/lang/XComponent.hpp>
47 #include <com/sun/star/util/XCloseable.hpp>
49 using ::com::sun::star::uno::UNO_QUERY;
50 using ::com::sun::star::uno::Reference;
51 using ::com::sun::star::lang::XComponent;
52 using ::com::sun::star::util::XCloseable;
54 namespace sfx2
57 class SvxInternalLink : public sfx2::SvLinkSource
59 public:
60 SvxInternalLink() {}
62 virtual sal_Bool Connect( sfx2::SvBaseLink* );
66 LinkManager::LinkManager(SfxObjectShell* p)
67 : pPersist( p )
72 LinkManager::~LinkManager()
74 for( size_t n = 0; n < aLinkTbl.size(); ++n)
76 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
77 if( pTmp->Is() )
79 (*pTmp)->Disconnect();
80 (*pTmp)->SetLinkManager( NULL );
82 delete pTmp;
86 void LinkManager::InsertCachedComp(const Reference<XComponent>& xComp)
88 maCachedComps.push_back(xComp);
91 void LinkManager::CloseCachedComps()
93 CompVector::iterator itr = maCachedComps.begin(), itrEnd = maCachedComps.end();
94 for (; itr != itrEnd; ++itr)
96 Reference<XCloseable> xCloseable(*itr, UNO_QUERY);
97 if (!xCloseable.is())
98 continue;
100 xCloseable->close(true);
102 maCachedComps.clear();
105 //--------------------------------------------------------------------------
107 void LinkManager::Remove( SvBaseLink *pLink )
109 // No duplicate links inserted
110 int bFound = sal_False;
111 for( size_t n = 0; n < aLinkTbl.size(); )
113 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
114 if( pLink == *pTmp )
116 (*pTmp)->Disconnect();
117 (*pTmp)->SetLinkManager( NULL );
118 (*pTmp).Clear();
119 bFound = sal_True;
122 // Remove empty ones if they exist
123 if( !pTmp->Is() )
125 delete pTmp;
126 aLinkTbl.erase( aLinkTbl.begin() + n );
127 if( bFound )
128 return ;
130 else
131 ++n;
136 void LinkManager::Remove( size_t nPos, size_t nCnt )
138 if( nCnt && nPos < aLinkTbl.size() )
140 if (sal::static_int_cast<size_t>(nPos + nCnt) > aLinkTbl.size())
141 nCnt = aLinkTbl.size() - nPos;
143 for( size_t n = nPos; n < nPos + nCnt; ++n)
145 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
146 if( pTmp->Is() )
148 (*pTmp)->Disconnect();
149 (*pTmp)->SetLinkManager( NULL );
151 delete pTmp;
153 aLinkTbl.erase( aLinkTbl.begin() + nPos, aLinkTbl.begin() + nPos + nCnt );
158 sal_Bool LinkManager::Insert( SvBaseLink* pLink )
160 for( size_t n = 0; n < aLinkTbl.size(); ++n )
162 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
163 if( !pTmp->Is() )
165 delete pTmp;
166 aLinkTbl.erase( aLinkTbl.begin() + n-- );
168 else if( pLink == *pTmp )
169 return sal_False; // No duplicate links inserted
172 SvBaseLinkRef* pTmp = new SvBaseLinkRef( pLink );
173 pLink->SetLinkManager( this );
174 aLinkTbl.push_back( pTmp );
175 return sal_True;
179 sal_Bool LinkManager::InsertLink( SvBaseLink * pLink,
180 sal_uInt16 nObjType,
181 sal_uInt16 nUpdateMode,
182 const OUString* pName )
184 // This First
185 pLink->SetObjType( nObjType );
186 if( pName )
187 pLink->SetName( *pName );
188 pLink->SetUpdateMode( nUpdateMode );
189 return Insert( pLink );
193 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink,
194 const OUString& rServer,
195 const OUString& rTopic,
196 const OUString& rItem )
198 if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
199 return sal_False;
201 OUString sCmd;
202 ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem );
204 pLink->SetObjType( OBJECT_CLIENT_DDE );
205 pLink->SetName( sCmd );
206 return Insert( pLink );
210 sal_Bool LinkManager::InsertDDELink( SvBaseLink * pLink )
212 DBG_ASSERT( OBJECT_CLIENT_SO & pLink->GetObjType(), "no OBJECT_CLIENT_SO" );
213 if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
214 return sal_False;
216 if( pLink->GetObjType() == OBJECT_CLIENT_SO )
217 pLink->SetObjType( OBJECT_CLIENT_DDE );
219 return Insert( pLink );
223 // Obtain the string for the dialog
224 bool LinkManager::GetDisplayNames( const SvBaseLink * pLink,
225 OUString* pType,
226 OUString* pFile,
227 OUString* pLinkStr,
228 OUString* pFilter ) const
230 bool bRet = false;
231 const OUString sLNm( pLink->GetLinkSourceName() );
232 if( !sLNm.isEmpty() )
234 switch( pLink->GetObjType() )
236 case OBJECT_CLIENT_FILE:
237 case OBJECT_CLIENT_GRF:
238 case OBJECT_CLIENT_OLE:
240 sal_Int32 nPos = 0;
241 OUString sFile( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
242 OUString sRange( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
244 if( pFile )
245 *pFile = sFile;
246 if( pLinkStr )
247 *pLinkStr = sRange;
248 if( pFilter )
249 *pFilter = nPos == -1 ? OUString() : sLNm.copy(nPos);
251 if( pType )
253 sal_uInt16 nObjType = pLink->GetObjType();
254 *pType = SfxResId(
255 ( OBJECT_CLIENT_FILE == nObjType || OBJECT_CLIENT_OLE == nObjType )
256 ? RID_SVXSTR_FILELINK
257 : RID_SVXSTR_GRAFIKLINK).toString();
259 bRet = true;
261 break;
262 case OBJECT_CLIENT_DDE:
264 sal_Int32 nTmp = 0;
265 OUString sCmd( sLNm );
266 OUString sServer( sCmd.getToken( 0, cTokenSeparator, nTmp ) );
267 OUString sTopic( sCmd.getToken( 0, cTokenSeparator, nTmp ) );
269 if( pType )
270 *pType = sServer;
271 if( pFile )
272 *pFile = sTopic;
273 if( pLinkStr )
274 *pLinkStr = sCmd.copy( nTmp );
275 bRet = true;
277 break;
278 default:
279 break;
283 return bRet;
286 void LinkManager::UpdateAllLinks(
287 bool bAskUpdate,
288 bool /*bCallErrHdl*/,
289 bool bUpdateGrfLinks,
290 Window* pParentWin )
292 // First make a copy of the array in order to update links
293 // links in ... no contact between them!
294 std::vector<SvBaseLink*> aTmpArr;
295 for( size_t n = 0; n < aLinkTbl.size(); ++n )
297 SvBaseLink* pLink = *aLinkTbl[ n ];
298 if( !pLink )
300 Remove( n-- );
301 continue;
303 aTmpArr.push_back( pLink );
306 for( size_t n = 0; n < aTmpArr.size(); ++n )
308 SvBaseLink* pLink = aTmpArr[ n ];
310 // search first in the array after the entry
311 bool bFound = false;
312 for( size_t i = 0; i < aLinkTbl.size(); ++i )
313 if( pLink == *aLinkTbl[ i ] )
315 bFound = true;
316 break;
319 if( !bFound )
320 continue; // was not available!
322 // Graphic-Links not to update yet
323 if( !pLink->IsVisible() ||
324 ( !bUpdateGrfLinks && OBJECT_CLIENT_GRF == pLink->GetObjType() ))
325 continue;
327 if( bAskUpdate )
329 int nRet = QueryBox( pParentWin, WB_YES_NO | WB_DEF_YES, SfxResId( STR_QUERY_UPDATE_LINKS ).toString() ).Execute();
330 if( RET_YES != nRet )
331 return ; // nothing should be updated
332 bAskUpdate = false; // once is enough
335 pLink->Update();
337 CloseCachedComps();
340 //--------------------------------------------------------------------------
342 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink * pLink )
344 switch( pLink->GetObjType() )
346 case OBJECT_CLIENT_FILE:
347 case OBJECT_CLIENT_GRF:
348 case OBJECT_CLIENT_OLE:
349 return new SvFileObject;
350 case OBJECT_INTERN:
351 return new SvxInternalLink;
352 case OBJECT_CLIENT_DDE:
353 return new SvDDEObject;
354 default:
355 return SvLinkSourceRef();
359 sal_Bool LinkManager::InsertServer( SvLinkSource* pObj )
361 // no duplicate inserts
362 if( !pObj )
363 return sal_False;
365 return aServerTbl.insert( pObj ).second;
369 void LinkManager::RemoveServer( SvLinkSource* pObj )
371 aServerTbl.erase( pObj );
375 void MakeLnkName( OUString& rName, const OUString* pType, const OUString& rFile,
376 const OUString& rLink, const OUString* pFilter )
378 if( pType )
380 rName = comphelper::string::strip(*pType, ' ');
381 rName += OUString(cTokenSeparator);
383 else if( !rName.isEmpty() )
384 rName = "";
386 rName += rFile;
388 rName = comphelper::string::strip(rName, ' ');
389 rName += OUString(cTokenSeparator);
390 rName = comphelper::string::strip(rName, ' ');
391 rName += rLink;
392 if( pFilter )
394 rName += OUString(cTokenSeparator);
395 rName += *pFilter;
396 rName = comphelper::string::strip(rName, ' ');
400 void LinkManager::ReconnectDdeLink(SfxObjectShell& rServer)
402 SfxMedium* pMed = rServer.GetMedium();
403 if (!pMed)
404 return;
406 const ::sfx2::SvBaseLinks& rLinks = GetLinks();
407 size_t n = rLinks.size();
409 for (size_t i = 0; i < n; ++i)
411 ::sfx2::SvBaseLink* p = *rLinks[i];
412 OUString aType, aFile, aLink, aFilter;
413 if (!GetDisplayNames(p, &aType, &aFile, &aLink, &aFilter))
414 continue;
416 if (aType != "soffice")
417 // DDE connections between OOo apps are always named 'soffice'.
418 continue;
420 OUString aTmp;
421 OUString aURL = aFile;
422 if (utl::LocalFileHelper::ConvertPhysicalNameToURL(aFile, aTmp))
423 aURL = aTmp;
425 if (!aURL.equalsIgnoreAsciiCase(pMed->GetName()))
426 // This DDE link is not associated with this server shell... Skip it.
427 continue;
429 if (aLink.isEmpty())
430 continue;
432 LinkServerShell(aLink, rServer, *p);
436 void LinkManager::LinkServerShell(const OUString& rPath, SfxObjectShell& rServer, ::sfx2::SvBaseLink& rLink) const
438 ::sfx2::SvLinkSource* pSrvSrc = rServer.DdeCreateLinkSource(rPath);
439 if (pSrvSrc)
441 ::com::sun::star::datatransfer::DataFlavor aFl;
442 SotExchange::GetFormatDataFlavor(rLink.GetContentType(), aFl);
443 rLink.SetObj(pSrvSrc);
444 pSrvSrc->AddDataAdvise(
445 &rLink, aFl.MimeType,
446 sfx2::LINKUPDATE_ONCALL == rLink.GetUpdateMode() ? ADVISEMODE_ONLYONCE : 0);
450 bool LinkManager::InsertFileLink(
451 sfx2::SvBaseLink& rLink, sal_uInt16 nFileType, const OUString& rFileNm,
452 const OUString* pFilterNm, const OUString* pRange)
454 if (!(OBJECT_CLIENT_SO & rLink.GetObjType()))
455 return false;
457 OUStringBuffer aBuf;
458 aBuf.append(rFileNm);
459 aBuf.append(sfx2::cTokenSeparator);
461 if (pRange)
462 aBuf.append(*pRange);
464 if (pFilterNm)
466 aBuf.append(sfx2::cTokenSeparator);
467 aBuf.append(*pFilterNm);
470 OUString aCmd = aBuf.makeStringAndClear();
471 return InsertLink(&rLink, nFileType, sfx2::LINKUPDATE_ONCALL, &aCmd);
474 // A transfer is aborted, so cancel all download media
475 // (for now this is only of interest for the file links!)
476 void LinkManager::CancelTransfers()
478 SvFileObject* pFileObj;
479 sfx2::SvBaseLink* pLnk;
481 const sfx2::SvBaseLinks& rLnks = GetLinks();
482 for( size_t n = rLnks.size(); n; )
483 if( 0 != ( pLnk = &(*rLnks[ --n ])) &&
484 OBJECT_CLIENT_FILE == (OBJECT_CLIENT_FILE & pLnk->GetObjType()) &&
485 0 != ( pFileObj = (SvFileObject*)pLnk->GetObj() ) )
486 pFileObj->CancelTransfers();
488 // For the purpose of sending Status information from the file object to
489 // the base link, there exist a dedicated ClipBoardId. The SvData-object
490 // gets the appropriate information as a string
491 // For now this is required for file object in conjunction with JavaScript
492 // - needs information about Load/Abort/Error
493 sal_uIntPtr LinkManager::RegisterStatusInfoId()
495 static sal_uIntPtr nFormat = 0;
497 if( !nFormat )
499 nFormat = SotExchange::RegisterFormatName(
500 OUString("StatusInfo from SvxInternalLink"));
502 return nFormat;
505 // ----------------------------------------------------------------------
507 sal_Bool LinkManager::GetGraphicFromAny( const OUString& rMimeType,
508 const ::com::sun::star::uno::Any & rValue,
509 Graphic& rGrf )
511 sal_Bool bRet = sal_False;
512 ::com::sun::star::uno::Sequence< sal_Int8 > aSeq;
513 if( rValue.hasValue() && ( rValue >>= aSeq ) )
515 SvMemoryStream aMemStm( (void*)aSeq.getConstArray(), aSeq.getLength(),
516 STREAM_READ );
517 aMemStm.Seek( 0 );
519 switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
521 case SOT_FORMATSTR_ID_SVXB:
523 aMemStm >> rGrf;
524 bRet = sal_True;
526 break;
527 case FORMAT_GDIMETAFILE:
529 GDIMetaFile aMtf;
530 aMtf.Read( aMemStm );
531 rGrf = aMtf;
532 bRet = sal_True;
534 break;
535 case FORMAT_BITMAP:
537 Bitmap aBmp;
538 ReadDIB(aBmp, aMemStm, true);
539 rGrf = aBmp;
540 bRet = sal_True;
542 break;
545 return bRet;
549 // ----------------------------------------------------------------------
550 OUString lcl_DDE_RelToAbs( const OUString& rTopic, const OUString& rBaseURL )
552 OUString sRet;
553 INetURLObject aURL( rTopic );
554 if( INET_PROT_NOT_VALID == aURL.GetProtocol() )
555 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic, rBaseURL, sRet );
556 if( sRet.isEmpty() )
557 sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl(), true );
558 return sRet;
561 sal_Bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink )
563 SfxObjectShell* pFndShell = 0;
564 sal_uInt16 nUpdateMode = com::sun::star::document::UpdateDocMode::NO_UPDATE;
565 OUString sTopic, sItem, sReferer;
566 LinkManager* pLinkMgr = pLink->GetLinkManager();
567 if (pLinkMgr && pLinkMgr->GetDisplayNames(pLink, 0, &sTopic, &sItem) && !sTopic.isEmpty())
569 // first only loop over the DocumentShells the shells and find those
570 // with the name:
571 CharClass aCC( LanguageTag( LANGUAGE_SYSTEM) );
573 TypeId aType( TYPE(SfxObjectShell) );
575 sal_Bool bFirst = sal_True;
576 SfxObjectShell* pShell = pLinkMgr->GetPersist();
577 if( pShell && pShell->GetMedium() )
579 sReferer = pShell->GetMedium()->GetBaseURL();
580 SFX_ITEMSET_ARG( pShell->GetMedium()->GetItemSet(), pItem, SfxUInt16Item, SID_UPDATEDOCMODE, sal_False );
581 if ( pItem )
582 nUpdateMode = pItem->GetValue();
585 OUString sNmURL(aCC.lowercase(lcl_DDE_RelToAbs(sTopic, sReferer)));
587 if ( !pShell )
589 bFirst = sal_False;
590 pShell = SfxObjectShell::GetFirst( &aType, sal_False );
593 OUString sTmp;
594 while( pShell )
596 if( sTmp.isEmpty() )
598 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME );
599 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer );
603 sTmp = aCC.lowercase( sTmp );
604 if( sTmp == sNmURL ) // we want these
606 pFndShell = pShell;
607 break;
610 if( bFirst )
612 bFirst = sal_False;
613 pShell = SfxObjectShell::GetFirst( &aType, sal_False );
615 else
616 pShell = SfxObjectShell::GetNext( *pShell, &aType, sal_False );
618 sTmp = "";
622 // empty topics are not allowed - which document is it
623 if( sTopic.isEmpty() )
624 return sal_False;
626 if (pFndShell)
628 sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem );
629 if( pNewSrc )
631 ::com::sun::star::datatransfer::DataFlavor aFl;
632 SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl );
634 pLink->SetObj( pNewSrc );
635 pNewSrc->AddDataAdvise( pLink, aFl.MimeType,
636 sfx2::LINKUPDATE_ONCALL == pLink->GetUpdateMode()
637 ? ADVISEMODE_ONLYONCE
638 : 0 );
639 return true;
642 else
644 // then try to download the file:
645 INetURLObject aURL( sTopic );
646 INetProtocol eOld = aURL.GetProtocol();
647 aURL.SetURL( sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ) );
648 if( INET_PROT_NOT_VALID != eOld ||
649 INET_PROT_HTTP != aURL.GetProtocol() )
651 SfxStringItem aName( SID_FILE_NAME, sTopic );
652 SfxBoolItem aMinimized(SID_MINIMIZED, sal_True);
653 SfxBoolItem aHidden(SID_HIDDEN, sal_True);
654 SfxStringItem aTarget( SID_TARGETNAME, OUString("_blank") );
655 SfxStringItem aReferer( SID_REFERER, sReferer );
656 SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode );
657 SfxBoolItem aReadOnly(SID_DOC_READONLY, false);
659 // Disable automatic re-connection to avoid this link instance
660 // being destroyed at re-connection.
661 SfxBoolItem aDdeConnect(SID_DDE_RECONNECT_ONLOAD, false);
663 // #i14200# (DDE-link crashes wordprocessor)
664 SfxAllItemSet aArgs( SFX_APP()->GetPool() );
665 aArgs.Put(aReferer);
666 aArgs.Put(aTarget);
667 aArgs.Put(aHidden);
668 aArgs.Put(aMinimized);
669 aArgs.Put(aName);
670 aArgs.Put(aUpdate);
671 aArgs.Put(aReadOnly);
672 aArgs.Put(aDdeConnect);
673 Reference<XComponent> xComp = SfxObjectShell::CreateAndLoadComponent(aArgs);
674 pFndShell = SfxObjectShell::GetShellFromComponent(xComp);
675 if (xComp.is() && pFndShell)
677 pLinkMgr->InsertCachedComp(xComp);
678 pLinkMgr->LinkServerShell(sItem, *pFndShell, *pLink);
679 return true;
684 return false;
692 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */