1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
;
64 class SvxInternalLink
: public sfx2::SvLinkSource
69 virtual bool Connect( sfx2::SvBaseLink
* ) override
;
74 LinkManager::LinkManager(SfxObjectShell
* p
)
79 LinkManager::~LinkManager()
81 for(tools::SvRef
<SvBaseLink
> & rTmp
: aLinkTbl
)
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())
104 xCloseable
->close(true);
106 maCachedComps
.clear();
109 void LinkManager::Remove( SvBaseLink
const *pLink
)
111 // No duplicate links inserted
113 for( size_t n
= 0; n
< aLinkTbl
.size(); )
115 tools::SvRef
<SvBaseLink
>& rTmp
= aLinkTbl
[ n
];
116 if( pLink
== rTmp
.get() )
119 rTmp
->SetLinkManager( nullptr );
124 // Remove empty ones if they exist
127 aLinkTbl
.erase( aLinkTbl
.begin() + n
);
136 void LinkManager::Remove( size_t nPos
, size_t nCnt
)
138 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 tools::SvRef
<SvBaseLink
>& rTmp
= aLinkTbl
[ n
];
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
];
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
);
174 bool LinkManager::InsertLink( SvBaseLink
* pLink
,
175 SvBaseLinkObjectType nObjType
,
176 SfxLinkUpdateMode nUpdateMode
,
177 const OUString
* pName
)
180 pLink
->SetObjType( nObjType
);
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() ) )
196 ::sfx2::MakeLnkName( sCmd
, &rServer
, rTopic
, rItem
);
198 pLink
->SetObjType( SvBaseLinkObjectType::ClientDde
);
199 pLink
->SetName( sCmd
);
203 void LinkManager::InsertDDELink( SvBaseLink
* pLink
)
205 DBG_ASSERT( isClientType(pLink
->GetObjType()), "no OBJECT_CLIENT_SO" );
206 if( !isClientType( pLink
->GetObjType() ) )
209 if( pLink
->GetObjType() == SvBaseLinkObjectType::ClientSo
)
210 pLink
->SetObjType( SvBaseLinkObjectType::ClientDde
);
215 // Obtain the string for the dialog
216 bool LinkManager::GetDisplayNames( const SvBaseLink
* pLink
,
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
:
233 OUString
sFile( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
234 OUString
sRange( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
241 *pFilter
= nPos
== -1 ? OUString() : sLNm
.copy(nPos
);
245 SvBaseLinkObjectType nObjType
= pLink
->GetObjType();
247 ( SvBaseLinkObjectType::ClientFile
== nObjType
|| SvBaseLinkObjectType::ClientOle
== nObjType
)
248 ? RID_SVXSTR_FILELINK
249 : RID_SVXSTR_GRAFIKLINK
);
254 case SvBaseLinkObjectType::ClientDde
:
257 OUString
sServer( sLNm
.getToken( 0, cTokenSeparator
, nTmp
) );
258 OUString
sTopic( sLNm
.getToken( 0, cTokenSeparator
, nTmp
) );
265 *pLinkStr
= nTmp
!= -1 ? sLNm
.copy(nTmp
) : OUString();
277 void LinkManager::UpdateAllLinks(
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
];
293 aTmpArr
.push_back( rLink
.get() );
296 for(SvBaseLink
* pLink
: aTmpArr
)
298 // search first in the array after the entry
300 for(const tools::SvRef
<SvBaseLink
> & i
: aLinkTbl
)
301 if( pLink
== i
.get() )
308 continue; // was not available!
310 // Graphic-Links not to update yet
311 if( !pLink
->IsVisible() ||
312 ( !bUpdateGrfLinks
&& SvBaseLinkObjectType::ClientGraphic
== pLink
->GetObjType() ))
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();
332 comphelper::EmbeddedObjectContainer
& rEmbeddedObjectContainer
= pShell
->getEmbeddedObjectContainer();
333 rEmbeddedObjectContainer
.setUserAllowsLinkUpdate(false);
336 return ; // nothing should be updated
338 bAskUpdate
= false; // once is enough
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
;
359 return SvLinkSourceRef();
363 bool LinkManager::InsertServer( SvLinkSource
* pObj
)
365 // no duplicate inserts
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
)
382 rName
= comphelper::string::strip(*pType
, ' ')
383 + OUStringChar(cTokenSeparator
);
390 rName
= comphelper::string::strip(rName
, ' ')
391 + OUStringChar(cTokenSeparator
);
392 rName
= comphelper::string::strip(rName
, ' ') + rLink
;
395 rName
+= OUStringChar(cTokenSeparator
) + *pFilter
;
396 rName
= comphelper::string::strip(rName
, ' ');
400 void LinkManager::ReconnectDdeLink(SfxObjectShell
& rServer
)
402 SfxMedium
* pMed
= rServer
.GetMedium();
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
))
416 if (aType
!= "soffice")
417 // DDE connections between OOo apps are always named 'soffice'.
421 OUString aURL
= aFile
;
422 if (osl::FileBase::getFileURLFromSystemPath(aFile
, aTmp
)
423 == osl::FileBase::E_None
)
426 if (!aURL
.equalsIgnoreAsciiCase(pMed
->GetName()))
427 // This DDE link is not associated with this server shell... Skip it.
433 LinkServerShell(aLink
, rServer
, *p
);
437 void LinkManager::LinkServerShell(const OUString
& rPath
, SfxObjectShell
& rServer
, ::sfx2::SvBaseLink
& rLink
)
439 ::sfx2::SvLinkSource
* pSrvSrc
= rServer
.DdeCreateLinkSource(rPath
);
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()))
458 OUStringBuffer
aBuf(64);
459 aBuf
.append(rFileNm
);
460 aBuf
.append(sfx2::cTokenSeparator
);
463 aBuf
.append(*pRange
);
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");
509 bool LinkManager::GetGraphicFromAny(const OUString
& rMimeType
,
510 const css::uno::Any
& rValue
,
512 weld::Window
* pParentWin
)
516 if (!rValue
.hasValue())
519 if (rValue
.has
<OUString
>())
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
);
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(),
542 switch( SotExchange::GetFormatIdFromMimeType( rMimeType
) )
544 case SotClipboardFormatId::SVXB
:
546 ReadGraphic( aMemStm
, rGraphic
);
550 case SotClipboardFormatId::GDIMETAFILE
:
553 aMtf
.Read( aMemStm
);
558 case SotClipboardFormatId::BITMAP
:
561 ReadDIB(aBmp
, aMemStm
, true);
562 rGraphic
= BitmapEx(aBmp
);
572 static OUString
lcl_DDE_RelToAbs( const OUString
& rTopic
, const OUString
& rBaseURL
)
575 INetURLObject
aURL( rTopic
);
576 if( INetProtocol::NotValid
== aURL
.GetProtocol() )
577 osl::FileBase::getFileURLFromSystemPath(rTopic
, sRet
);
579 sRet
= URIHelper::SmartRel2Abs( INetURLObject(rBaseURL
), rTopic
, URIHelper::GetMaybeFileHdl() );
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
593 CharClass
aCC( LanguageTag( LANGUAGE_SYSTEM
) );
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);
602 nUpdateMode
= pItem
->GetValue();
605 OUString
sNmURL(aCC
.lowercase(lcl_DDE_RelToAbs(sTopic
, sReferer
)));
610 pShell
= SfxObjectShell::GetFirst( nullptr, false );
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
633 pShell
= SfxObjectShell::GetFirst( nullptr, false );
636 pShell
= SfxObjectShell::GetNext( *pShell
, nullptr, false );
642 // empty topics are not allowed - which document is it
643 if( sTopic
.isEmpty() )
648 sfx2::SvLinkSource
* pNewSrc
= pFndShell
->DdeCreateLinkSource( sItem
);
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
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() );
689 aArgs
.Put(aMinimized
);
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
);
710 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */