blob: 4b80c788486d8acef9eab1fa1436350fbba6a8cc [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sfx2.hxx"
#include <sfx2/lnkbase.hxx>
#include <sot/exchange.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <vcl/msgbox.hxx>
#include <sfx2/linkmgr.hxx>
#include <vcl/svapp.hxx>
#include "app.hrc"
#include "sfx2/sfxresid.hxx"
#include <sfx2/filedlghelper.hxx>
#include <tools/debug.hxx>
#include <svl/svdde.hxx>
using namespace ::com::sun::star::uno;
namespace sfx2
{
TYPEINIT0( SvBaseLink )
static DdeTopic* FindTopic( const String &, sal_uInt16* = 0 );
class ImplDdeItem;
struct BaseLink_Impl
{
Link m_aEndEditLink;
LinkManager* m_pLinkMgr;
Window* m_pParentWin;
FileDialogHelper* m_pFileDlg;
bool m_bIsConnect;
BaseLink_Impl() :
m_pLinkMgr( NULL )
, m_pParentWin( NULL )
, m_pFileDlg( NULL )
, m_bIsConnect( false )
{}
~BaseLink_Impl()
{ delete m_pFileDlg; }
};
// nur fuer die interne Verwaltung
struct ImplBaseLinkData
{
struct tClientType
{
// gilt fuer alle Links
sal_uIntPtr nCntntType; // Update Format
// nicht Ole-Links
sal_Bool bIntrnlLnk; // ist es ein interner Link
sal_uInt16 nUpdateMode;// UpdateMode
};
struct tDDEType
{
ImplDdeItem* pItem;
};
union {
tClientType ClientType;
tDDEType DDEType;
};
ImplBaseLinkData()
{
ClientType.nCntntType = 0;
ClientType.bIntrnlLnk = sal_False;
ClientType.nUpdateMode = 0;
DDEType.pItem = NULL;
}
};
class ImplDdeItem : public DdeGetPutItem
{
SvBaseLink* pLink;
DdeData aData;
Sequence< sal_Int8 > aSeq; // Datacontainer for DdeData !!!
sal_Bool bIsValidData : 1;
sal_Bool bIsInDTOR : 1;
public:
ImplDdeItem( SvBaseLink& rLink, const String& rStr )
: DdeGetPutItem( rStr ), pLink( &rLink ), bIsValidData( sal_False ),
bIsInDTOR( sal_False )
{}
virtual ~ImplDdeItem();
virtual DdeData* Get( sal_uIntPtr );
virtual sal_Bool Put( const DdeData* );
virtual void AdviseLoop( sal_Bool );
void Notify()
{
bIsValidData = sal_False;
DdeGetPutItem::NotifyClient();
}
sal_Bool IsInDTOR() const { return bIsInDTOR; }
};
/************************************************************************
|* SvBaseLink::SvBaseLink()
|*
|* Beschreibung
*************************************************************************/
SvBaseLink::SvBaseLink()
: SvRefBase(),
xObj(),
aLinkName(),
pImpl(new BaseLink_Impl()),
nObjType(OBJECT_CLIENT_SO),
bVisible(sal_True),
bSynchron(sal_True),
bUseCache(sal_True),
bWasLastEditOK(sal_False),
pImplData(new ImplBaseLinkData),
m_bIsReadOnly(false),
m_xInputStreamToLoadFrom()
{
}
/************************************************************************
|* SvBaseLink::SvBaseLink()
|*
|* Beschreibung
*************************************************************************/
SvBaseLink::SvBaseLink( sal_uInt16 nUpdateMode, sal_uIntPtr nContentType )
: SvRefBase(),
xObj(),
aLinkName(),
pImpl(new BaseLink_Impl()),
nObjType(OBJECT_CLIENT_SO),
bVisible(sal_True),
bSynchron(sal_True),
bUseCache(sal_True),
bWasLastEditOK(sal_False),
pImplData(new ImplBaseLinkData),
m_bIsReadOnly(false),
m_xInputStreamToLoadFrom()
{
// falls es ein Ole-Link wird,
pImplData->ClientType.nUpdateMode = nUpdateMode;
pImplData->ClientType.nCntntType = nContentType;
pImplData->ClientType.bIntrnlLnk = sal_False;
}
/************************************************************************
|* SvBaseLink::SvBaseLink()
|*
|* Beschreibung
*************************************************************************/
SvBaseLink::SvBaseLink( const String& rLinkName, sal_uInt16 nObjectType, SvLinkSource* pObj )
: SvRefBase(),
xObj(),
aLinkName(rLinkName),
pImpl(0),
nObjType(nObjectType),
bVisible(sal_True),
bSynchron(sal_True),
bUseCache(sal_True),
bWasLastEditOK(sal_False),
pImplData(new ImplBaseLinkData),
m_bIsReadOnly(false),
m_xInputStreamToLoadFrom()
{
if( !pObj )
{
DBG_ASSERT( pObj, "Wo ist mein zu linkendes Object" );
return;
}
if( OBJECT_DDE_EXTERN == nObjType )
{
sal_uInt16 nItemStt = 0;
DdeTopic* pTopic = FindTopic( aLinkName, &nItemStt );
if( pTopic )
{
// dann haben wir alles zusammen
// MM hat gefummelt ???
// MM_TODO wie kriege ich den Namen
String aStr = aLinkName; // xLinkName->GetDisplayName();
aStr = aStr.Copy( nItemStt );
pImplData->DDEType.pItem = new ImplDdeItem( *this, aStr );
pTopic->InsertItem( pImplData->DDEType.pItem );
// dann koennen wir uns auch das Advise merken
xObj = pObj;
}
}
else if( pObj->Connect( this ) )
xObj = pObj;
}
/************************************************************************
|* SvBaseLink::~SvBaseLink()
|*
|* Beschreibung
*************************************************************************/
SvBaseLink::~SvBaseLink()
{
Disconnect();
switch( nObjType )
{
case OBJECT_DDE_EXTERN:
if( !pImplData->DDEType.pItem->IsInDTOR() )
delete pImplData->DDEType.pItem;
break;
}
delete pImplData;
if(pImpl)
{
delete pImpl;
}
}
IMPL_LINK( SvBaseLink, EndEditHdl, String*, _pNewName )
{
if(pImpl)
{
String sNewName;
if ( _pNewName )
sNewName = *_pNewName;
if ( !ExecuteEdit( sNewName ) )
sNewName.Erase();
bWasLastEditOK = ( sNewName.Len() > 0 );
if ( pImpl->m_aEndEditLink.IsSet() )
pImpl->m_aEndEditLink.Call( this );
}
else
{
OSL_ENSURE(false, "No pImpl (!)");
}
return 0;
}
/************************************************************************
|* SvBaseLink::SetObjType()
|*
|* Beschreibung
*************************************************************************/
void SvBaseLink::SetObjType( sal_uInt16 nObjTypeP )
{
DBG_ASSERT( nObjType != OBJECT_CLIENT_DDE, "type already set" );
DBG_ASSERT( !xObj.Is(), "object exist" );
nObjType = nObjTypeP;
}
/************************************************************************
|* SvBaseLink::SetName()
|*
|* Beschreibung
*************************************************************************/
void SvBaseLink::SetName( const String & rNm )
{
aLinkName = rNm;
}
/************************************************************************
|* SvBaseLink::GetName()
|*
|* Beschreibung
*************************************************************************/
String SvBaseLink::GetName() const
{
return aLinkName;
}
/************************************************************************
|* SvBaseLink::SetObj()
|*
|* Beschreibung
*************************************************************************/
void SvBaseLink::SetObj( SvLinkSource * pObj )
{
DBG_ASSERT( (nObjType & OBJECT_CLIENT_SO &&
pImplData->ClientType.bIntrnlLnk) ||
nObjType == OBJECT_CLIENT_GRF,
"no intern link" );
xObj = pObj;
}
/************************************************************************
|* SvBaseLink::SetLinkSourceName()
|*
|* Beschreibung
*************************************************************************/
void SvBaseLink::SetLinkSourceName( const String & rLnkNm )
{
if( aLinkName == rLnkNm )
return;
AddNextRef(); // sollte ueberfluessig sein
// Alte Verbindung weg
Disconnect();
aLinkName = rLnkNm;
// Neu verbinden
_GetRealObject();
ReleaseRef(); // sollte ueberfluessig sein
}
/************************************************************************
|* SvBaseLink::GetLinkSourceName()
|*
|* Beschreibung
*************************************************************************/
String SvBaseLink::GetLinkSourceName() const
{
return aLinkName;
}
/************************************************************************
|* SvBaseLink::SetUpdateMode()
|*
|* Beschreibung
*************************************************************************/
void SvBaseLink::SetUpdateMode( sal_uInt16 nMode )
{
if( ( OBJECT_CLIENT_SO & nObjType ) &&
pImplData->ClientType.nUpdateMode != nMode )
{
AddNextRef();
Disconnect();
pImplData->ClientType.nUpdateMode = nMode;
_GetRealObject();
ReleaseRef();
}
}
// --> OD 2008-06-19 #i88291#
void SvBaseLink::clearStreamToLoadFrom()
{
m_xInputStreamToLoadFrom.clear();
if( xObj.Is() )
{
xObj->clearStreamToLoadFrom();
}
}
// <--
sal_Bool SvBaseLink::Update()
{
if( OBJECT_CLIENT_SO & nObjType )
{
AddNextRef();
Disconnect();
_GetRealObject();
ReleaseRef();
if( xObj.Is() )
{
xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly);
// m_xInputStreamToLoadFrom = 0;
String sMimeType( SotExchange::GetFormatMimeType(
pImplData->ClientType.nCntntType ));
Any aData;
if( xObj->GetData( aData, sMimeType ) )
{
DataChanged( sMimeType, aData );
//JP 13.07.00: Bug 76817 - for manual Updates there is no
// need to hold the ServerObject
if( OBJECT_CLIENT_DDE == nObjType &&
LINKUPDATE_ONCALL == GetUpdateMode() && xObj.Is() )
xObj->RemoveAllDataAdvise( this );
return sal_True;
}
if( xObj.Is() )
{
// sollten wir asynschron sein?
if( xObj->IsPending() )
return sal_True;
// dann brauchen wir das Object auch nicht mehr
AddNextRef();
Disconnect();
ReleaseRef();
}
}
}
return sal_False;
}
sal_uInt16 SvBaseLink::GetUpdateMode() const
{
return ( OBJECT_CLIENT_SO & nObjType )
? pImplData->ClientType.nUpdateMode
: sal::static_int_cast< sal_uInt16 >( LINKUPDATE_ONCALL );
}
void SvBaseLink::_GetRealObject( sal_Bool bConnect)
{
if(pImpl)
{
if( !pImpl->m_pLinkMgr )
return;
DBG_ASSERT( !xObj.Is(), "object already exist" );
if( OBJECT_CLIENT_DDE == nObjType )
{
String sServer;
if( pImpl->m_pLinkMgr->GetDisplayNames( this, &sServer ) &&
sServer == GetpApp()->GetAppName() ) // interner Link !!!
{
// damit der Internal - Link erzeugt werden kann !!!
nObjType = OBJECT_INTERN;
xObj = pImpl->m_pLinkMgr->CreateObj( this );
pImplData->ClientType.bIntrnlLnk = sal_True;
nObjType = OBJECT_CLIENT_DDE; // damit wir wissen was es mal war !!
}
else
{
pImplData->ClientType.bIntrnlLnk = sal_False;
xObj = pImpl->m_pLinkMgr->CreateObj( this );
}
}
else if( OBJECT_CLIENT_SO & nObjType )
xObj = pImpl->m_pLinkMgr->CreateObj( this );
if( bConnect && ( !xObj.Is() || !xObj->Connect( this ) ) )
Disconnect();
}
else
{
OSL_ENSURE(false, "No pImpl (!)");
}
}
sal_uIntPtr SvBaseLink::GetContentType() const
{
if( OBJECT_CLIENT_SO & nObjType )
return pImplData->ClientType.nCntntType;
return 0; // alle Formate ?
}
sal_Bool SvBaseLink::SetContentType( sal_uIntPtr nType )
{
if( OBJECT_CLIENT_SO & nObjType )
{
pImplData->ClientType.nCntntType = nType;
return sal_True;
}
return sal_False;
}
LinkManager* SvBaseLink::GetLinkManager()
{
if(pImpl)
{
return pImpl->m_pLinkMgr;
}
return 0;
}
const LinkManager* SvBaseLink::GetLinkManager() const
{
if(pImpl)
{
return pImpl->m_pLinkMgr;
}
return 0;
}
void SvBaseLink::SetLinkManager( LinkManager* _pMgr )
{
if(pImpl)
{
pImpl->m_pLinkMgr = _pMgr;
}
else
{
OSL_ENSURE(false, "No pImpl (!)");
}
}
void SvBaseLink::Disconnect()
{
if( xObj.Is() )
{
xObj->RemoveAllDataAdvise( this );
xObj->RemoveConnectAdvise( this );
xObj.Clear();
}
}
void SvBaseLink::DataChanged( const String &, const ::com::sun::star::uno::Any & )
{
switch( nObjType )
{
case OBJECT_DDE_EXTERN:
if( pImplData->DDEType.pItem )
pImplData->DDEType.pItem->Notify();
break;
}
}
void SvBaseLink::Edit( Window* pParent, const Link& rEndEditHdl )
{
if(pImpl)
{
pImpl->m_pParentWin = pParent;
pImpl->m_aEndEditLink = rEndEditHdl;
pImpl->m_bIsConnect = ( xObj.Is() != sal_False );
if( !pImpl->m_bIsConnect )
_GetRealObject( xObj.Is() );
bool bAsync = false;
Link aLink = LINK( this, SvBaseLink, EndEditHdl );
if( OBJECT_CLIENT_SO & nObjType && pImplData->ClientType.bIntrnlLnk )
{
if( pImpl->m_pLinkMgr )
{
SvLinkSourceRef ref = pImpl->m_pLinkMgr->CreateObj( this );
if( ref.Is() )
{
ref->Edit( pParent, this, aLink );
bAsync = true;
}
}
}
else
{
xObj->Edit( pParent, this, aLink );
bAsync = true;
}
if ( !bAsync )
{
ExecuteEdit( String() );
bWasLastEditOK = sal_False;
if ( pImpl->m_aEndEditLink.IsSet() )
pImpl->m_aEndEditLink.Call( this );
}
}
else
{
OSL_ENSURE(false, "No pImpl (!)");
}
}
bool SvBaseLink::ExecuteEdit( const String& _rNewName )
{
if(pImpl)
{
if( _rNewName.Len() != 0 )
{
SetLinkSourceName( _rNewName );
if( !Update() )
{
String sApp, sTopic, sItem, sError;
pImpl->m_pLinkMgr->GetDisplayNames( this, &sApp, &sTopic, &sItem );
if( nObjType == OBJECT_CLIENT_DDE )
{
sError = SfxResId( STR_DDE_ERROR );
sal_uInt16 nFndPos = sError.Search( '%' );
if( STRING_NOTFOUND != nFndPos )
{
sError.Erase( nFndPos, 1 ).Insert( sApp, nFndPos );
nFndPos = nFndPos + sApp.Len();
}
if( STRING_NOTFOUND != ( nFndPos = sError.Search( '%', nFndPos )))
{
sError.Erase( nFndPos, 1 ).Insert( sTopic, nFndPos );
nFndPos = nFndPos + sTopic.Len();
}
if( STRING_NOTFOUND != ( nFndPos = sError.Search( '%', nFndPos )))
sError.Erase( nFndPos, 1 ).Insert( sItem, nFndPos );
}
else
return false;
ErrorBox( pImpl->m_pParentWin, WB_OK, sError ).Execute();
}
}
else if( !pImpl->m_bIsConnect )
Disconnect();
pImpl->m_bIsConnect = false;
return true;
}
else
{
OSL_ENSURE(false, "No pImpl (!)");
return false;
}
}
void SvBaseLink::Closed()
{
if( xObj.Is() )
// beim Advise Abmelden
xObj->RemoveAllDataAdvise( this );
}
FileDialogHelper* SvBaseLink::GetFileDialog( sal_uInt32 nFlags, const String& rFactory ) const
{
if(pImpl)
{
if ( pImpl->m_pFileDlg )
delete pImpl->m_pFileDlg;
pImpl->m_pFileDlg = new FileDialogHelper( nFlags, rFactory );
return pImpl->m_pFileDlg;
}
else
{
OSL_ENSURE(false, "No pImpl (!)");
return 0;
}
}
ImplDdeItem::~ImplDdeItem()
{
bIsInDTOR = sal_True;
// damit im Disconnect nicht jemand auf die Idee kommt, den Pointer zu
// loeschen!!
SvBaseLinkRef aRef( pLink );
aRef->Disconnect();
}
DdeData* ImplDdeItem::Get( sal_uIntPtr nFormat )
{
if( pLink->GetObj() )
{
// ist das noch gueltig?
if( bIsValidData && nFormat == aData.GetFormat() )
return &aData;
Any aValue;
String sMimeType( SotExchange::GetFormatMimeType( nFormat ));
if( pLink->GetObj()->GetData( aValue, sMimeType ) )
{
if( aValue >>= aSeq )
{
aData = DdeData( (const char *)aSeq.getConstArray(), aSeq.getLength(), nFormat );
bIsValidData = sal_True;
return &aData;
}
}
}
aSeq.realloc( 0 );
bIsValidData = sal_False;
return 0;
}
sal_Bool ImplDdeItem::Put( const DdeData* )
{
DBG_ERROR( "ImplDdeItem::Put not implemented" );
return sal_False;
}
void ImplDdeItem::AdviseLoop( sal_Bool bOpen )
{
// Verbindung wird geschlossen, also Link abmelden
if( pLink->GetObj() )
{
if( bOpen )
{
// es wird wieder eine Verbindung hergestellt
if( OBJECT_DDE_EXTERN == pLink->GetObjType() )
{
pLink->GetObj()->AddDataAdvise( pLink, String::CreateFromAscii( "text/plain;charset=utf-16" ), ADVISEMODE_NODATA );
pLink->GetObj()->AddConnectAdvise( pLink );
}
}
else
{
// damit im Disconnect nicht jemand auf die Idee kommt,
// den Pointer zu loeschen!!
SvBaseLinkRef aRef( pLink );
aRef->Disconnect();
}
}
}
static DdeTopic* FindTopic( const String & rLinkName, sal_uInt16* pItemStt )
{
if( 0 == rLinkName.Len() )
return 0;
String sNm( rLinkName );
sal_uInt16 nTokenPos = 0;
String sService( sNm.GetToken( 0, cTokenSeperator, nTokenPos ) );
DdeServices& rSvc = DdeService::GetServices();
for( DdeService* pService = rSvc.First(); pService;
pService = rSvc.Next() )
if( pService->GetName() == sService )
{
// dann suchen wir uns das Topic
String sTopic( sNm.GetToken( 0, cTokenSeperator, nTokenPos ) );
if( pItemStt )
*pItemStt = nTokenPos;
DdeTopics& rTopics = pService->GetTopics();
for( int i = 0; i < 2; ++i )
{
for( DdeTopic* pTopic = rTopics.First(); pTopic;
pTopic = rTopics.Next() )
if( pTopic->GetName() == sTopic )
return pTopic;
// Topic nicht gefunden ?
// dann versuchen wir ihn mal anzulegen
if( i || !pService->MakeTopic( sTopic ) )
break; // hat nicht geklappt, also raus
}
break;
}
return 0;
}
}