Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / appl / linkmgr2.cxx
blobbefb7510f82a6fd06cbbf993969790fd891c1beb
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 <sfx2/sfxsids.hrc>
23 #include <com/sun/star/document/UpdateDocMode.hpp>
24 #include <osl/file.hxx>
25 #include <sfx2/objsh.hxx>
26 #include <svl/urihelper.hxx>
27 #include <sot/formats.hxx>
28 #include <tools/urlobj.hxx>
29 #include <sot/exchange.hxx>
30 #include <tools/debug.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/weld.hxx>
33 #include <vcl/gdimtf.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 <i18nlangtag/languagetag.hxx>
41 #include <vcl/dibtools.hxx>
42 #include <unotools/charclass.hxx>
43 #include <unotools/securityoptions.hxx>
44 #include <vcl/GraphicLoader.hxx>
46 #include "fileobj.hxx"
47 #include "impldde.hxx"
48 #include <sfx2/strings.hrc>
49 #include <sfx2/sfxresid.hxx>
51 #include <com/sun/star/lang/XComponent.hpp>
52 #include <com/sun/star/util/XCloseable.hpp>
54 using ::com::sun::star::uno::UNO_QUERY;
55 using ::com::sun::star::uno::Reference;
56 using ::com::sun::star::lang::XComponent;
57 using ::com::sun::star::util::XCloseable;
59 namespace sfx2
62 namespace {
64 class SvxInternalLink : public sfx2::SvLinkSource
66 public:
67 SvxInternalLink() {}
69 virtual bool Connect( sfx2::SvBaseLink* ) override;
74 LinkManager::LinkManager(SfxObjectShell* p)
75 : pPersist( p )
79 LinkManager::~LinkManager()
81 for(tools::SvRef<SvBaseLink> & rTmp : aLinkTbl)
83 if( rTmp.is() )
85 rTmp->Disconnect();
86 rTmp->SetLinkManager( nullptr );
91 void LinkManager::InsertCachedComp(const Reference<XComponent>& xComp)
93 maCachedComps.push_back(xComp);
96 void LinkManager::CloseCachedComps()
98 for (const auto& rxCachedComp : maCachedComps)
100 Reference<XCloseable> xCloseable(rxCachedComp, UNO_QUERY);
101 if (!xCloseable.is())
102 continue;
104 xCloseable->close(true);
106 maCachedComps.clear();
109 void LinkManager::Remove( SvBaseLink const *pLink )
111 // No duplicate links inserted
112 bool bFound = false;
113 for( size_t n = 0; n < aLinkTbl.size(); )
115 tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
116 if( pLink == rTmp.get() )
118 rTmp->Disconnect();
119 rTmp->SetLinkManager( nullptr );
120 rTmp.clear();
121 bFound = true;
124 // Remove empty ones if they exist
125 if( !rTmp.is() )
127 aLinkTbl.erase( aLinkTbl.begin() + n );
128 if( bFound )
129 return ;
131 else
132 ++n;
136 void LinkManager::Remove( size_t nPos, size_t nCnt )
138 if( !nCnt || nPos >= aLinkTbl.size() )
139 return;
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 tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
147 if( rTmp.is() )
149 rTmp->Disconnect();
150 rTmp->SetLinkManager( nullptr );
153 aLinkTbl.erase( aLinkTbl.begin() + nPos, aLinkTbl.begin() + nPos + nCnt );
156 bool LinkManager::Insert( SvBaseLink* pLink )
158 for( size_t n = 0; n < aLinkTbl.size(); ++n )
160 tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
161 if( !rTmp.is() )
163 aLinkTbl.erase( aLinkTbl.begin() + n-- );
165 else if( pLink == rTmp.get() )
166 return false; // No duplicate links inserted
169 pLink->SetLinkManager( this );
170 aLinkTbl.emplace_back(pLink );
171 return true;
174 bool LinkManager::InsertLink( SvBaseLink * pLink,
175 SvBaseLinkObjectType nObjType,
176 SfxLinkUpdateMode nUpdateMode,
177 const OUString* pName )
179 // This First
180 pLink->SetObjType( nObjType );
181 if( pName )
182 pLink->SetName( *pName );
183 pLink->SetUpdateMode( nUpdateMode );
184 return Insert( pLink );
187 void LinkManager::InsertDDELink( SvBaseLink * pLink,
188 const OUString& rServer,
189 const OUString& rTopic,
190 const OUString& rItem )
192 if( !isClientType( pLink->GetObjType() ) )
193 return;
195 OUString sCmd;
196 ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem );
198 pLink->SetObjType( SvBaseLinkObjectType::ClientDde );
199 pLink->SetName( sCmd );
200 Insert( pLink );
203 void LinkManager::InsertDDELink( SvBaseLink * pLink )
205 DBG_ASSERT( isClientType(pLink->GetObjType()), "no OBJECT_CLIENT_SO" );
206 if( !isClientType( pLink->GetObjType() ) )
207 return;
209 if( pLink->GetObjType() == SvBaseLinkObjectType::ClientSo )
210 pLink->SetObjType( SvBaseLinkObjectType::ClientDde );
212 Insert( pLink );
215 // Obtain the string for the dialog
216 bool LinkManager::GetDisplayNames( const SvBaseLink * pLink,
217 OUString* pType,
218 OUString* pFile,
219 OUString* pLinkStr,
220 OUString* pFilter )
222 bool bRet = false;
223 const OUString& sLNm( pLink->GetLinkSourceName() );
224 if( !sLNm.isEmpty() )
226 switch( pLink->GetObjType() )
228 case SvBaseLinkObjectType::ClientFile:
229 case SvBaseLinkObjectType::ClientGraphic:
230 case SvBaseLinkObjectType::ClientOle:
232 sal_Int32 nPos = 0;
233 OUString sFile( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
234 OUString sRange( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
236 if( pFile )
237 *pFile = sFile;
238 if( pLinkStr )
239 *pLinkStr = sRange;
240 if( pFilter )
241 *pFilter = nPos == -1 ? OUString() : sLNm.copy(nPos);
243 if( pType )
245 SvBaseLinkObjectType nObjType = pLink->GetObjType();
246 *pType = SfxResId(
247 ( SvBaseLinkObjectType::ClientFile == nObjType || SvBaseLinkObjectType::ClientOle == nObjType )
248 ? RID_SVXSTR_FILELINK
249 : RID_SVXSTR_GRAFIKLINK);
251 bRet = true;
253 break;
254 case SvBaseLinkObjectType::ClientDde:
256 sal_Int32 nTmp = 0;
257 OUString sServer( sLNm.getToken( 0, cTokenSeparator, nTmp ) );
258 OUString sTopic( sLNm.getToken( 0, cTokenSeparator, nTmp ) );
260 if( pType )
261 *pType = sServer;
262 if( pFile )
263 *pFile = sTopic;
264 if( pLinkStr )
265 *pLinkStr = nTmp != -1 ? sLNm.copy(nTmp) : OUString();
266 bRet = true;
268 break;
269 default:
270 break;
274 return bRet;
277 void LinkManager::UpdateAllLinks(
278 bool bAskUpdate,
279 bool bUpdateGrfLinks,
280 weld::Window* pParentWin )
282 // First make a copy of the array in order to update links
283 // links in ... no contact between them!
284 std::vector<SvBaseLink*> aTmpArr;
285 for( size_t n = 0; n < aLinkTbl.size(); ++n )
287 tools::SvRef<SvBaseLink>& rLink = aLinkTbl[ n ];
288 if( !rLink.is() )
290 Remove( n-- );
291 continue;
293 aTmpArr.push_back( rLink.get() );
296 for(SvBaseLink* pLink : aTmpArr)
298 // search first in the array after the entry
299 bool bFound = false;
300 for(const tools::SvRef<SvBaseLink> & i : aLinkTbl)
301 if( pLink == i.get() )
303 bFound = true;
304 break;
307 if( !bFound )
308 continue; // was not available!
310 // Graphic-Links not to update yet
311 if( !pLink->IsVisible() ||
312 ( !bUpdateGrfLinks && SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() ))
313 continue;
315 if( bAskUpdate )
317 OUString aMsg = SfxResId(STR_QUERY_UPDATE_LINKS);
318 INetURLObject aURL(pPersist->getDocumentBaseURL());
319 aMsg = aMsg.replaceFirst("%{filename}", aURL.GetLastName());
321 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParentWin,
322 VclMessageType::Question, VclButtonsType::YesNo, aMsg));
323 xQueryBox->set_default_response(RET_YES);
325 int nRet = xQueryBox->run();
326 if( RET_YES != nRet )
328 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
330 if(pShell)
332 comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer();
333 rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false);
336 return ; // nothing should be updated
338 bAskUpdate = false; // once is enough
341 pLink->Update();
343 CloseCachedComps();
346 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink const * pLink )
348 switch( pLink->GetObjType() )
350 case SvBaseLinkObjectType::ClientFile:
351 case SvBaseLinkObjectType::ClientGraphic:
352 case SvBaseLinkObjectType::ClientOle:
353 return new SvFileObject;
354 case SvBaseLinkObjectType::Internal:
355 return new SvxInternalLink;
356 case SvBaseLinkObjectType::ClientDde:
357 return new SvDDEObject;
358 default:
359 return SvLinkSourceRef();
363 bool LinkManager::InsertServer( SvLinkSource* pObj )
365 // no duplicate inserts
366 if( !pObj )
367 return false;
369 return aServerTbl.insert( pObj ).second;
372 void LinkManager::RemoveServer( SvLinkSource* pObj )
374 aServerTbl.erase( pObj );
377 void MakeLnkName( OUString& rName, const OUString* pType, const OUString& rFile,
378 const OUString& rLink, const OUString* pFilter )
380 if( pType )
382 rName = comphelper::string::strip(*pType, ' ')
383 + OUStringChar(cTokenSeparator);
385 else
386 rName.clear();
388 rName += rFile;
390 rName = comphelper::string::strip(rName, ' ')
391 + OUStringChar(cTokenSeparator);
392 rName = comphelper::string::strip(rName, ' ') + rLink;
393 if( pFilter )
395 rName += OUStringChar(cTokenSeparator) + *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].get();
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 (osl::FileBase::getFileURLFromSystemPath(aFile, aTmp)
423 == osl::FileBase::E_None)
424 aURL = aTmp;
426 if (!aURL.equalsIgnoreAsciiCase(pMed->GetName()))
427 // This DDE link is not associated with this server shell... Skip it.
428 continue;
430 if (aLink.isEmpty())
431 continue;
433 LinkServerShell(aLink, rServer, *p);
437 void LinkManager::LinkServerShell(const OUString& rPath, SfxObjectShell& rServer, ::sfx2::SvBaseLink& rLink)
439 ::sfx2::SvLinkSource* pSrvSrc = rServer.DdeCreateLinkSource(rPath);
440 if (pSrvSrc)
442 css::datatransfer::DataFlavor aFl;
443 SotExchange::GetFormatDataFlavor(rLink.GetContentType(), aFl);
444 rLink.SetObj(pSrvSrc);
445 pSrvSrc->AddDataAdvise(
446 &rLink, aFl.MimeType,
447 SfxLinkUpdateMode::ONCALL == rLink.GetUpdateMode() ? ADVISEMODE_ONLYONCE : 0);
451 void LinkManager::InsertFileLink(
452 sfx2::SvBaseLink& rLink, SvBaseLinkObjectType nFileType, const OUString& rFileNm,
453 const OUString* pFilterNm, const OUString* pRange)
455 if (!isClientType(rLink.GetObjType()))
456 return;
458 OUStringBuffer aBuf(64);
459 aBuf.append(rFileNm);
460 aBuf.append(sfx2::cTokenSeparator);
462 if (pRange)
463 aBuf.append(*pRange);
465 if (pFilterNm)
467 aBuf.append(sfx2::cTokenSeparator);
468 aBuf.append(*pFilterNm);
471 OUString aCmd = aBuf.makeStringAndClear();
472 InsertLink(&rLink, nFileType, SfxLinkUpdateMode::ONCALL, &aCmd);
475 // A transfer is aborted, so cancel all download media
476 // (for now this is only of interest for the file links!)
477 void LinkManager::CancelTransfers()
480 const sfx2::SvBaseLinks& rLnks = GetLinks();
481 for( size_t n = rLnks.size(); n; )
483 const sfx2::SvBaseLink& rLnk = *rLnks[--n];
484 if (isClientFileType(rLnk.GetObjType()))
486 if (SvFileObject* pFileObj = static_cast<SvFileObject*>(rLnk.GetObj()))
487 pFileObj->CancelTransfers();
492 // For the purpose of sending Status information from the file object to
493 // the base link, there exist a dedicated ClipBoardId. The SvData-object
494 // gets the appropriate information as a string
495 // For now this is required for file object in conjunction with JavaScript
496 // - needs information about Load/Abort/Error
497 SotClipboardFormatId LinkManager::RegisterStatusInfoId()
499 static SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
501 if( nFormat == SotClipboardFormatId::NONE )
503 nFormat = SotExchange::RegisterFormatName(
504 "StatusInfo from SvxInternalLink");
506 return nFormat;
509 bool LinkManager::GetGraphicFromAny(const OUString& rMimeType,
510 const css::uno::Any & rValue,
511 Graphic& rGraphic,
512 weld::Window* pParentWin)
514 bool bRet = false;
516 if (!rValue.hasValue())
517 return bRet;
519 if (rValue.has<OUString>())
521 OUString sReferer;
522 SfxObjectShell* sh = GetPersist();
523 if (sh && sh->HasName())
524 sReferer = sh->GetMedium()->GetName();
526 OUString sURL = rValue.get<OUString>();
527 if (!SvtSecurityOptions().isUntrustedReferer(sReferer))
528 rGraphic = vcl::graphic::loadFromURL(sURL, pParentWin);
529 if (rGraphic.IsNone())
530 rGraphic.SetDefaultType();
531 rGraphic.setOriginURL(sURL);
532 return true;
534 else if (rValue.has<css::uno::Sequence<sal_Int8>>())
536 auto aSeq = rValue.get<css::uno::Sequence<sal_Int8>>();
538 SvMemoryStream aMemStm( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(),
539 StreamMode::READ );
540 aMemStm.Seek( 0 );
542 switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
544 case SotClipboardFormatId::SVXB:
546 ReadGraphic( aMemStm, rGraphic );
547 bRet = true;
549 break;
550 case SotClipboardFormatId::GDIMETAFILE:
552 GDIMetaFile aMtf;
553 aMtf.Read( aMemStm );
554 rGraphic = aMtf;
555 bRet = true;
557 break;
558 case SotClipboardFormatId::BITMAP:
560 Bitmap aBmp;
561 ReadDIB(aBmp, aMemStm, true);
562 rGraphic = BitmapEx(aBmp);
563 bRet = true;
565 break;
566 default: break;
569 return bRet;
572 static OUString lcl_DDE_RelToAbs( const OUString& rTopic, const OUString& rBaseURL )
574 OUString sRet;
575 INetURLObject aURL( rTopic );
576 if( INetProtocol::NotValid == aURL.GetProtocol() )
577 osl::FileBase::getFileURLFromSystemPath(rTopic, sRet);
578 if( sRet.isEmpty() )
579 sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl() );
580 return sRet;
583 bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink )
585 SfxObjectShell* pFndShell = nullptr;
586 sal_uInt16 nUpdateMode = css::document::UpdateDocMode::NO_UPDATE;
587 OUString sTopic, sItem, sReferer;
588 LinkManager* pLinkMgr = pLink->GetLinkManager();
589 if (pLinkMgr && sfx2::LinkManager::GetDisplayNames(pLink, nullptr, &sTopic, &sItem) && !sTopic.isEmpty())
591 // first only loop over the DocumentShells the shells and find those
592 // with the name:
593 CharClass aCC( LanguageTag( LANGUAGE_SYSTEM) );
595 bool bFirst = true;
596 SfxObjectShell* pShell = pLinkMgr->GetPersist();
597 if( pShell && pShell->GetMedium() )
599 sReferer = pShell->GetMedium()->GetBaseURL();
600 const SfxUInt16Item* pItem = SfxItemSet::GetItem<SfxUInt16Item>(pShell->GetMedium()->GetItemSet(), SID_UPDATEDOCMODE, false);
601 if ( pItem )
602 nUpdateMode = pItem->GetValue();
605 OUString sNmURL(aCC.lowercase(lcl_DDE_RelToAbs(sTopic, sReferer)));
607 if ( !pShell )
609 bFirst = false;
610 pShell = SfxObjectShell::GetFirst( nullptr, false );
613 OUString sTmp;
614 while( pShell )
616 if( sTmp.isEmpty() )
618 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME );
619 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer );
623 sTmp = aCC.lowercase( sTmp );
624 if( sTmp == sNmURL ) // we want these
626 pFndShell = pShell;
627 break;
630 if( bFirst )
632 bFirst = false;
633 pShell = SfxObjectShell::GetFirst( nullptr, false );
635 else
636 pShell = SfxObjectShell::GetNext( *pShell, nullptr, false );
638 sTmp.clear();
642 // empty topics are not allowed - which document is it
643 if( sTopic.isEmpty() )
644 return false;
646 if (pFndShell)
648 sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem );
649 if( pNewSrc )
651 css::datatransfer::DataFlavor aFl;
652 SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl );
654 pLink->SetObj( pNewSrc );
655 pNewSrc->AddDataAdvise( pLink, aFl.MimeType,
656 SfxLinkUpdateMode::ONCALL == pLink->GetUpdateMode()
657 ? ADVISEMODE_ONLYONCE
658 : 0 );
659 return true;
662 else
664 // then try to download the file:
665 INetURLObject aURL( sTopic );
666 INetProtocol eOld = aURL.GetProtocol();
667 sTopic = lcl_DDE_RelToAbs( sTopic, sReferer );
668 aURL.SetURL( sTopic );
669 if( INetProtocol::NotValid != eOld ||
670 INetProtocol::Http != aURL.GetProtocol() )
672 SfxStringItem aName( SID_FILE_NAME, sTopic );
673 SfxBoolItem aMinimized(SID_MINIMIZED, true);
674 SfxBoolItem aHidden(SID_HIDDEN, true);
675 SfxStringItem aTarget( SID_TARGETNAME, "_blank" );
676 SfxStringItem aReferer( SID_REFERER, sReferer );
677 SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode );
678 SfxBoolItem aReadOnly(SID_DOC_READONLY, false);
680 // Disable automatic re-connection to avoid this link instance
681 // being destroyed at re-connection.
682 SfxBoolItem aDdeConnect(SID_DDE_RECONNECT_ONLOAD, false);
684 // #i14200# (DDE-link crashes wordprocessor)
685 SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
686 aArgs.Put(aReferer);
687 aArgs.Put(aTarget);
688 aArgs.Put(aHidden);
689 aArgs.Put(aMinimized);
690 aArgs.Put(aName);
691 aArgs.Put(aUpdate);
692 aArgs.Put(aReadOnly);
693 aArgs.Put(aDdeConnect);
694 Reference<XComponent> xComp = SfxObjectShell::CreateAndLoadComponent(aArgs);
695 pFndShell = SfxObjectShell::GetShellFromComponent(xComp);
696 if (xComp.is() && pFndShell && pLinkMgr)
698 pLinkMgr->InsertCachedComp(xComp);
699 sfx2::LinkManager::LinkServerShell(sItem, *pFndShell, *pLink);
700 return true;
705 return false;
710 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */