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>
41 #include "fileobj.hxx"
42 #include "impldde.hxx"
44 #include <sfx2/sfxresid.hxx>
46 #include <com/sun/star/lang/XComponent.hpp>
47 #include <com/sun/star/util/XCloseable.hpp>
49 using ::com::sun::star::uno::UNO_QUERY
;
50 using ::com::sun::star::uno::Reference
;
51 using ::com::sun::star::lang::XComponent
;
52 using ::com::sun::star::util::XCloseable
;
57 class SvxInternalLink
: public sfx2::SvLinkSource
62 virtual sal_Bool
Connect( sfx2::SvBaseLink
* );
66 LinkManager::LinkManager(SfxObjectShell
* p
)
72 LinkManager::~LinkManager()
74 for( size_t n
= 0; n
< aLinkTbl
.size(); ++n
)
76 SvBaseLinkRef
* pTmp
= aLinkTbl
[ n
];
79 (*pTmp
)->Disconnect();
80 (*pTmp
)->SetLinkManager( NULL
);
86 void LinkManager::InsertCachedComp(const Reference
<XComponent
>& xComp
)
88 maCachedComps
.push_back(xComp
);
91 void LinkManager::CloseCachedComps()
93 CompVector::iterator itr
= maCachedComps
.begin(), itrEnd
= maCachedComps
.end();
94 for (; itr
!= itrEnd
; ++itr
)
96 Reference
<XCloseable
> xCloseable(*itr
, UNO_QUERY
);
100 xCloseable
->close(true);
102 maCachedComps
.clear();
105 //--------------------------------------------------------------------------
107 void LinkManager::Remove( SvBaseLink
*pLink
)
109 // No duplicate links inserted
110 int bFound
= sal_False
;
111 for( size_t n
= 0; n
< aLinkTbl
.size(); )
113 SvBaseLinkRef
* pTmp
= aLinkTbl
[ n
];
116 (*pTmp
)->Disconnect();
117 (*pTmp
)->SetLinkManager( NULL
);
122 // Remove empty ones if they exist
126 aLinkTbl
.erase( aLinkTbl
.begin() + n
);
136 void LinkManager::Remove( size_t nPos
, size_t nCnt
)
138 if( nCnt
&& nPos
< aLinkTbl
.size() )
140 if (sal::static_int_cast
<size_t>(nPos
+ nCnt
) > aLinkTbl
.size())
141 nCnt
= aLinkTbl
.size() - nPos
;
143 for( size_t n
= nPos
; n
< nPos
+ nCnt
; ++n
)
145 SvBaseLinkRef
* pTmp
= aLinkTbl
[ n
];
148 (*pTmp
)->Disconnect();
149 (*pTmp
)->SetLinkManager( NULL
);
153 aLinkTbl
.erase( aLinkTbl
.begin() + nPos
, aLinkTbl
.begin() + nPos
+ nCnt
);
158 sal_Bool
LinkManager::Insert( SvBaseLink
* pLink
)
160 for( size_t n
= 0; n
< aLinkTbl
.size(); ++n
)
162 SvBaseLinkRef
* pTmp
= aLinkTbl
[ n
];
166 aLinkTbl
.erase( aLinkTbl
.begin() + n
-- );
168 else if( pLink
== *pTmp
)
169 return sal_False
; // No duplicate links inserted
172 SvBaseLinkRef
* pTmp
= new SvBaseLinkRef( pLink
);
173 pLink
->SetLinkManager( this );
174 aLinkTbl
.push_back( pTmp
);
179 sal_Bool
LinkManager::InsertLink( SvBaseLink
* pLink
,
181 sal_uInt16 nUpdateMode
,
182 const OUString
* pName
)
185 pLink
->SetObjType( nObjType
);
187 pLink
->SetName( *pName
);
188 pLink
->SetUpdateMode( nUpdateMode
);
189 return Insert( pLink
);
193 sal_Bool
LinkManager::InsertDDELink( SvBaseLink
* pLink
,
194 const OUString
& rServer
,
195 const OUString
& rTopic
,
196 const OUString
& rItem
)
198 if( !( OBJECT_CLIENT_SO
& pLink
->GetObjType() ) )
202 ::sfx2::MakeLnkName( sCmd
, &rServer
, rTopic
, rItem
);
204 pLink
->SetObjType( OBJECT_CLIENT_DDE
);
205 pLink
->SetName( sCmd
);
206 return Insert( pLink
);
210 sal_Bool
LinkManager::InsertDDELink( SvBaseLink
* pLink
)
212 DBG_ASSERT( OBJECT_CLIENT_SO
& pLink
->GetObjType(), "no OBJECT_CLIENT_SO" );
213 if( !( OBJECT_CLIENT_SO
& pLink
->GetObjType() ) )
216 if( pLink
->GetObjType() == OBJECT_CLIENT_SO
)
217 pLink
->SetObjType( OBJECT_CLIENT_DDE
);
219 return Insert( pLink
);
223 // Obtain the string for the dialog
224 bool LinkManager::GetDisplayNames( const SvBaseLink
* pLink
,
228 OUString
* pFilter
) const
231 const OUString
sLNm( pLink
->GetLinkSourceName() );
232 if( !sLNm
.isEmpty() )
234 switch( pLink
->GetObjType() )
236 case OBJECT_CLIENT_FILE
:
237 case OBJECT_CLIENT_GRF
:
238 case OBJECT_CLIENT_OLE
:
241 OUString
sFile( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
242 OUString
sRange( sLNm
.getToken( 0, ::sfx2::cTokenSeparator
, nPos
) );
249 *pFilter
= nPos
== -1 ? OUString() : sLNm
.copy(nPos
);
253 sal_uInt16 nObjType
= pLink
->GetObjType();
255 ( OBJECT_CLIENT_FILE
== nObjType
|| OBJECT_CLIENT_OLE
== nObjType
)
256 ? RID_SVXSTR_FILELINK
257 : RID_SVXSTR_GRAFIKLINK
).toString();
262 case OBJECT_CLIENT_DDE
:
265 OUString
sCmd( sLNm
);
266 OUString
sServer( sCmd
.getToken( 0, cTokenSeparator
, nTmp
) );
267 OUString
sTopic( sCmd
.getToken( 0, cTokenSeparator
, nTmp
) );
274 *pLinkStr
= sCmd
.copy( nTmp
);
286 void LinkManager::UpdateAllLinks(
288 bool /*bCallErrHdl*/,
289 bool bUpdateGrfLinks
,
292 // First make a copy of the array in order to update links
293 // links in ... no contact between them!
294 std::vector
<SvBaseLink
*> aTmpArr
;
295 for( size_t n
= 0; n
< aLinkTbl
.size(); ++n
)
297 SvBaseLink
* pLink
= *aLinkTbl
[ n
];
303 aTmpArr
.push_back( pLink
);
306 for( size_t n
= 0; n
< aTmpArr
.size(); ++n
)
308 SvBaseLink
* pLink
= aTmpArr
[ n
];
310 // search first in the array after the entry
312 for( size_t i
= 0; i
< aLinkTbl
.size(); ++i
)
313 if( pLink
== *aLinkTbl
[ i
] )
320 continue; // was not available!
322 // Graphic-Links not to update yet
323 if( !pLink
->IsVisible() ||
324 ( !bUpdateGrfLinks
&& OBJECT_CLIENT_GRF
== pLink
->GetObjType() ))
329 int nRet
= QueryBox( pParentWin
, WB_YES_NO
| WB_DEF_YES
, SfxResId( STR_QUERY_UPDATE_LINKS
).toString() ).Execute();
330 if( RET_YES
!= nRet
)
331 return ; // nothing should be updated
332 bAskUpdate
= false; // once is enough
340 //--------------------------------------------------------------------------
342 SvLinkSourceRef
LinkManager::CreateObj( SvBaseLink
* pLink
)
344 switch( pLink
->GetObjType() )
346 case OBJECT_CLIENT_FILE
:
347 case OBJECT_CLIENT_GRF
:
348 case OBJECT_CLIENT_OLE
:
349 return new SvFileObject
;
351 return new SvxInternalLink
;
352 case OBJECT_CLIENT_DDE
:
353 return new SvDDEObject
;
355 return SvLinkSourceRef();
359 sal_Bool
LinkManager::InsertServer( SvLinkSource
* pObj
)
361 // no duplicate inserts
365 return aServerTbl
.insert( pObj
).second
;
369 void LinkManager::RemoveServer( SvLinkSource
* pObj
)
371 aServerTbl
.erase( pObj
);
375 void MakeLnkName( OUString
& rName
, const OUString
* pType
, const OUString
& rFile
,
376 const OUString
& rLink
, const OUString
* pFilter
)
380 rName
= comphelper::string::strip(*pType
, ' ');
381 rName
+= OUString(cTokenSeparator
);
383 else if( !rName
.isEmpty() )
388 rName
= comphelper::string::strip(rName
, ' ');
389 rName
+= OUString(cTokenSeparator
);
390 rName
= comphelper::string::strip(rName
, ' ');
394 rName
+= OUString(cTokenSeparator
);
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
];
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 (utl::LocalFileHelper::ConvertPhysicalNameToURL(aFile
, aTmp
))
425 if (!aURL
.equalsIgnoreAsciiCase(pMed
->GetName()))
426 // This DDE link is not associated with this server shell... Skip it.
432 LinkServerShell(aLink
, rServer
, *p
);
436 void LinkManager::LinkServerShell(const OUString
& rPath
, SfxObjectShell
& rServer
, ::sfx2::SvBaseLink
& rLink
) const
438 ::sfx2::SvLinkSource
* pSrvSrc
= rServer
.DdeCreateLinkSource(rPath
);
441 ::com::sun::star::datatransfer::DataFlavor aFl
;
442 SotExchange::GetFormatDataFlavor(rLink
.GetContentType(), aFl
);
443 rLink
.SetObj(pSrvSrc
);
444 pSrvSrc
->AddDataAdvise(
445 &rLink
, aFl
.MimeType
,
446 sfx2::LINKUPDATE_ONCALL
== rLink
.GetUpdateMode() ? ADVISEMODE_ONLYONCE
: 0);
450 bool LinkManager::InsertFileLink(
451 sfx2::SvBaseLink
& rLink
, sal_uInt16 nFileType
, const OUString
& rFileNm
,
452 const OUString
* pFilterNm
, const OUString
* pRange
)
454 if (!(OBJECT_CLIENT_SO
& rLink
.GetObjType()))
458 aBuf
.append(rFileNm
);
459 aBuf
.append(sfx2::cTokenSeparator
);
462 aBuf
.append(*pRange
);
466 aBuf
.append(sfx2::cTokenSeparator
);
467 aBuf
.append(*pFilterNm
);
470 OUString aCmd
= aBuf
.makeStringAndClear();
471 return InsertLink(&rLink
, nFileType
, sfx2::LINKUPDATE_ONCALL
, &aCmd
);
474 // A transfer is aborted, so cancel all download media
475 // (for now this is only of interest for the file links!)
476 void LinkManager::CancelTransfers()
478 SvFileObject
* pFileObj
;
479 sfx2::SvBaseLink
* pLnk
;
481 const sfx2::SvBaseLinks
& rLnks
= GetLinks();
482 for( size_t n
= rLnks
.size(); n
; )
483 if( 0 != ( pLnk
= &(*rLnks
[ --n
])) &&
484 OBJECT_CLIENT_FILE
== (OBJECT_CLIENT_FILE
& pLnk
->GetObjType()) &&
485 0 != ( pFileObj
= (SvFileObject
*)pLnk
->GetObj() ) )
486 pFileObj
->CancelTransfers();
488 // For the purpose of sending Status information from the file object to
489 // the base link, there exist a dedicated ClipBoardId. The SvData-object
490 // gets the appropriate information as a string
491 // For now this is required for file object in conjunction with JavaScript
492 // - needs information about Load/Abort/Error
493 sal_uIntPtr
LinkManager::RegisterStatusInfoId()
495 static sal_uIntPtr nFormat
= 0;
499 nFormat
= SotExchange::RegisterFormatName(
500 OUString("StatusInfo from SvxInternalLink"));
505 // ----------------------------------------------------------------------
507 sal_Bool
LinkManager::GetGraphicFromAny( const OUString
& rMimeType
,
508 const ::com::sun::star::uno::Any
& rValue
,
511 sal_Bool bRet
= sal_False
;
512 ::com::sun::star::uno::Sequence
< sal_Int8
> aSeq
;
513 if( rValue
.hasValue() && ( rValue
>>= aSeq
) )
515 SvMemoryStream
aMemStm( (void*)aSeq
.getConstArray(), aSeq
.getLength(),
519 switch( SotExchange::GetFormatIdFromMimeType( rMimeType
) )
521 case SOT_FORMATSTR_ID_SVXB
:
527 case FORMAT_GDIMETAFILE
:
530 aMtf
.Read( aMemStm
);
538 ReadDIB(aBmp
, aMemStm
, true);
549 // ----------------------------------------------------------------------
550 OUString
lcl_DDE_RelToAbs( const OUString
& rTopic
, const OUString
& rBaseURL
)
553 INetURLObject
aURL( rTopic
);
554 if( INET_PROT_NOT_VALID
== aURL
.GetProtocol() )
555 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic
, rBaseURL
, sRet
);
557 sRet
= URIHelper::SmartRel2Abs( INetURLObject(rBaseURL
), rTopic
, URIHelper::GetMaybeFileHdl(), true );
561 sal_Bool
SvxInternalLink::Connect( sfx2::SvBaseLink
* pLink
)
563 SfxObjectShell
* pFndShell
= 0;
564 sal_uInt16 nUpdateMode
= com::sun::star::document::UpdateDocMode::NO_UPDATE
;
565 OUString sTopic
, sItem
, sReferer
;
566 LinkManager
* pLinkMgr
= pLink
->GetLinkManager();
567 if (pLinkMgr
&& pLinkMgr
->GetDisplayNames(pLink
, 0, &sTopic
, &sItem
) && !sTopic
.isEmpty())
569 // first only loop over the DocumentShells the shells and find those
571 CharClass
aCC( LanguageTag( LANGUAGE_SYSTEM
) );
573 TypeId
aType( TYPE(SfxObjectShell
) );
575 sal_Bool bFirst
= sal_True
;
576 SfxObjectShell
* pShell
= pLinkMgr
->GetPersist();
577 if( pShell
&& pShell
->GetMedium() )
579 sReferer
= pShell
->GetMedium()->GetBaseURL();
580 SFX_ITEMSET_ARG( pShell
->GetMedium()->GetItemSet(), pItem
, SfxUInt16Item
, SID_UPDATEDOCMODE
, sal_False
);
582 nUpdateMode
= pItem
->GetValue();
585 OUString
sNmURL(aCC
.lowercase(lcl_DDE_RelToAbs(sTopic
, sReferer
)));
590 pShell
= SfxObjectShell::GetFirst( &aType
, sal_False
);
598 sTmp
= pShell
->GetTitle( SFX_TITLE_FULLNAME
);
599 sTmp
= lcl_DDE_RelToAbs(sTmp
, sReferer
);
603 sTmp
= aCC
.lowercase( sTmp
);
604 if( sTmp
== sNmURL
) // we want these
613 pShell
= SfxObjectShell::GetFirst( &aType
, sal_False
);
616 pShell
= SfxObjectShell::GetNext( *pShell
, &aType
, sal_False
);
622 // empty topics are not allowed - which document is it
623 if( sTopic
.isEmpty() )
628 sfx2::SvLinkSource
* pNewSrc
= pFndShell
->DdeCreateLinkSource( sItem
);
631 ::com::sun::star::datatransfer::DataFlavor aFl
;
632 SotExchange::GetFormatDataFlavor( pLink
->GetContentType(), aFl
);
634 pLink
->SetObj( pNewSrc
);
635 pNewSrc
->AddDataAdvise( pLink
, aFl
.MimeType
,
636 sfx2::LINKUPDATE_ONCALL
== pLink
->GetUpdateMode()
637 ? ADVISEMODE_ONLYONCE
644 // then try to download the file:
645 INetURLObject
aURL( sTopic
);
646 INetProtocol eOld
= aURL
.GetProtocol();
647 aURL
.SetURL( sTopic
= lcl_DDE_RelToAbs( sTopic
, sReferer
) );
648 if( INET_PROT_NOT_VALID
!= eOld
||
649 INET_PROT_HTTP
!= aURL
.GetProtocol() )
651 SfxStringItem
aName( SID_FILE_NAME
, sTopic
);
652 SfxBoolItem
aMinimized(SID_MINIMIZED
, sal_True
);
653 SfxBoolItem
aHidden(SID_HIDDEN
, sal_True
);
654 SfxStringItem
aTarget( SID_TARGETNAME
, OUString("_blank") );
655 SfxStringItem
aReferer( SID_REFERER
, sReferer
);
656 SfxUInt16Item
aUpdate( SID_UPDATEDOCMODE
, nUpdateMode
);
657 SfxBoolItem
aReadOnly(SID_DOC_READONLY
, false);
659 // Disable automatic re-connection to avoid this link instance
660 // being destroyed at re-connection.
661 SfxBoolItem
aDdeConnect(SID_DDE_RECONNECT_ONLOAD
, false);
663 // #i14200# (DDE-link crashes wordprocessor)
664 SfxAllItemSet
aArgs( SFX_APP()->GetPool() );
668 aArgs
.Put(aMinimized
);
671 aArgs
.Put(aReadOnly
);
672 aArgs
.Put(aDdeConnect
);
673 Reference
<XComponent
> xComp
= SfxObjectShell::CreateAndLoadComponent(aArgs
);
674 pFndShell
= SfxObjectShell::GetShellFromComponent(xComp
);
675 if (xComp
.is() && pFndShell
)
677 pLinkMgr
->InsertCachedComp(xComp
);
678 pLinkMgr
->LinkServerShell(sItem
, *pFndShell
, *pLink
);
692 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */