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/filter/SvmReader.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/weld.hxx>
34 #include <vcl/gdimtf.hxx>
35 #include <sfx2/lnkbase.hxx>
36 #include <sfx2/app.hxx>
37 #include <vcl/graph.hxx>
38 #include <svl/stritem.hxx>
39 #include <svl/eitem.hxx>
40 #include <svl/intitem.hxx>
41 #include <i18nlangtag/languagetag.hxx>
42 #include <vcl/dibtools.hxx>
43 #include <unotools/charclass.hxx>
44 #include <unotools/securityoptions.hxx>
45 #include <vcl/GraphicLoader.hxx>
46 #include <vcl/TypeSerializer.hxx>
48 #include "fileobj.hxx"
49 #include "impldde.hxx"
50 #include <sfx2/strings.hrc>
51 #include <sfx2/sfxresid.hxx>
53 #include <com/sun/star/lang/XComponent.hpp>
54 #include <com/sun/star/util/XCloseable.hpp>
56 using ::com::sun::star::uno::UNO_QUERY
;
57 using ::com::sun::star::uno::Reference
;
58 using ::com::sun::star::lang::XComponent
;
59 using ::com::sun::star::util::XCloseable
;
66 class SvxInternalLink
: public sfx2::SvLinkSource
71 virtual bool Connect( sfx2::SvBaseLink
* ) override
;
76 LinkManager::LinkManager(SfxObjectShell
* p
)
81 LinkManager::~LinkManager()
83 for(tools::SvRef
<SvBaseLink
> & rTmp
: aLinkTbl
)
88 rTmp
->SetLinkManager( nullptr );
93 void LinkManager::InsertCachedComp(const Reference
<XComponent
>& xComp
)
95 maCachedComps
.push_back(xComp
);
98 void LinkManager::CloseCachedComps()
100 for (const auto& rxCachedComp
: maCachedComps
)
102 Reference
<XCloseable
> xCloseable(rxCachedComp
, UNO_QUERY
);
103 if (!xCloseable
.is())
106 xCloseable
->close(true);
108 maCachedComps
.clear();
111 void LinkManager::Remove( SvBaseLink
const *pLink
)
113 // No duplicate links inserted
115 for( size_t n
= 0; n
< aLinkTbl
.size(); )
117 tools::SvRef
<SvBaseLink
>& rTmp
= aLinkTbl
[ n
];
118 if( pLink
== rTmp
.get() )
121 rTmp
->SetLinkManager( nullptr );
126 // Remove empty ones if they exist
129 aLinkTbl
.erase( aLinkTbl
.begin() + n
);
138 void LinkManager::Remove( size_t nPos
, size_t nCnt
)
140 if( !nCnt
|| nPos
>= aLinkTbl
.size() )
143 if (sal::static_int_cast
<size_t>(nPos
+ nCnt
) > aLinkTbl
.size())
144 nCnt
= aLinkTbl
.size() - nPos
;
146 for( size_t n
= nPos
; n
< nPos
+ nCnt
; ++n
)
148 tools::SvRef
<SvBaseLink
>& rTmp
= aLinkTbl
[ n
];
152 rTmp
->SetLinkManager( nullptr );
155 aLinkTbl
.erase( aLinkTbl
.begin() + nPos
, aLinkTbl
.begin() + nPos
+ nCnt
);
158 bool LinkManager::Insert( SvBaseLink
* pLink
)
160 for( size_t n
= 0; n
< aLinkTbl
.size(); ++n
)
162 tools::SvRef
<SvBaseLink
>& rTmp
= aLinkTbl
[ n
];
165 aLinkTbl
.erase( aLinkTbl
.begin() + n
-- );
167 else if( pLink
== rTmp
.get() )
168 return false; // No duplicate links inserted
171 pLink
->SetLinkManager( this );
172 aLinkTbl
.emplace_back(pLink
);
176 bool LinkManager::InsertLink( SvBaseLink
* pLink
,
177 SvBaseLinkObjectType nObjType
,
178 SfxLinkUpdateMode nUpdateMode
,
179 const OUString
* pName
)
182 pLink
->SetObjType( nObjType
);
184 pLink
->SetName( *pName
);
185 pLink
->SetUpdateMode( nUpdateMode
);
186 return Insert( pLink
);
189 void LinkManager::InsertDDELink( SvBaseLink
* pLink
,
190 const OUString
& rServer
,
191 std::u16string_view rTopic
,
192 std::u16string_view rItem
)
194 if( !isClientType( pLink
->GetObjType() ) )
198 ::sfx2::MakeLnkName( sCmd
, &rServer
, rTopic
, rItem
);
200 pLink
->SetObjType( SvBaseLinkObjectType::ClientDde
);
201 pLink
->SetName( sCmd
);
205 void LinkManager::InsertDDELink( SvBaseLink
* pLink
)
207 DBG_ASSERT( isClientType(pLink
->GetObjType()), "no OBJECT_CLIENT_SO" );
208 if( !isClientType( pLink
->GetObjType() ) )
211 if( pLink
->GetObjType() == SvBaseLinkObjectType::ClientSo
)
212 pLink
->SetObjType( SvBaseLinkObjectType::ClientDde
);
217 // Obtain the string for the dialog
218 bool LinkManager::GetDisplayNames( const SvBaseLink
* pLink
,
225 const OUString
& sLNm( pLink
->GetLinkSourceName() );
226 if( !sLNm
.isEmpty() )
228 switch( pLink
->GetObjType() )
230 case SvBaseLinkObjectType::ClientFile
:
231 case SvBaseLinkObjectType::ClientGraphic
:
232 case SvBaseLinkObjectType::ClientOle
:
235 OUString
sFile( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
236 OUString
sRange( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
243 *pFilter
= nPos
== -1 ? OUString() : sLNm
.copy(nPos
);
247 SvBaseLinkObjectType nObjType
= pLink
->GetObjType();
249 ( SvBaseLinkObjectType::ClientFile
== nObjType
|| SvBaseLinkObjectType::ClientOle
== nObjType
)
250 ? RID_SVXSTR_FILELINK
251 : RID_SVXSTR_GRAPHICLINK
);
256 case SvBaseLinkObjectType::ClientDde
:
259 OUString
sServer( sLNm
.getToken( 0, cTokenSeparator
, nTmp
) );
260 OUString
sTopic( sLNm
.getToken( 0, cTokenSeparator
, nTmp
) );
267 *pLinkStr
= nTmp
!= -1 ? sLNm
.copy(nTmp
) : OUString();
279 void LinkManager::UpdateAllLinks(
281 bool bUpdateGrfLinks
,
282 weld::Window
* pParentWin
)
284 // First make a copy of the array in order to update links
285 // links in ... no contact between them!
286 std::vector
<SvBaseLink
*> aTmpArr
;
287 for( size_t n
= 0; n
< aLinkTbl
.size(); ++n
)
289 tools::SvRef
<SvBaseLink
>& rLink
= aLinkTbl
[ n
];
295 aTmpArr
.push_back( rLink
.get() );
298 for(SvBaseLink
* pLink
: aTmpArr
)
300 // search first in the array after the entry
302 for(const tools::SvRef
<SvBaseLink
> & i
: aLinkTbl
)
303 if( pLink
== i
.get() )
310 continue; // was not available!
312 // Graphic-Links not to update yet
313 if( !pLink
->IsVisible() ||
314 ( !bUpdateGrfLinks
&& SvBaseLinkObjectType::ClientGraphic
== pLink
->GetObjType() ))
319 OUString aMsg
= SfxResId(STR_QUERY_UPDATE_LINKS
);
320 INetURLObject
aURL(pPersist
->getDocumentBaseURL());
321 aMsg
= aMsg
.replaceFirst("%{filename}", aURL
.GetLastName());
323 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(pParentWin
,
324 VclMessageType::Question
, VclButtonsType::YesNo
, aMsg
));
325 xQueryBox
->set_default_response(RET_YES
);
327 int nRet
= xQueryBox
->run();
328 if( RET_YES
!= nRet
)
330 SfxObjectShell
* pShell
= pLink
->GetLinkManager()->GetPersist();
334 comphelper::EmbeddedObjectContainer
& rEmbeddedObjectContainer
= pShell
->getEmbeddedObjectContainer();
335 rEmbeddedObjectContainer
.setUserAllowsLinkUpdate(false);
338 return ; // nothing should be updated
340 bAskUpdate
= false; // once is enough
348 SvLinkSourceRef
LinkManager::CreateObj( SvBaseLink
const * pLink
)
350 switch( pLink
->GetObjType() )
352 case SvBaseLinkObjectType::ClientFile
:
353 case SvBaseLinkObjectType::ClientGraphic
:
354 case SvBaseLinkObjectType::ClientOle
:
355 return new SvFileObject
;
356 case SvBaseLinkObjectType::Internal
:
357 return new SvxInternalLink
;
358 case SvBaseLinkObjectType::ClientDde
:
359 return new SvDDEObject
;
361 return SvLinkSourceRef();
365 bool LinkManager::InsertServer( SvLinkSource
* pObj
)
367 // no duplicate inserts
371 return aServerTbl
.insert( pObj
).second
;
374 void LinkManager::RemoveServer( SvLinkSource
* pObj
)
376 aServerTbl
.erase( pObj
);
379 void MakeLnkName( OUString
& rName
, const OUString
* pType
, std::u16string_view rFile
,
380 std::u16string_view rLink
, const OUString
* pFilter
)
384 rName
= comphelper::string::strip(*pType
, ' ')
385 + OUStringChar(cTokenSeparator
);
392 rName
= comphelper::string::strip(rName
, ' ')
393 + OUStringChar(cTokenSeparator
);
394 rName
= comphelper::string::strip(rName
, ' ') + rLink
;
397 rName
+= OUStringChar(cTokenSeparator
) + *pFilter
;
398 rName
= comphelper::string::strip(rName
, ' ');
402 void LinkManager::ReconnectDdeLink(SfxObjectShell
& rServer
)
404 SfxMedium
* pMed
= rServer
.GetMedium();
408 const ::sfx2::SvBaseLinks
& rLinks
= GetLinks();
409 size_t n
= rLinks
.size();
411 for (size_t i
= 0; i
< n
; ++i
)
413 ::sfx2::SvBaseLink
* p
= rLinks
[i
].get();
414 OUString aType
, aFile
, aLink
, aFilter
;
415 if (!GetDisplayNames(p
, &aType
, &aFile
, &aLink
, &aFilter
))
418 if (aType
!= "soffice")
419 // DDE connections between OOo apps are always named 'soffice'.
423 OUString aURL
= aFile
;
424 if (osl::FileBase::getFileURLFromSystemPath(aFile
, aTmp
)
425 == osl::FileBase::E_None
)
428 if (!aURL
.equalsIgnoreAsciiCase(pMed
->GetName()))
429 // This DDE link is not associated with this server shell... Skip it.
435 LinkServerShell(aLink
, rServer
, *p
);
439 void LinkManager::LinkServerShell(const OUString
& rPath
, SfxObjectShell
& rServer
, ::sfx2::SvBaseLink
& rLink
)
441 ::sfx2::SvLinkSource
* pSrvSrc
= rServer
.DdeCreateLinkSource(rPath
);
444 css::datatransfer::DataFlavor aFl
;
445 SotExchange::GetFormatDataFlavor(rLink
.GetContentType(), aFl
);
446 rLink
.SetObj(pSrvSrc
);
447 pSrvSrc
->AddDataAdvise(
448 &rLink
, aFl
.MimeType
,
449 SfxLinkUpdateMode::ONCALL
== rLink
.GetUpdateMode() ? ADVISEMODE_ONLYONCE
: 0);
453 void LinkManager::InsertFileLink(
454 sfx2::SvBaseLink
& rLink
, SvBaseLinkObjectType nFileType
, std::u16string_view rFileNm
,
455 const OUString
* pFilterNm
, const OUString
* pRange
)
457 if (!isClientType(rLink
.GetObjType()))
460 OUStringBuffer
aBuf(64);
461 aBuf
.append(rFileNm
);
462 aBuf
.append(sfx2::cTokenSeparator
);
465 aBuf
.append(*pRange
);
469 aBuf
.append(sfx2::cTokenSeparator
);
470 aBuf
.append(*pFilterNm
);
473 OUString aCmd
= aBuf
.makeStringAndClear();
474 InsertLink(&rLink
, nFileType
, SfxLinkUpdateMode::ONCALL
, &aCmd
);
477 // A transfer is aborted, so cancel all download media
478 // (for now this is only of interest for the file links!)
479 void LinkManager::CancelTransfers()
482 const sfx2::SvBaseLinks
& rLnks
= GetLinks();
483 for( size_t n
= rLnks
.size(); n
; )
485 const sfx2::SvBaseLink
& rLnk
= *rLnks
[--n
];
486 if (isClientFileType(rLnk
.GetObjType()))
488 if (SvFileObject
* pFileObj
= static_cast<SvFileObject
*>(rLnk
.GetObj()))
489 pFileObj
->CancelTransfers();
494 // For the purpose of sending Status information from the file object to
495 // the base link, there exist a dedicated ClipBoardId. The SvData-object
496 // gets the appropriate information as a string
497 // For now this is required for file object in conjunction with JavaScript
498 // - needs information about Load/Abort/Error
499 SotClipboardFormatId
LinkManager::RegisterStatusInfoId()
501 static SotClipboardFormatId nFormat
= SotClipboardFormatId::NONE
;
503 if( nFormat
== SotClipboardFormatId::NONE
)
505 nFormat
= SotExchange::RegisterFormatName(
506 "StatusInfo from SvxInternalLink");
511 bool LinkManager::GetGraphicFromAny(const OUString
& rMimeType
,
512 const css::uno::Any
& rValue
,
514 weld::Window
* pParentWin
)
518 if (!rValue
.hasValue())
521 if (rValue
.has
<OUString
>())
524 SfxObjectShell
* sh
= GetPersist();
525 if (sh
&& sh
->HasName())
526 sReferer
= sh
->GetMedium()->GetName();
528 OUString sURL
= rValue
.get
<OUString
>();
529 if (!SvtSecurityOptions::isUntrustedReferer(sReferer
))
530 rGraphic
= vcl::graphic::loadFromURL(sURL
, pParentWin
);
531 if (rGraphic
.IsNone())
532 rGraphic
.SetDefaultType();
533 rGraphic
.setOriginURL(sURL
);
536 else if (rValue
.has
<css::uno::Sequence
<sal_Int8
>>())
538 auto aSeq
= rValue
.get
<css::uno::Sequence
<sal_Int8
>>();
540 SvMemoryStream
aMemStm( const_cast<sal_Int8
*>(aSeq
.getConstArray()), aSeq
.getLength(),
544 switch( SotExchange::GetFormatIdFromMimeType( rMimeType
) )
546 case SotClipboardFormatId::SVXB
:
548 TypeSerializer
aSerializer(aMemStm
);
549 aSerializer
.readGraphic(rGraphic
);
553 case SotClipboardFormatId::GDIMETAFILE
:
556 SvmReader
aReader( aMemStm
);
557 aReader
.Read( aMtf
);
562 case SotClipboardFormatId::BITMAP
:
565 ReadDIB(aBmp
, aMemStm
, true);
566 rGraphic
= BitmapEx(aBmp
);
576 static OUString
lcl_DDE_RelToAbs( const OUString
& rTopic
, const OUString
& rBaseURL
)
579 INetURLObject
aURL( rTopic
);
580 if( INetProtocol::NotValid
== aURL
.GetProtocol() )
581 osl::FileBase::getFileURLFromSystemPath(rTopic
, sRet
);
583 sRet
= URIHelper::SmartRel2Abs( INetURLObject(rBaseURL
), rTopic
, URIHelper::GetMaybeFileHdl() );
587 bool SvxInternalLink::Connect( sfx2::SvBaseLink
* pLink
)
589 SfxObjectShell
* pFndShell
= nullptr;
590 sal_uInt16 nUpdateMode
= css::document::UpdateDocMode::NO_UPDATE
;
591 OUString sTopic
, sItem
, sReferer
;
592 LinkManager
* pLinkMgr
= pLink
->GetLinkManager();
593 if (pLinkMgr
&& sfx2::LinkManager::GetDisplayNames(pLink
, nullptr, &sTopic
, &sItem
) && !sTopic
.isEmpty())
595 // first only loop over the DocumentShells the shells and find those
597 CharClass
aCC( LanguageTag( LANGUAGE_SYSTEM
) );
600 SfxObjectShell
* pShell
= pLinkMgr
->GetPersist();
601 if( pShell
&& pShell
->GetMedium() )
603 sReferer
= pShell
->GetMedium()->GetBaseURL();
604 const SfxUInt16Item
* pItem
= SfxItemSet::GetItem
<SfxUInt16Item
>(pShell
->GetMedium()->GetItemSet(), SID_UPDATEDOCMODE
, false);
606 nUpdateMode
= pItem
->GetValue();
609 OUString
sNmURL(aCC
.lowercase(lcl_DDE_RelToAbs(sTopic
, sReferer
)));
614 pShell
= SfxObjectShell::GetFirst( nullptr, false );
622 sTmp
= pShell
->GetTitle( SFX_TITLE_FULLNAME
);
623 sTmp
= lcl_DDE_RelToAbs(sTmp
, sReferer
);
627 sTmp
= aCC
.lowercase( sTmp
);
628 if( sTmp
== sNmURL
) // we want these
637 pShell
= SfxObjectShell::GetFirst( nullptr, false );
640 pShell
= SfxObjectShell::GetNext( *pShell
, nullptr, false );
646 // empty topics are not allowed - which document is it
647 if( sTopic
.isEmpty() )
652 sfx2::SvLinkSource
* pNewSrc
= pFndShell
->DdeCreateLinkSource( sItem
);
655 css::datatransfer::DataFlavor aFl
;
656 SotExchange::GetFormatDataFlavor( pLink
->GetContentType(), aFl
);
658 pLink
->SetObj( pNewSrc
);
659 pNewSrc
->AddDataAdvise( pLink
, aFl
.MimeType
,
660 SfxLinkUpdateMode::ONCALL
== pLink
->GetUpdateMode()
661 ? ADVISEMODE_ONLYONCE
668 // then try to download the file:
669 INetURLObject
aURL( sTopic
);
670 INetProtocol eOld
= aURL
.GetProtocol();
671 sTopic
= lcl_DDE_RelToAbs( sTopic
, sReferer
);
672 aURL
.SetURL( sTopic
);
673 if( INetProtocol::NotValid
!= eOld
||
674 INetProtocol::Http
!= aURL
.GetProtocol() )
676 SfxStringItem
aName( SID_FILE_NAME
, sTopic
);
677 SfxBoolItem
aMinimized(SID_MINIMIZED
, true);
678 SfxBoolItem
aHidden(SID_HIDDEN
, true);
679 SfxStringItem
aTarget( SID_TARGETNAME
, "_blank" );
680 SfxStringItem
aReferer( SID_REFERER
, sReferer
);
681 SfxUInt16Item
aUpdate( SID_UPDATEDOCMODE
, nUpdateMode
);
682 SfxBoolItem
aReadOnly(SID_DOC_READONLY
, false);
684 // Disable automatic re-connection to avoid this link instance
685 // being destroyed at re-connection.
686 SfxBoolItem
aDdeConnect(SID_DDE_RECONNECT_ONLOAD
, false);
688 // #i14200# (DDE-link crashes wordprocessor)
689 SfxAllItemSet
aArgs( SfxGetpApp()->GetPool() );
693 aArgs
.Put(aMinimized
);
696 aArgs
.Put(aReadOnly
);
697 aArgs
.Put(aDdeConnect
);
698 Reference
<XComponent
> xComp
= SfxObjectShell::CreateAndLoadComponent(aArgs
);
699 pFndShell
= SfxObjectShell::GetShellFromComponent(xComp
);
700 if (xComp
.is() && pFndShell
&& pLinkMgr
)
702 pLinkMgr
->InsertCachedComp(xComp
);
703 sfx2::LinkManager::LinkServerShell(sItem
, *pFndShell
, *pLink
);
714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */