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 .
22 #include <sfx2/lnkbase.hxx>
23 #include <sot/exchange.hxx>
24 #include <com/sun/star/uno/Any.hxx>
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
27 #include <sfx2/linkmgr.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/weld.hxx>
30 #include <sfx2/strings.hrc>
31 #include <sfx2/sfxresid.hxx>
32 #include <sfx2/filedlghelper.hxx>
33 #include <tools/debug.hxx>
34 #include <svl/svdde.hxx>
35 #include <osl/diagnose.h>
37 using namespace ::com::sun::star
;
38 using namespace ::com::sun::star::uno
;
47 // only for internal management
48 struct ImplBaseLinkData
52 // applies for all links
53 SotClipboardFormatId nCntntType
; // Update Format
55 bool bIntrnlLnk
; // It is an internal link
56 SfxLinkUpdateMode nUpdateMode
; // UpdateMode
65 tClientType ClientType
;
70 ClientType
.nCntntType
= SotClipboardFormatId::NONE
;
71 ClientType
.bIntrnlLnk
= false;
72 ClientType
.nUpdateMode
= SfxLinkUpdateMode::NONE
;
73 DDEType
.pItem
= nullptr;
79 class ImplDdeItem
: public DdeGetPutItem
83 Sequence
< sal_Int8
> aSeq
; // Datacontainer for DdeData !!!
84 bool bIsValidData
: 1;
88 ImplDdeItem( SvBaseLink
& rLink
, const OUString
& rStr
)
89 : DdeGetPutItem( rStr
), pLink( &rLink
), bIsValidData( false ),
93 virtual ~ImplDdeItem() override
;
95 virtual DdeData
* Get( SotClipboardFormatId
) override
;
96 virtual bool Put( const DdeData
* ) override
;
97 virtual void AdviseLoop( bool ) override
;
101 bIsValidData
= false;
102 DdeGetPutItem::NotifyClient();
105 bool IsInDTOR() const { return bIsInDTOR
; }
110 SvBaseLink::SvBaseLink()
111 : m_pLinkMgr( nullptr )
112 , m_pParentWin( nullptr )
113 , m_bIsConnect( false )
114 , m_bIsReadOnly(false)
116 mnObjType
= SvBaseLinkObjectType::ClientSo
;
117 pImplData
.reset( new ImplBaseLinkData
);
118 bVisible
= bSynchron
= true;
119 bWasLastEditOK
= false;
123 SvBaseLink::SvBaseLink( SfxLinkUpdateMode nUpdateMode
, SotClipboardFormatId nContentType
)
124 : m_pLinkMgr( nullptr )
125 , m_pParentWin( nullptr )
126 , m_bIsConnect( false )
127 , m_bIsReadOnly(false)
129 mnObjType
= SvBaseLinkObjectType::ClientSo
;
130 pImplData
.reset( new ImplBaseLinkData
);
131 bVisible
= bSynchron
= true;
132 bWasLastEditOK
= false;
134 // It is going to be an OLE-Link,
135 pImplData
->ClientType
.nUpdateMode
= nUpdateMode
;
136 pImplData
->ClientType
.nCntntType
= nContentType
;
137 pImplData
->ClientType
.bIntrnlLnk
= false;
142 static DdeTopic
* FindTopic( const OUString
& rLinkName
, sal_uInt16
* pItemStt
)
144 if( rLinkName
.isEmpty() )
147 OUString
sNm( rLinkName
);
148 sal_Int32 nTokenPos
= 0;
149 OUString
sService( sNm
.getToken( 0, cTokenSeparator
, nTokenPos
) );
151 DdeServices
& rSvc
= DdeService::GetServices();
152 for (auto const& elem
: rSvc
)
154 if(elem
->GetName() == sService
)
156 // then we search for the Topic
157 OUString
sTopic( sNm
.getToken( 0, cTokenSeparator
, nTokenPos
) );
159 *pItemStt
= nTokenPos
;
161 std::vector
<DdeTopic
*>& rTopics
= elem
->GetTopics();
163 for (auto const& topic
: rTopics
)
164 if( topic
->GetName() == sTopic
)
172 SvBaseLink::SvBaseLink( const OUString
& rLinkName
, SvBaseLinkObjectType nObjectType
, SvLinkSource
* pObj
)
173 : m_pLinkMgr( nullptr )
174 , m_pParentWin( nullptr )
175 , m_bIsConnect( false )
176 , m_bIsReadOnly(false)
178 bVisible
= bSynchron
= true;
179 bWasLastEditOK
= false;
180 aLinkName
= rLinkName
;
181 pImplData
.reset( new ImplBaseLinkData
);
182 mnObjType
= nObjectType
;
186 DBG_ASSERT( pObj
, "Where is my left-most object" );
190 if( SvBaseLinkObjectType::DdeExternal
== mnObjType
)
192 sal_uInt16 nItemStt
= 0;
193 DdeTopic
* pTopic
= FindTopic( aLinkName
, &nItemStt
);
196 // then we have it all together
197 // MM_TODO how do I get the name
198 OUString aStr
= aLinkName
; // xLinkName->GetDisplayName();
199 aStr
= aStr
.copy( nItemStt
);
200 pImplData
->DDEType
.pItem
= new ImplDdeItem( *this, aStr
);
201 pTopic
->InsertItem( pImplData
->DDEType
.pItem
);
207 else if( pObj
->Connect( this ) )
213 SvBaseLink::~SvBaseLink()
217 if( mnObjType
== SvBaseLinkObjectType::DdeExternal
)
219 if( !pImplData
->DDEType
.pItem
->IsInDTOR() )
220 delete pImplData
->DDEType
.pItem
;
226 IMPL_LINK( SvBaseLink
, EndEditHdl
, const OUString
&, _rNewName
, void )
228 OUString sNewName
= _rNewName
;
229 if ( !ExecuteEdit( sNewName
) )
231 bWasLastEditOK
= !sNewName
.isEmpty();
232 m_aEndEditLink
.Call( *this );
236 void SvBaseLink::SetObjType( SvBaseLinkObjectType mnObjTypeP
)
238 DBG_ASSERT( mnObjType
!= SvBaseLinkObjectType::ClientDde
, "type already set" );
239 DBG_ASSERT( !xObj
.is(), "object exist" );
241 mnObjType
= mnObjTypeP
;
245 void SvBaseLink::SetName( const OUString
& rNm
)
251 void SvBaseLink::SetObj( SvLinkSource
* pObj
)
253 DBG_ASSERT( (isClientType(mnObjType
) &&
254 pImplData
->ClientType
.bIntrnlLnk
) ||
255 mnObjType
== SvBaseLinkObjectType::ClientGraphic
,
261 void SvBaseLink::SetLinkSourceName( const OUString
& rLnkNm
)
263 if( aLinkName
== rLnkNm
)
266 AddNextRef(); // should be superfluous
267 // remove old connection
274 ReleaseRef(); // should be superfluous
278 void SvBaseLink::SetUpdateMode( SfxLinkUpdateMode nMode
)
280 if( isClientType(mnObjType
) &&
281 pImplData
->ClientType
.nUpdateMode
!= nMode
)
286 pImplData
->ClientType
.nUpdateMode
= nMode
;
293 void SvBaseLink::clearStreamToLoadFrom()
295 m_xInputStreamToLoadFrom
.clear();
298 xObj
->clearStreamToLoadFrom();
302 bool SvBaseLink::Update()
304 if( isClientType(mnObjType
) )
313 xObj
->setStreamToLoadFrom(m_xInputStreamToLoadFrom
,m_bIsReadOnly
);
314 OUString
sMimeType( SotExchange::GetFormatMimeType(
315 pImplData
->ClientType
.nCntntType
));
318 if( xObj
->GetData( aData
, sMimeType
) )
320 UpdateResult eRes
= DataChanged(sMimeType
, aData
);
321 bool bSuccess
= eRes
== SUCCESS
;
322 //for manual Updates there is no need to hold the ServerObject
323 if( SvBaseLinkObjectType::ClientDde
== mnObjType
&&
324 SfxLinkUpdateMode::ONCALL
== GetUpdateMode() && xObj
.is() )
325 xObj
->RemoveAllDataAdvise( this );
330 // should be asynchronous?
331 if( xObj
->IsPending() )
334 // we do not need the object anymore
345 SfxLinkUpdateMode
SvBaseLink::GetUpdateMode() const
347 return isClientType(mnObjType
)
348 ? pImplData
->ClientType
.nUpdateMode
349 : SfxLinkUpdateMode::ONCALL
;
353 void SvBaseLink::GetRealObject_( bool bConnect
)
358 DBG_ASSERT( !xObj
.is(), "object already exist" );
360 if( SvBaseLinkObjectType::ClientDde
== mnObjType
)
363 if( sfx2::LinkManager::GetDisplayNames( this, &sServer
) &&
364 sServer
== Application::GetAppName() ) // internal Link !!!
366 // so that the Internal link can be created!
367 mnObjType
= SvBaseLinkObjectType::Internal
;
368 xObj
= sfx2::LinkManager::CreateObj( this );
370 pImplData
->ClientType
.bIntrnlLnk
= true;
371 mnObjType
= SvBaseLinkObjectType::ClientDde
; // so we know what it once was!
375 pImplData
->ClientType
.bIntrnlLnk
= false;
376 xObj
= sfx2::LinkManager::CreateObj( this );
379 else if( isClientType(mnObjType
) )
380 xObj
= sfx2::LinkManager::CreateObj( this );
382 if( bConnect
&& ( !xObj
.is() || !xObj
->Connect( this ) ) )
386 SotClipboardFormatId
SvBaseLink::GetContentType() const
388 if( isClientType(mnObjType
) )
389 return pImplData
->ClientType
.nCntntType
;
391 return SotClipboardFormatId::NONE
; // all Formats ?
395 void SvBaseLink::SetContentType( SotClipboardFormatId nType
)
397 if( isClientType(mnObjType
) )
399 pImplData
->ClientType
.nCntntType
= nType
;
403 LinkManager
* SvBaseLink::GetLinkManager()
408 const LinkManager
* SvBaseLink::GetLinkManager() const
413 void SvBaseLink::SetLinkManager( LinkManager
* _pMgr
)
418 void SvBaseLink::Disconnect()
422 xObj
->RemoveAllDataAdvise( this );
423 xObj
->RemoveConnectAdvise( this );
428 SvBaseLink::UpdateResult
SvBaseLink::DataChanged( const OUString
&, const css::uno::Any
& )
430 if ( mnObjType
== SvBaseLinkObjectType::DdeExternal
)
432 if( pImplData
->DDEType
.pItem
)
433 pImplData
->DDEType
.pItem
->Notify();
438 void SvBaseLink::Edit(weld::Window
* pParent
, const Link
<SvBaseLink
&,void>& rEndEditHdl
)
440 m_pParentWin
= pParent
;
441 m_aEndEditLink
= rEndEditHdl
;
442 m_bIsConnect
= xObj
.is();
444 GetRealObject_( xObj
.is() );
447 Link
<const OUString
&, void> aLink
= LINK( this, SvBaseLink
, EndEditHdl
);
449 if( isClientType(mnObjType
) && pImplData
->ClientType
.bIntrnlLnk
)
453 SvLinkSourceRef ref
= sfx2::LinkManager::CreateObj( this );
456 ref
->Edit( pParent
, this, aLink
);
463 xObj
->Edit( pParent
, this, aLink
);
469 ExecuteEdit( OUString() );
470 bWasLastEditOK
= false;
471 m_aEndEditLink
.Call( *this );
475 bool SvBaseLink::ExecuteEdit( const OUString
& _rNewName
)
477 if( !_rNewName
.isEmpty() )
479 SetLinkSourceName( _rNewName
);
482 OUString sApp
, sTopic
, sItem
, sError
;
483 sfx2::LinkManager::GetDisplayNames( this, &sApp
, &sTopic
, &sItem
);
484 if( mnObjType
== SvBaseLinkObjectType::ClientDde
)
486 sError
= SfxResId(STR_DDE_ERROR
);
488 sal_Int32 nFndPos
= sError
.indexOf( "%1" );
491 sError
= sError
.replaceAt( nFndPos
, 2, sApp
);
492 nFndPos
= nFndPos
+ sApp
.getLength();
494 if( -1 != ( nFndPos
= sError
.indexOf( "%2", nFndPos
)))
496 sError
= sError
.replaceAt( nFndPos
, 2, sTopic
);
497 nFndPos
= nFndPos
+ sTopic
.getLength();
499 if( -1 != ( nFndPos
= sError
.indexOf( "%3", nFndPos
)))
500 sError
= sError
.replaceAt( nFndPos
, 2, sItem
);
507 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(m_pParentWin
,
508 VclMessageType::Warning
, VclButtonsType::Ok
, sError
));
512 else if( !m_bIsConnect
)
514 m_bIsConnect
= false;
518 void SvBaseLink::Closed()
521 xObj
->RemoveAllDataAdvise( this );
524 FileDialogHelper
& SvBaseLink::GetInsertFileDialog(const OUString
& rFactory
)
526 m_pFileDlg
.reset( new FileDialogHelper(
527 ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
,
528 FileDialogFlags::Insert
, rFactory
, SfxFilterFlags::NONE
, SfxFilterFlags::NONE
, m_pParentWin
) );
532 ImplDdeItem::~ImplDdeItem()
535 // So that no-one gets the idea to delete the pointer when Disconnecting!
536 tools::SvRef
<SvBaseLink
> aRef( pLink
);
540 DdeData
* ImplDdeItem::Get( SotClipboardFormatId nFormat
)
542 if( pLink
->GetObj() )
544 // is it still valid?
545 if( bIsValidData
&& nFormat
== aData
.GetFormat() )
549 OUString
sMimeType( SotExchange::GetFormatMimeType( nFormat
));
550 if( pLink
->GetObj()->GetData( aValue
, sMimeType
) )
552 if( aValue
>>= aSeq
)
554 aData
= DdeData( aSeq
.getConstArray(), aSeq
.getLength(), nFormat
);
562 bIsValidData
= false;
567 bool ImplDdeItem::Put( const DdeData
* )
569 OSL_FAIL( "ImplDdeItem::Put not implemented" );
574 void ImplDdeItem::AdviseLoop( bool bOpen
)
576 // Connection is closed, so also unsubscribe link
577 if( !pLink
->GetObj() )
582 // A connection is re-established
583 if( SvBaseLinkObjectType::DdeExternal
== pLink
->GetObjType() )
585 pLink
->GetObj()->AddDataAdvise( pLink
, "text/plain;charset=utf-16", ADVISEMODE_NODATA
);
586 pLink
->GetObj()->AddConnectAdvise( pLink
);
591 // So that no-one gets the idea to delete the pointer
592 // when Disconnecting!
593 tools::SvRef
<SvBaseLink
> aRef( pLink
);
600 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */