Branch libreoffice-5-0-4
[LibreOffice.git] / sfx2 / source / appl / linkmgr2.cxx
blobe3d4b45d2560b694f2444a8bd1abc15eb2bb6d40
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>
40 #include "unotools/charclass.hxx"
42 #include "fileobj.hxx"
43 #include "impldde.hxx"
44 #include "app.hrc"
45 #include <sfx2/sfxresid.hxx>
47 #include <com/sun/star/lang/XComponent.hpp>
48 #include <com/sun/star/util/XCloseable.hpp>
50 using ::com::sun::star::uno::UNO_QUERY;
51 using ::com::sun::star::uno::Reference;
52 using ::com::sun::star::lang::XComponent;
53 using ::com::sun::star::util::XCloseable;
55 namespace sfx2
58 class SvxInternalLink : public sfx2::SvLinkSource
60 public:
61 SvxInternalLink() {}
63 virtual bool Connect( sfx2::SvBaseLink* ) SAL_OVERRIDE;
67 LinkManager::LinkManager(SfxObjectShell* p)
68 : pPersist( p )
73 LinkManager::~LinkManager()
75 for( size_t n = 0; n < aLinkTbl.size(); ++n)
77 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
78 if( pTmp->Is() )
80 (*pTmp)->Disconnect();
81 (*pTmp)->SetLinkManager( NULL );
83 delete pTmp;
87 void LinkManager::InsertCachedComp(const Reference<XComponent>& xComp)
89 maCachedComps.push_back(xComp);
92 void LinkManager::CloseCachedComps()
94 CompVector::iterator itr = maCachedComps.begin(), itrEnd = maCachedComps.end();
95 for (; itr != itrEnd; ++itr)
97 Reference<XCloseable> xCloseable(*itr, UNO_QUERY);
98 if (!xCloseable.is())
99 continue;
101 xCloseable->close(true);
103 maCachedComps.clear();
108 void LinkManager::Remove( SvBaseLink *pLink )
110 // No duplicate links inserted
111 bool bFound = false;
112 for( size_t n = 0; n < aLinkTbl.size(); )
114 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
115 if( pLink == *pTmp )
117 (*pTmp)->Disconnect();
118 (*pTmp)->SetLinkManager( NULL );
119 (*pTmp).Clear();
120 bFound = true;
123 // Remove empty ones if they exist
124 if( !pTmp->Is() )
126 delete pTmp;
127 aLinkTbl.erase( aLinkTbl.begin() + n );
128 if( bFound )
129 return ;
131 else
132 ++n;
137 void LinkManager::Remove( size_t nPos, size_t nCnt )
139 if( nCnt && nPos < aLinkTbl.size() )
141 if (sal::static_int_cast<size_t>(nPos + nCnt) > aLinkTbl.size())
142 nCnt = aLinkTbl.size() - nPos;
144 for( size_t n = nPos; n < nPos + nCnt; ++n)
146 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
147 if( pTmp->Is() )
149 (*pTmp)->Disconnect();
150 (*pTmp)->SetLinkManager( NULL );
152 delete pTmp;
154 aLinkTbl.erase( aLinkTbl.begin() + nPos, aLinkTbl.begin() + nPos + nCnt );
159 bool LinkManager::Insert( SvBaseLink* pLink )
161 for( size_t n = 0; n < aLinkTbl.size(); ++n )
163 SvBaseLinkRef* pTmp = aLinkTbl[ n ];
164 if( !pTmp->Is() )
166 delete pTmp;
167 aLinkTbl.erase( aLinkTbl.begin() + n-- );
169 else if( pLink == *pTmp )
170 return false; // No duplicate links inserted
173 SvBaseLinkRef* pTmp = new SvBaseLinkRef( pLink );
174 pLink->SetLinkManager( this );
175 aLinkTbl.push_back( pTmp );
176 return true;
180 bool LinkManager::InsertLink( SvBaseLink * pLink,
181 sal_uInt16 nObjType,
182 SfxLinkUpdateMode nUpdateMode,
183 const OUString* pName )
185 // This First
186 pLink->SetObjType( nObjType );
187 if( pName )
188 pLink->SetName( *pName );
189 pLink->SetUpdateMode( nUpdateMode );
190 return Insert( pLink );
194 bool LinkManager::InsertDDELink( SvBaseLink * pLink,
195 const OUString& rServer,
196 const OUString& rTopic,
197 const OUString& rItem )
199 if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
200 return false;
202 OUString sCmd;
203 ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem );
205 pLink->SetObjType( OBJECT_CLIENT_DDE );
206 pLink->SetName( sCmd );
207 return Insert( pLink );
211 bool LinkManager::InsertDDELink( SvBaseLink * pLink )
213 DBG_ASSERT( OBJECT_CLIENT_SO & pLink->GetObjType(), "no OBJECT_CLIENT_SO" );
214 if( !( OBJECT_CLIENT_SO & pLink->GetObjType() ) )
215 return false;
217 if( pLink->GetObjType() == OBJECT_CLIENT_SO )
218 pLink->SetObjType( OBJECT_CLIENT_DDE );
220 return Insert( pLink );
224 // Obtain the string for the dialog
225 bool LinkManager::GetDisplayNames( const SvBaseLink * pLink,
226 OUString* pType,
227 OUString* pFile,
228 OUString* pLinkStr,
229 OUString* pFilter )
231 bool bRet = false;
232 const OUString sLNm( pLink->GetLinkSourceName() );
233 if( !sLNm.isEmpty() )
235 switch( pLink->GetObjType() )
237 case OBJECT_CLIENT_FILE:
238 case OBJECT_CLIENT_GRF:
239 case OBJECT_CLIENT_OLE:
241 sal_Int32 nPos = 0;
242 OUString sFile( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
243 OUString sRange( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
245 if( pFile )
246 *pFile = sFile;
247 if( pLinkStr )
248 *pLinkStr = sRange;
249 if( pFilter )
250 *pFilter = nPos == -1 ? OUString() : sLNm.copy(nPos);
252 if( pType )
254 sal_uInt16 nObjType = pLink->GetObjType();
255 *pType = SfxResId(
256 ( OBJECT_CLIENT_FILE == nObjType || OBJECT_CLIENT_OLE == nObjType )
257 ? RID_SVXSTR_FILELINK
258 : RID_SVXSTR_GRAFIKLINK).toString();
260 bRet = true;
262 break;
263 case OBJECT_CLIENT_DDE:
265 sal_Int32 nTmp = 0;
266 OUString sCmd( sLNm );
267 OUString sServer( sCmd.getToken( 0, cTokenSeparator, nTmp ) );
268 OUString sTopic( sCmd.getToken( 0, cTokenSeparator, nTmp ) );
270 if( pType )
271 *pType = sServer;
272 if( pFile )
273 *pFile = sTopic;
274 if( pLinkStr )
275 *pLinkStr = nTmp != -1 ? sCmd.copy(nTmp) : OUString();
276 bRet = true;
278 break;
279 default:
280 break;
284 return bRet;
287 void LinkManager::UpdateAllLinks(
288 bool bAskUpdate,
289 bool /*bCallErrHdl*/,
290 bool bUpdateGrfLinks,
291 vcl::Window* pParentWin )
293 // First make a copy of the array in order to update links
294 // links in ... no contact between them!
295 std::vector<SvBaseLink*> aTmpArr;
296 for( size_t n = 0; n < aLinkTbl.size(); ++n )
298 SvBaseLink* pLink = *aLinkTbl[ n ];
299 if( !pLink )
301 Remove( n-- );
302 continue;
304 aTmpArr.push_back( pLink );
307 for( size_t n = 0; n < aTmpArr.size(); ++n )
309 SvBaseLink* pLink = aTmpArr[ n ];
311 // search first in the array after the entry
312 bool bFound = false;
313 for( size_t i = 0; i < aLinkTbl.size(); ++i )
314 if( pLink == *aLinkTbl[ i ] )
316 bFound = true;
317 break;
320 if( !bFound )
321 continue; // was not available!
323 // Graphic-Links not to update yet
324 if( !pLink->IsVisible() ||
325 ( !bUpdateGrfLinks && OBJECT_CLIENT_GRF == pLink->GetObjType() ))
326 continue;
328 if( bAskUpdate )
330 int nRet = ScopedVclPtr<QueryBox>::Create( pParentWin, WB_YES_NO | WB_DEF_YES, SfxResId( STR_QUERY_UPDATE_LINKS ).toString() )->Execute();
331 if( RET_YES != nRet )
333 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
335 if(pShell)
337 comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer();
338 rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false);
341 return ; // nothing should be updated
343 bAskUpdate = false; // once is enough
346 pLink->Update();
348 CloseCachedComps();
353 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink * pLink )
355 switch( pLink->GetObjType() )
357 case OBJECT_CLIENT_FILE:
358 case OBJECT_CLIENT_GRF:
359 case OBJECT_CLIENT_OLE:
360 return new SvFileObject;
361 case OBJECT_INTERN:
362 return new SvxInternalLink;
363 case OBJECT_CLIENT_DDE:
364 return new SvDDEObject;
365 default:
366 return SvLinkSourceRef();
370 bool LinkManager::InsertServer( SvLinkSource* pObj )
372 // no duplicate inserts
373 if( !pObj )
374 return false;
376 return aServerTbl.insert( pObj ).second;
380 void LinkManager::RemoveServer( SvLinkSource* pObj )
382 aServerTbl.erase( pObj );
386 void MakeLnkName( OUString& rName, const OUString* pType, const OUString& rFile,
387 const OUString& rLink, const OUString* pFilter )
389 if( pType )
391 rName = comphelper::string::strip(*pType, ' ');
392 rName += OUString(cTokenSeparator);
394 else if( !rName.isEmpty() )
395 rName.clear();
397 rName += rFile;
399 rName = comphelper::string::strip(rName, ' ');
400 rName += OUString(cTokenSeparator);
401 rName = comphelper::string::strip(rName, ' ');
402 rName += rLink;
403 if( pFilter )
405 rName += OUString(cTokenSeparator);
406 rName += *pFilter;
407 rName = comphelper::string::strip(rName, ' ');
411 void LinkManager::ReconnectDdeLink(SfxObjectShell& rServer)
413 SfxMedium* pMed = rServer.GetMedium();
414 if (!pMed)
415 return;
417 const ::sfx2::SvBaseLinks& rLinks = GetLinks();
418 size_t n = rLinks.size();
420 for (size_t i = 0; i < n; ++i)
422 ::sfx2::SvBaseLink* p = *rLinks[i];
423 OUString aType, aFile, aLink, aFilter;
424 if (!GetDisplayNames(p, &aType, &aFile, &aLink, &aFilter))
425 continue;
427 if (aType != "soffice")
428 // DDE connections between OOo apps are always named 'soffice'.
429 continue;
431 OUString aTmp;
432 OUString aURL = aFile;
433 if (utl::LocalFileHelper::ConvertPhysicalNameToURL(aFile, aTmp))
434 aURL = aTmp;
436 if (!aURL.equalsIgnoreAsciiCase(pMed->GetName()))
437 // This DDE link is not associated with this server shell... Skip it.
438 continue;
440 if (aLink.isEmpty())
441 continue;
443 LinkServerShell(aLink, rServer, *p);
447 void LinkManager::LinkServerShell(const OUString& rPath, SfxObjectShell& rServer, ::sfx2::SvBaseLink& rLink)
449 ::sfx2::SvLinkSource* pSrvSrc = rServer.DdeCreateLinkSource(rPath);
450 if (pSrvSrc)
452 ::com::sun::star::datatransfer::DataFlavor aFl;
453 SotExchange::GetFormatDataFlavor(rLink.GetContentType(), aFl);
454 rLink.SetObj(pSrvSrc);
455 pSrvSrc->AddDataAdvise(
456 &rLink, aFl.MimeType,
457 SfxLinkUpdateMode::ONCALL == rLink.GetUpdateMode() ? ADVISEMODE_ONLYONCE : 0);
461 bool LinkManager::InsertFileLink(
462 sfx2::SvBaseLink& rLink, sal_uInt16 nFileType, const OUString& rFileNm,
463 const OUString* pFilterNm, const OUString* pRange)
465 if (!(OBJECT_CLIENT_SO & rLink.GetObjType()))
466 return false;
468 OUStringBuffer aBuf;
469 aBuf.append(rFileNm);
470 aBuf.append(sfx2::cTokenSeparator);
472 if (pRange)
473 aBuf.append(*pRange);
475 if (pFilterNm)
477 aBuf.append(sfx2::cTokenSeparator);
478 aBuf.append(*pFilterNm);
481 OUString aCmd = aBuf.makeStringAndClear();
482 return InsertLink(&rLink, nFileType, SfxLinkUpdateMode::ONCALL, &aCmd);
485 // A transfer is aborted, so cancel all download media
486 // (for now this is only of interest for the file links!)
487 void LinkManager::CancelTransfers()
489 SvFileObject* pFileObj;
490 sfx2::SvBaseLink* pLnk;
492 const sfx2::SvBaseLinks& rLnks = GetLinks();
493 for( size_t n = rLnks.size(); n; )
494 if( 0 != ( pLnk = &(*rLnks[ --n ])) &&
495 OBJECT_CLIENT_FILE == (OBJECT_CLIENT_FILE & pLnk->GetObjType()) &&
496 0 != ( pFileObj = static_cast<SvFileObject*>(pLnk->GetObj()) ) )
497 pFileObj->CancelTransfers();
499 // For the purpose of sending Status information from the file object to
500 // the base link, there exist a dedicated ClipBoardId. The SvData-object
501 // gets the appropriate information as a string
502 // For now this is required for file object in conjunction with JavaScript
503 // - needs information about Load/Abort/Error
504 SotClipboardFormatId LinkManager::RegisterStatusInfoId()
506 static SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
508 if( nFormat == SotClipboardFormatId::NONE )
510 nFormat = SotExchange::RegisterFormatName(
511 OUString("StatusInfo from SvxInternalLink"));
513 return nFormat;
518 bool LinkManager::GetGraphicFromAny( const OUString& rMimeType,
519 const ::com::sun::star::uno::Any & rValue,
520 Graphic& rGrf )
522 bool bRet = false;
523 ::com::sun::star::uno::Sequence< sal_Int8 > aSeq;
524 if( rValue.hasValue() && ( rValue >>= aSeq ) )
526 SvMemoryStream aMemStm( (void*)aSeq.getConstArray(), aSeq.getLength(),
527 StreamMode::READ );
528 aMemStm.Seek( 0 );
530 switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
532 case SotClipboardFormatId::SVXB:
534 ReadGraphic( aMemStm, rGrf );
535 bRet = true;
537 break;
538 case SotClipboardFormatId::GDIMETAFILE:
540 GDIMetaFile aMtf;
541 aMtf.Read( aMemStm );
542 rGrf = aMtf;
543 bRet = true;
545 break;
546 case SotClipboardFormatId::BITMAP:
548 Bitmap aBmp;
549 ReadDIB(aBmp, aMemStm, true);
550 rGrf = aBmp;
551 bRet = true;
553 break;
554 default: break;
557 return bRet;
562 OUString lcl_DDE_RelToAbs( const OUString& rTopic, const OUString& rBaseURL )
564 OUString sRet;
565 INetURLObject aURL( rTopic );
566 if( INetProtocol::NotValid == aURL.GetProtocol() )
567 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic, rBaseURL, sRet );
568 if( sRet.isEmpty() )
569 sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl(), true );
570 return sRet;
573 bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink )
575 SfxObjectShell* pFndShell = 0;
576 sal_uInt16 nUpdateMode = com::sun::star::document::UpdateDocMode::NO_UPDATE;
577 OUString sTopic, sItem, sReferer;
578 LinkManager* pLinkMgr = pLink->GetLinkManager();
579 if (pLinkMgr && sfx2::LinkManager::GetDisplayNames(pLink, 0, &sTopic, &sItem) && !sTopic.isEmpty())
581 // first only loop over the DocumentShells the shells and find those
582 // with the name:
583 CharClass aCC( LanguageTag( LANGUAGE_SYSTEM) );
585 TypeId aType( TYPE(SfxObjectShell) );
587 bool bFirst = true;
588 SfxObjectShell* pShell = pLinkMgr->GetPersist();
589 if( pShell && pShell->GetMedium() )
591 sReferer = pShell->GetMedium()->GetBaseURL();
592 SFX_ITEMSET_ARG( pShell->GetMedium()->GetItemSet(), pItem, SfxUInt16Item, SID_UPDATEDOCMODE, false );
593 if ( pItem )
594 nUpdateMode = pItem->GetValue();
597 OUString sNmURL(aCC.lowercase(lcl_DDE_RelToAbs(sTopic, sReferer)));
599 if ( !pShell )
601 bFirst = false;
602 pShell = SfxObjectShell::GetFirst( &aType, false );
605 OUString sTmp;
606 while( pShell )
608 if( sTmp.isEmpty() )
610 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME );
611 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer );
615 sTmp = aCC.lowercase( sTmp );
616 if( sTmp == sNmURL ) // we want these
618 pFndShell = pShell;
619 break;
622 if( bFirst )
624 bFirst = false;
625 pShell = SfxObjectShell::GetFirst( &aType, false );
627 else
628 pShell = SfxObjectShell::GetNext( *pShell, &aType, false );
630 sTmp.clear();
634 // empty topics are not allowed - which document is it
635 if( sTopic.isEmpty() )
636 return false;
638 if (pFndShell)
640 sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem );
641 if( pNewSrc )
643 ::com::sun::star::datatransfer::DataFlavor aFl;
644 SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl );
646 pLink->SetObj( pNewSrc );
647 pNewSrc->AddDataAdvise( pLink, aFl.MimeType,
648 SfxLinkUpdateMode::ONCALL == pLink->GetUpdateMode()
649 ? ADVISEMODE_ONLYONCE
650 : 0 );
651 return true;
654 else
656 // then try to download the file:
657 INetURLObject aURL( sTopic );
658 INetProtocol eOld = aURL.GetProtocol();
659 aURL.SetURL( sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ) );
660 if( INetProtocol::NotValid != eOld ||
661 INetProtocol::Http != aURL.GetProtocol() )
663 SfxStringItem aName( SID_FILE_NAME, sTopic );
664 SfxBoolItem aMinimized(SID_MINIMIZED, true);
665 SfxBoolItem aHidden(SID_HIDDEN, true);
666 SfxStringItem aTarget( SID_TARGETNAME, OUString("_blank") );
667 SfxStringItem aReferer( SID_REFERER, sReferer );
668 SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode );
669 SfxBoolItem aReadOnly(SID_DOC_READONLY, false);
671 // Disable automatic re-connection to avoid this link instance
672 // being destroyed at re-connection.
673 SfxBoolItem aDdeConnect(SID_DDE_RECONNECT_ONLOAD, false);
675 // #i14200# (DDE-link crashes wordprocessor)
676 SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
677 aArgs.Put(aReferer);
678 aArgs.Put(aTarget);
679 aArgs.Put(aHidden);
680 aArgs.Put(aMinimized);
681 aArgs.Put(aName);
682 aArgs.Put(aUpdate);
683 aArgs.Put(aReadOnly);
684 aArgs.Put(aDdeConnect);
685 Reference<XComponent> xComp = SfxObjectShell::CreateAndLoadComponent(aArgs);
686 pFndShell = SfxObjectShell::GetShellFromComponent(xComp);
687 if (xComp.is() && pFndShell)
689 pLinkMgr->InsertCachedComp(xComp);
690 sfx2::LinkManager::LinkServerShell(sItem, *pFndShell, *pLink);
691 return true;
696 return false;
704 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */