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 <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"
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
;
58 class SvxInternalLink
: public sfx2::SvLinkSource
63 virtual bool Connect( sfx2::SvBaseLink
* ) SAL_OVERRIDE
;
67 LinkManager::LinkManager(SfxObjectShell
* p
)
73 LinkManager::~LinkManager()
75 for( size_t n
= 0; n
< aLinkTbl
.size(); ++n
)
77 SvBaseLinkRef
* pTmp
= aLinkTbl
[ n
];
80 (*pTmp
)->Disconnect();
81 (*pTmp
)->SetLinkManager( NULL
);
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
);
101 xCloseable
->close(true);
103 maCachedComps
.clear();
108 void LinkManager::Remove( SvBaseLink
*pLink
)
110 // No duplicate links inserted
112 for( size_t n
= 0; n
< aLinkTbl
.size(); )
114 SvBaseLinkRef
* pTmp
= aLinkTbl
[ n
];
117 (*pTmp
)->Disconnect();
118 (*pTmp
)->SetLinkManager( NULL
);
123 // Remove empty ones if they exist
127 aLinkTbl
.erase( aLinkTbl
.begin() + 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
];
149 (*pTmp
)->Disconnect();
150 (*pTmp
)->SetLinkManager( NULL
);
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
];
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
);
180 bool LinkManager::InsertLink( SvBaseLink
* pLink
,
182 SfxLinkUpdateMode nUpdateMode
,
183 const OUString
* pName
)
186 pLink
->SetObjType( nObjType
);
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() ) )
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() ) )
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
,
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
:
242 OUString
sFile( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
243 OUString
sRange( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
250 *pFilter
= nPos
== -1 ? OUString() : sLNm
.copy(nPos
);
254 sal_uInt16 nObjType
= pLink
->GetObjType();
256 ( OBJECT_CLIENT_FILE
== nObjType
|| OBJECT_CLIENT_OLE
== nObjType
)
257 ? RID_SVXSTR_FILELINK
258 : RID_SVXSTR_GRAFIKLINK
).toString();
263 case OBJECT_CLIENT_DDE
:
266 OUString
sCmd( sLNm
);
267 OUString
sServer( sCmd
.getToken( 0, cTokenSeparator
, nTmp
) );
268 OUString
sTopic( sCmd
.getToken( 0, cTokenSeparator
, nTmp
) );
275 *pLinkStr
= nTmp
!= -1 ? sCmd
.copy(nTmp
) : OUString();
287 void LinkManager::UpdateAllLinks(
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
];
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
313 for( size_t i
= 0; i
< aLinkTbl
.size(); ++i
)
314 if( pLink
== *aLinkTbl
[ i
] )
321 continue; // was not available!
323 // Graphic-Links not to update yet
324 if( !pLink
->IsVisible() ||
325 ( !bUpdateGrfLinks
&& OBJECT_CLIENT_GRF
== pLink
->GetObjType() ))
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();
337 comphelper::EmbeddedObjectContainer
& rEmbeddedObjectContainer
= pShell
->getEmbeddedObjectContainer();
338 rEmbeddedObjectContainer
.setUserAllowsLinkUpdate(false);
341 return ; // nothing should be updated
343 bAskUpdate
= false; // once is enough
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
;
362 return new SvxInternalLink
;
363 case OBJECT_CLIENT_DDE
:
364 return new SvDDEObject
;
366 return SvLinkSourceRef();
370 bool LinkManager::InsertServer( SvLinkSource
* pObj
)
372 // no duplicate inserts
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
)
391 rName
= comphelper::string::strip(*pType
, ' ');
392 rName
+= OUString(cTokenSeparator
);
394 else if( !rName
.isEmpty() )
399 rName
= comphelper::string::strip(rName
, ' ');
400 rName
+= OUString(cTokenSeparator
);
401 rName
= comphelper::string::strip(rName
, ' ');
405 rName
+= OUString(cTokenSeparator
);
407 rName
= comphelper::string::strip(rName
, ' ');
411 void LinkManager::ReconnectDdeLink(SfxObjectShell
& rServer
)
413 SfxMedium
* pMed
= rServer
.GetMedium();
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
))
427 if (aType
!= "soffice")
428 // DDE connections between OOo apps are always named 'soffice'.
432 OUString aURL
= aFile
;
433 if (utl::LocalFileHelper::ConvertPhysicalNameToURL(aFile
, aTmp
))
436 if (!aURL
.equalsIgnoreAsciiCase(pMed
->GetName()))
437 // This DDE link is not associated with this server shell... Skip it.
443 LinkServerShell(aLink
, rServer
, *p
);
447 void LinkManager::LinkServerShell(const OUString
& rPath
, SfxObjectShell
& rServer
, ::sfx2::SvBaseLink
& rLink
)
449 ::sfx2::SvLinkSource
* pSrvSrc
= rServer
.DdeCreateLinkSource(rPath
);
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()))
469 aBuf
.append(rFileNm
);
470 aBuf
.append(sfx2::cTokenSeparator
);
473 aBuf
.append(*pRange
);
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"));
518 bool LinkManager::GetGraphicFromAny( const OUString
& rMimeType
,
519 const ::com::sun::star::uno::Any
& rValue
,
523 ::com::sun::star::uno::Sequence
< sal_Int8
> aSeq
;
524 if( rValue
.hasValue() && ( rValue
>>= aSeq
) )
526 SvMemoryStream
aMemStm( (void*)aSeq
.getConstArray(), aSeq
.getLength(),
530 switch( SotExchange::GetFormatIdFromMimeType( rMimeType
) )
532 case SotClipboardFormatId::SVXB
:
534 ReadGraphic( aMemStm
, rGrf
);
538 case SotClipboardFormatId::GDIMETAFILE
:
541 aMtf
.Read( aMemStm
);
546 case SotClipboardFormatId::BITMAP
:
549 ReadDIB(aBmp
, aMemStm
, true);
562 OUString
lcl_DDE_RelToAbs( const OUString
& rTopic
, const OUString
& rBaseURL
)
565 INetURLObject
aURL( rTopic
);
566 if( INetProtocol::NotValid
== aURL
.GetProtocol() )
567 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic
, rBaseURL
, sRet
);
569 sRet
= URIHelper::SmartRel2Abs( INetURLObject(rBaseURL
), rTopic
, URIHelper::GetMaybeFileHdl(), true );
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
583 CharClass
aCC( LanguageTag( LANGUAGE_SYSTEM
) );
585 TypeId
aType( TYPE(SfxObjectShell
) );
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 );
594 nUpdateMode
= pItem
->GetValue();
597 OUString
sNmURL(aCC
.lowercase(lcl_DDE_RelToAbs(sTopic
, sReferer
)));
602 pShell
= SfxObjectShell::GetFirst( &aType
, false );
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
625 pShell
= SfxObjectShell::GetFirst( &aType
, false );
628 pShell
= SfxObjectShell::GetNext( *pShell
, &aType
, false );
634 // empty topics are not allowed - which document is it
635 if( sTopic
.isEmpty() )
640 sfx2::SvLinkSource
* pNewSrc
= pFndShell
->DdeCreateLinkSource( sItem
);
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
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() );
680 aArgs
.Put(aMinimized
);
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
);
704 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */