1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include <sfx2/linkmgr.hxx>
30 #include <com/sun/star/document/UpdateDocMode.hpp>
31 #include <sfx2/objsh.hxx>
32 #include <svl/urihelper.hxx>
33 #include <sot/formats.hxx>
34 #include <tools/urlobj.hxx>
35 #include <sot/exchange.hxx>
36 #include <tools/debug.hxx>
37 #include <vcl/msgbox.hxx>
38 #include <sfx2/lnkbase.hxx>
39 #include <sfx2/app.hxx>
40 #include <vcl/graph.hxx>
41 #include <svl/stritem.hxx>
42 #include <svl/eitem.hxx>
43 #include <svl/intitem.hxx>
44 #include <unotools/localfilehelper.hxx>
45 #include <i18npool/mslangid.hxx>
46 #include <sfx2/request.hxx>
48 #include "fileobj.hxx"
49 #include "impldde.hxx"
51 #include "sfx2/sfxresid.hxx"
52 #include <svl/svstdarr.hxx>
54 #include <com/sun/star/lang/XComponent.hpp>
55 #include <com/sun/star/util/XCloseable.hpp>
57 using ::com::sun::star::uno::UNO_QUERY
;
58 using ::com::sun::star::uno::Reference
;
59 using ::com::sun::star::lang::XComponent
;
60 using ::com::sun::star::util::XCloseable
;
61 using ::rtl::OUString
;
62 using ::rtl::OUStringBuffer
;
67 class SvxInternalLink
: public sfx2::SvLinkSource
72 virtual sal_Bool
Connect( sfx2::SvBaseLink
* );
76 SV_IMPL_PTRARR( SvBaseLinks
, SvBaseLinkRefPtr
)
78 LinkManager::LinkManager(SfxObjectShell
* p
)
84 LinkManager::~LinkManager()
86 SvBaseLinkRef
** ppRef
= (SvBaseLinkRef
**)aLinkTbl
.GetData();
87 for( sal_uInt16 n
= aLinkTbl
.Count(); n
; --n
, ++ppRef
)
91 (*(*ppRef
))->Disconnect();
92 (*(*ppRef
))->SetLinkManager( NULL
);
98 void LinkManager::InsertCachedComp(const Reference
<XComponent
>& xComp
)
100 maCachedComps
.push_back(xComp
);
103 void LinkManager::CloseCachedComps()
105 CompVector::iterator itr
= maCachedComps
.begin(), itrEnd
= maCachedComps
.end();
106 for (; itr
!= itrEnd
; ++itr
)
108 Reference
<XCloseable
> xCloseable(*itr
, UNO_QUERY
);
109 if (!xCloseable
.is())
112 xCloseable
->close(true);
114 maCachedComps
.clear();
117 //--------------------------------------------------------------------------
119 void LinkManager::Remove( SvBaseLink
*pLink
)
121 // No duplicate links inserted
122 int bFound
= sal_False
;
123 SvBaseLinkRef
** ppRef
= (SvBaseLinkRef
**)aLinkTbl
.GetData();
124 for( sal_uInt16 n
= aLinkTbl
.Count(); n
; --n
, ++ppRef
)
126 if( pLink
== *(*ppRef
) )
128 (*(*ppRef
))->Disconnect();
129 (*(*ppRef
))->SetLinkManager( NULL
);
134 // Remove emty ones if they exist
135 if( !(*ppRef
)->Is() )
138 aLinkTbl
.Remove( aLinkTbl
.Count() - n
, 1 );
147 void LinkManager::Remove( sal_uInt16 nPos
, sal_uInt16 nCnt
)
149 if( nCnt
&& nPos
< aLinkTbl
.Count() )
151 if( nPos
+ nCnt
> aLinkTbl
.Count() )
152 nCnt
= aLinkTbl
.Count() - nPos
;
154 SvBaseLinkRef
** ppRef
= (SvBaseLinkRef
**)aLinkTbl
.GetData() + nPos
;
155 for( sal_uInt16 n
= nCnt
; n
; --n
, ++ppRef
)
159 (*(*ppRef
))->Disconnect();
160 (*(*ppRef
))->SetLinkManager( NULL
);
164 aLinkTbl
.Remove( nPos
, nCnt
);
169 sal_Bool
LinkManager::Insert( SvBaseLink
* pLink
)
171 // No duplicate links inserted
172 for( sal_uInt16 n
= 0; n
< aLinkTbl
.Count(); ++n
)
174 SvBaseLinkRef
* pTmp
= aLinkTbl
[ n
];
176 aLinkTbl
.DeleteAndDestroy( n
-- );
182 SvBaseLinkRef
* pTmp
= new SvBaseLinkRef( pLink
);
183 pLink
->SetLinkManager( this );
184 aLinkTbl
.Insert( pTmp
, aLinkTbl
.Count() );
189 sal_Bool
LinkManager::InsertLink( SvBaseLink
* pLink
,
191 sal_uInt16 nUpdateMode
,
192 const String
* pName
)
195 pLink
->SetObjType( nObjType
);
197 pLink
->SetName( *pName
);
198 pLink
->SetUpdateMode( nUpdateMode
);
199 return Insert( pLink
);
203 sal_Bool
LinkManager::InsertDDELink( SvBaseLink
* pLink
,
204 const String
& rServer
,
205 const String
& rTopic
,
206 const String
& rItem
)
208 if( !( OBJECT_CLIENT_SO
& pLink
->GetObjType() ) )
212 ::sfx2::MakeLnkName( sCmd
, &rServer
, rTopic
, rItem
);
214 pLink
->SetObjType( OBJECT_CLIENT_DDE
);
215 pLink
->SetName( sCmd
);
216 return Insert( pLink
);
220 sal_Bool
LinkManager::InsertDDELink( SvBaseLink
* pLink
)
222 DBG_ASSERT( OBJECT_CLIENT_SO
& pLink
->GetObjType(), "no OBJECT_CLIENT_SO" );
223 if( !( OBJECT_CLIENT_SO
& pLink
->GetObjType() ) )
226 if( pLink
->GetObjType() == OBJECT_CLIENT_SO
)
227 pLink
->SetObjType( OBJECT_CLIENT_DDE
);
229 return Insert( pLink
);
233 // Obtain the string for the dialog
234 bool LinkManager::GetDisplayNames( const SvBaseLink
* pLink
,
238 String
* pFilter
) const
241 const String
sLNm( pLink
->GetLinkSourceName() );
244 switch( pLink
->GetObjType() )
246 case OBJECT_CLIENT_FILE
:
247 case OBJECT_CLIENT_GRF
:
248 case OBJECT_CLIENT_OLE
:
251 String
sFile( sLNm
.GetToken( 0, ::sfx2::cTokenSeperator
, nPos
) );
252 String
sRange( sLNm
.GetToken( 0, ::sfx2::cTokenSeperator
, nPos
) );
259 *pFilter
= sLNm
.Copy( nPos
);
263 sal_uInt16 nObjType
= pLink
->GetObjType();
264 *pType
= String( SfxResId(
265 ( OBJECT_CLIENT_FILE
== nObjType
|| OBJECT_CLIENT_OLE
== nObjType
)
266 ? RID_SVXSTR_FILELINK
267 : RID_SVXSTR_GRAFIKLINK
));
272 case OBJECT_CLIENT_DDE
:
276 String
sServer( sCmd
.GetToken( 0, cTokenSeperator
, nTmp
) );
277 String
sTopic( sCmd
.GetToken( 0, cTokenSeperator
, nTmp
) );
284 *pLinkStr
= sCmd
.Copy( nTmp
);
296 bool LinkManager::GetDisplayNames(
297 const SvBaseLink
* pLink
, rtl::OUString
* pType
, rtl::OUString
* pFile
,
298 rtl::OUString
* pLinkStr
, rtl::OUString
* pFilter
) const
300 String aType
, aFile
, aLinkStr
, aFilter
;
301 bool bRet
= GetDisplayNames(pLink
, &aType
, &aFile
, &aLinkStr
, &aFilter
);
307 *pLinkStr
= aLinkStr
;
313 void LinkManager::UpdateAllLinks(
315 sal_Bool
/*bCallErrHdl*/,
316 sal_Bool bUpdateGrfLinks
,
319 // First make a copy of the array in order to update links
320 // links in ... no contact between them!
321 SvPtrarr
aTmpArr( 255 );
323 for( n
= 0; n
< aLinkTbl
.Count(); ++n
)
325 SvBaseLink
* pLink
= *aLinkTbl
[ n
];
331 aTmpArr
.Insert( pLink
, aTmpArr
.Count() );
334 for( n
= 0; n
< aTmpArr
.Count(); ++n
)
336 SvBaseLink
* pLink
= (SvBaseLink
*)aTmpArr
[ n
];
338 // search first in the array after the entry
339 sal_uInt16 nFndPos
= USHRT_MAX
;
340 for( sal_uInt16 i
= 0; i
< aLinkTbl
.Count(); ++i
)
341 if( pLink
== *aLinkTbl
[ i
] )
347 if( USHRT_MAX
== nFndPos
)
348 continue; // was not available!
350 // Graphic-Links not to update yet
351 if( !pLink
->IsVisible() ||
352 ( !bUpdateGrfLinks
&& OBJECT_CLIENT_GRF
== pLink
->GetObjType() ))
357 int nRet
= QueryBox( pParentWin
, WB_YES_NO
| WB_DEF_YES
, SfxResId( STR_QUERY_UPDATE_LINKS
) ).Execute();
358 if( RET_YES
!= nRet
)
359 return ; // nothing should be updated
360 bAskUpdate
= sal_False
; // once is enough
368 //--------------------------------------------------------------------------
370 SvLinkSourceRef
LinkManager::CreateObj( SvBaseLink
* pLink
)
372 switch( pLink
->GetObjType() )
374 case OBJECT_CLIENT_FILE
:
375 case OBJECT_CLIENT_GRF
:
376 case OBJECT_CLIENT_OLE
:
377 return new SvFileObject
;
379 return new SvxInternalLink
;
380 case OBJECT_CLIENT_DDE
:
381 return new SvDDEObject
;
383 return SvLinkSourceRef();
387 sal_Bool
LinkManager::InsertServer( SvLinkSource
* pObj
)
389 // no duplicate inserts
390 if( !pObj
|| USHRT_MAX
!= aServerTbl
.GetPos( pObj
) )
393 aServerTbl
.Insert( pObj
, aServerTbl
.Count() );
398 void LinkManager::RemoveServer( SvLinkSource
* pObj
)
400 sal_uInt16 nPos
= aServerTbl
.GetPos( pObj
);
401 if( USHRT_MAX
!= nPos
)
402 aServerTbl
.Remove( nPos
, 1 );
406 void MakeLnkName( String
& rName
, const String
* pType
, const String
& rFile
,
407 const String
& rLink
, const String
* pFilter
)
410 (rName
= *pType
).EraseLeadingChars().EraseTrailingChars() += cTokenSeperator
;
411 else if( rName
.Len() )
414 ((rName
+= rFile
).EraseLeadingChars().EraseTrailingChars() +=
415 cTokenSeperator
).EraseLeadingChars().EraseTrailingChars() += rLink
;
417 ((rName
+= cTokenSeperator
) += *pFilter
).EraseLeadingChars().EraseTrailingChars();
420 void LinkManager::ReconnectDdeLink(SfxObjectShell
& rServer
)
422 SfxMedium
* pMed
= rServer
.GetMedium();
426 const ::sfx2::SvBaseLinks
& rLinks
= GetLinks();
427 sal_uInt16 n
= rLinks
.Count();
429 for (sal_uInt16 i
= 0; i
< n
; ++i
)
431 ::sfx2::SvBaseLink
* p
= *rLinks
[i
];
432 String aType
, aFile
, aLink
, aFilter
;
433 if (!GetDisplayNames(p
, &aType
, &aFile
, &aLink
, &aFilter
))
436 if (!aType
.EqualsAscii("soffice"))
437 // DDE connections between OOo apps are always named 'soffice'.
441 OUString aURL
= aFile
;
442 if (utl::LocalFileHelper::ConvertPhysicalNameToURL(aFile
, aTmp
))
445 if (!aURL
.equalsIgnoreAsciiCase(pMed
->GetName()))
446 // This DDE link is not associated with this server shell... Skip it.
452 LinkServerShell(aLink
, rServer
, *p
);
456 void LinkManager::LinkServerShell(const OUString
& rPath
, SfxObjectShell
& rServer
, ::sfx2::SvBaseLink
& rLink
) const
458 ::sfx2::SvLinkSource
* pSrvSrc
= rServer
.DdeCreateLinkSource(rPath
);
461 ::com::sun::star::datatransfer::DataFlavor aFl
;
462 SotExchange::GetFormatDataFlavor(rLink
.GetContentType(), aFl
);
463 rLink
.SetObj(pSrvSrc
);
464 pSrvSrc
->AddDataAdvise(
465 &rLink
, aFl
.MimeType
,
466 sfx2::LINKUPDATE_ONCALL
== rLink
.GetUpdateMode() ? ADVISEMODE_ONLYONCE
: 0);
470 bool LinkManager::InsertFileLink( sfx2::SvBaseLink
& rLink
,
471 sal_uInt16 nFileType
,
472 const String
& rFileNm
,
473 const String
* pFilterNm
,
474 const String
* pRange
)
476 if( !( OBJECT_CLIENT_SO
& rLink
.GetObjType() ))
479 String
sCmd( rFileNm
);
480 sCmd
+= ::sfx2::cTokenSeperator
;
484 ( sCmd
+= ::sfx2::cTokenSeperator
) += *pFilterNm
;
486 return InsertLink( &rLink
, nFileType
, sfx2::LINKUPDATE_ONCALL
, &sCmd
);
489 bool LinkManager::InsertFileLink(
490 sfx2::SvBaseLink
& rLink
, sal_uInt16 nFileType
, const rtl::OUString
& rFileNm
,
491 const rtl::OUString
* pFilterNm
, const rtl::OUString
* pRange
)
493 if (!(OBJECT_CLIENT_SO
& rLink
.GetObjType()))
496 rtl::OUStringBuffer aBuf
;
497 aBuf
.append(rFileNm
);
498 aBuf
.append(sfx2::cTokenSeperator
);
501 aBuf
.append(*pRange
);
505 aBuf
.append(sfx2::cTokenSeperator
);
506 aBuf
.append(*pFilterNm
);
509 String aCmd
= aBuf
.makeStringAndClear();
510 return InsertLink(&rLink
, nFileType
, sfx2::LINKUPDATE_ONCALL
, &aCmd
);
513 // A transfer is aborted, so cancel all download media
514 // (for now this is only of interest for the file links!)
515 void LinkManager::CancelTransfers()
517 SvFileObject
* pFileObj
;
518 sfx2::SvBaseLink
* pLnk
;
520 const sfx2::SvBaseLinks
& rLnks
= GetLinks();
521 for( sal_uInt16 n
= rLnks
.Count(); n
; )
522 if( 0 != ( pLnk
= &(*rLnks
[ --n
])) &&
523 OBJECT_CLIENT_FILE
== (OBJECT_CLIENT_FILE
& pLnk
->GetObjType()) &&
524 0 != ( pFileObj
= (SvFileObject
*)pLnk
->GetObj() ) )
525 pFileObj
->CancelTransfers();
527 // For the purpose of sending Status information from the file object to
528 // the base link, there exist a dedicated ClipBoardId. The SvData-object
529 // gets the appropriate information as a string
530 // For now this is required for file object in conjunction with JavaScript
531 // - needs information about Load/Abort/Error
532 sal_uIntPtr
LinkManager::RegisterStatusInfoId()
534 static sal_uIntPtr nFormat
= 0;
538 nFormat
= SotExchange::RegisterFormatName(
539 String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM(
540 "StatusInfo from SvxInternalLink" )));
545 // ----------------------------------------------------------------------
547 sal_Bool
LinkManager::GetGraphicFromAny( const String
& rMimeType
,
548 const ::com::sun::star::uno::Any
& rValue
,
551 sal_Bool bRet
= sal_False
;
552 ::com::sun::star::uno::Sequence
< sal_Int8
> aSeq
;
553 if( rValue
.hasValue() && ( rValue
>>= aSeq
) )
555 SvMemoryStream
aMemStm( (void*)aSeq
.getConstArray(), aSeq
.getLength(),
559 switch( SotExchange::GetFormatIdFromMimeType( rMimeType
) )
561 case SOT_FORMATSTR_ID_SVXB
:
567 case FORMAT_GDIMETAFILE
:
570 aMtf
.Read( aMemStm
);
589 // ----------------------------------------------------------------------
590 String
lcl_DDE_RelToAbs( const String
& rTopic
, const String
& rBaseURL
)
593 INetURLObject
aURL( rTopic
);
594 if( INET_PROT_NOT_VALID
== aURL
.GetProtocol() )
595 utl::LocalFileHelper::ConvertSystemPathToURL( rTopic
, rBaseURL
, sRet
);
597 sRet
= URIHelper::SmartRel2Abs( INetURLObject(rBaseURL
), rTopic
, URIHelper::GetMaybeFileHdl(), true );
601 sal_Bool
SvxInternalLink::Connect( sfx2::SvBaseLink
* pLink
)
603 SfxObjectShell
* pFndShell
= 0;
604 sal_uInt16 nUpdateMode
= com::sun::star::document::UpdateDocMode::NO_UPDATE
;
605 String sTopic
, sItem
, sReferer
;
606 LinkManager
* pLinkMgr
= pLink
->GetLinkManager();
607 if (pLinkMgr
&& pLinkMgr
->GetDisplayNames(pLink
, 0, &sTopic
, &sItem
) && sTopic
.Len())
609 // first only loop over the DocumentShells the shells and find those
611 com::sun::star::lang::Locale aLocale
;
612 MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM
, aLocale
);
613 CharClass
aCC( aLocale
);
615 TypeId
aType( TYPE(SfxObjectShell
) );
617 sal_Bool bFirst
= sal_True
;
618 SfxObjectShell
* pShell
= pLinkMgr
->GetPersist();
619 if( pShell
&& pShell
->GetMedium() )
621 sReferer
= pShell
->GetMedium()->GetBaseURL();
622 SFX_ITEMSET_ARG( pShell
->GetMedium()->GetItemSet(), pItem
, SfxUInt16Item
, SID_UPDATEDOCMODE
, sal_False
);
624 nUpdateMode
= pItem
->GetValue();
627 String
sNmURL(aCC
.lowercase(lcl_DDE_RelToAbs(sTopic
, sReferer
)));
632 pShell
= SfxObjectShell::GetFirst( &aType
, sal_False
);
640 sTmp
= pShell
->GetTitle( SFX_TITLE_FULLNAME
);
641 sTmp
= lcl_DDE_RelToAbs(sTmp
, sReferer
);
645 sTmp
= aCC
.lowercase( sTmp
);
646 if( sTmp
== sNmURL
) // we want these
655 pShell
= SfxObjectShell::GetFirst( &aType
, sal_False
);
658 pShell
= SfxObjectShell::GetNext( *pShell
, &aType
, sal_False
);
664 // empty topics are not allowed - which document is it
670 sfx2::SvLinkSource
* pNewSrc
= pFndShell
->DdeCreateLinkSource( sItem
);
673 ::com::sun::star::datatransfer::DataFlavor aFl
;
674 SotExchange::GetFormatDataFlavor( pLink
->GetContentType(), aFl
);
676 pLink
->SetObj( pNewSrc
);
677 pNewSrc
->AddDataAdvise( pLink
, aFl
.MimeType
,
678 sfx2::LINKUPDATE_ONCALL
== pLink
->GetUpdateMode()
679 ? ADVISEMODE_ONLYONCE
686 // then try to download the file:
687 INetURLObject
aURL( sTopic
);
688 INetProtocol eOld
= aURL
.GetProtocol();
689 aURL
.SetURL( sTopic
= lcl_DDE_RelToAbs( sTopic
, sReferer
) );
690 if( INET_PROT_NOT_VALID
!= eOld
||
691 INET_PROT_HTTP
!= aURL
.GetProtocol() )
693 SfxStringItem
aName( SID_FILE_NAME
, sTopic
);
694 SfxBoolItem
aMinimized(SID_MINIMIZED
, sal_True
);
695 SfxBoolItem
aHidden(SID_HIDDEN
, sal_True
);
696 SfxStringItem
aTarget( SID_TARGETNAME
, String::CreateFromAscii("_blank") );
697 SfxStringItem
aReferer( SID_REFERER
, sReferer
);
698 SfxUInt16Item
aUpdate( SID_UPDATEDOCMODE
, nUpdateMode
);
699 SfxBoolItem
aReadOnly(SID_DOC_READONLY
, false);
701 // Disable automatic re-connection to avoid this link instance
702 // being destroyed at re-connection.
703 SfxBoolItem
aDdeConnect(SID_DDE_RECONNECT_ONLOAD
, false);
705 // #i14200# (DDE-link crashes wordprocessor)
706 SfxAllItemSet
aArgs( SFX_APP()->GetPool() );
710 aArgs
.Put(aMinimized
);
713 aArgs
.Put(aReadOnly
);
714 aArgs
.Put(aDdeConnect
);
715 Reference
<XComponent
> xComp
= SfxObjectShell::CreateAndLoadComponent(aArgs
);
716 pFndShell
= SfxObjectShell::GetShellFromComponent(xComp
);
717 if (xComp
.is() && pFndShell
)
719 pLinkMgr
->InsertCachedComp(xComp
);
720 pLinkMgr
->LinkServerShell(sItem
, *pFndShell
, *pLink
);
734 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */