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
;
51 Link
<SvBaseLink
&,void> m_aEndEditLink
;
52 LinkManager
* m_pLinkMgr
;
53 weld::Window
* m_pParentWin
;
54 std::unique_ptr
<FileDialogHelper
>
60 , m_pParentWin( nullptr )
61 , m_bIsConnect( false )
65 // only for internal management
66 struct ImplBaseLinkData
70 // applies for all links
71 SotClipboardFormatId nCntntType
; // Update Format
73 bool bIntrnlLnk
; // It is an internal link
74 SfxLinkUpdateMode nUpdateMode
; // UpdateMode
83 tClientType ClientType
;
88 ClientType
.nCntntType
= SotClipboardFormatId::NONE
;
89 ClientType
.bIntrnlLnk
= false;
90 ClientType
.nUpdateMode
= SfxLinkUpdateMode::NONE
;
91 DDEType
.pItem
= nullptr;
97 class ImplDdeItem
: public DdeGetPutItem
101 Sequence
< sal_Int8
> aSeq
; // Datacontainer for DdeData !!!
102 bool bIsValidData
: 1;
106 ImplDdeItem( SvBaseLink
& rLink
, const OUString
& rStr
)
107 : DdeGetPutItem( rStr
), pLink( &rLink
), bIsValidData( false ),
111 virtual ~ImplDdeItem() override
;
113 virtual DdeData
* Get( SotClipboardFormatId
) override
;
114 virtual bool Put( const DdeData
* ) override
;
115 virtual void AdviseLoop( bool ) override
;
119 bIsValidData
= false;
120 DdeGetPutItem::NotifyClient();
123 bool IsInDTOR() const { return bIsInDTOR
; }
128 SvBaseLink::SvBaseLink()
129 : pImpl ( new BaseLink_Impl
),
132 mnObjType
= SvBaseLinkObjectType::ClientSo
;
133 pImplData
.reset( new ImplBaseLinkData
);
134 bVisible
= bSynchron
= true;
135 bWasLastEditOK
= false;
139 SvBaseLink::SvBaseLink( SfxLinkUpdateMode nUpdateMode
, SotClipboardFormatId nContentType
)
140 : pImpl( new BaseLink_Impl
),
143 mnObjType
= SvBaseLinkObjectType::ClientSo
;
144 pImplData
.reset( new ImplBaseLinkData
);
145 bVisible
= bSynchron
= true;
146 bWasLastEditOK
= false;
148 // It is going to be an OLE-Link,
149 pImplData
->ClientType
.nUpdateMode
= nUpdateMode
;
150 pImplData
->ClientType
.nCntntType
= nContentType
;
151 pImplData
->ClientType
.bIntrnlLnk
= false;
156 static DdeTopic
* FindTopic( const OUString
& rLinkName
, sal_uInt16
* pItemStt
)
158 if( rLinkName
.isEmpty() )
161 OUString
sNm( rLinkName
);
162 sal_Int32 nTokenPos
= 0;
163 OUString
sService( sNm
.getToken( 0, cTokenSeparator
, nTokenPos
) );
165 DdeServices
& rSvc
= DdeService::GetServices();
166 for (auto const& elem
: rSvc
)
168 if(elem
->GetName() == sService
)
170 // then we search for the Topic
171 OUString
sTopic( sNm
.getToken( 0, cTokenSeparator
, nTokenPos
) );
173 *pItemStt
= nTokenPos
;
175 std::vector
<DdeTopic
*>& rTopics
= elem
->GetTopics();
177 for (auto const& topic
: rTopics
)
178 if( topic
->GetName() == sTopic
)
186 SvBaseLink::SvBaseLink( const OUString
& rLinkName
, SvBaseLinkObjectType nObjectType
, SvLinkSource
* pObj
)
188 , m_bIsReadOnly(false)
190 bVisible
= bSynchron
= true;
191 bWasLastEditOK
= false;
192 aLinkName
= rLinkName
;
193 pImplData
.reset( new ImplBaseLinkData
);
194 mnObjType
= nObjectType
;
198 DBG_ASSERT( pObj
, "Where is my left-most object" );
202 if( SvBaseLinkObjectType::DdeExternal
== mnObjType
)
204 sal_uInt16 nItemStt
= 0;
205 DdeTopic
* pTopic
= FindTopic( aLinkName
, &nItemStt
);
208 // then we have it all together
209 // MM_TODO how do I get the name
210 OUString aStr
= aLinkName
; // xLinkName->GetDisplayName();
211 aStr
= aStr
.copy( nItemStt
);
212 pImplData
->DDEType
.pItem
= new ImplDdeItem( *this, aStr
);
213 pTopic
->InsertItem( pImplData
->DDEType
.pItem
);
219 else if( pObj
->Connect( this ) )
225 SvBaseLink::~SvBaseLink()
229 if( mnObjType
== SvBaseLinkObjectType::DdeExternal
)
231 if( !pImplData
->DDEType
.pItem
->IsInDTOR() )
232 delete pImplData
->DDEType
.pItem
;
238 IMPL_LINK( SvBaseLink
, EndEditHdl
, const OUString
&, _rNewName
, void )
240 OUString sNewName
= _rNewName
;
241 if ( !ExecuteEdit( sNewName
) )
243 bWasLastEditOK
= !sNewName
.isEmpty();
244 pImpl
->m_aEndEditLink
.Call( *this );
248 void SvBaseLink::SetObjType( SvBaseLinkObjectType mnObjTypeP
)
250 DBG_ASSERT( mnObjType
!= SvBaseLinkObjectType::ClientDde
, "type already set" );
251 DBG_ASSERT( !xObj
.is(), "object exist" );
253 mnObjType
= mnObjTypeP
;
257 void SvBaseLink::SetName( const OUString
& rNm
)
263 void SvBaseLink::SetObj( SvLinkSource
* pObj
)
265 DBG_ASSERT( (isClientType(mnObjType
) &&
266 pImplData
->ClientType
.bIntrnlLnk
) ||
267 mnObjType
== SvBaseLinkObjectType::ClientGraphic
,
273 void SvBaseLink::SetLinkSourceName( const OUString
& rLnkNm
)
275 if( aLinkName
== rLnkNm
)
278 AddNextRef(); // should be superfluous
279 // remove old connection
286 ReleaseRef(); // should be superfluous
290 void SvBaseLink::SetUpdateMode( SfxLinkUpdateMode nMode
)
292 if( isClientType(mnObjType
) &&
293 pImplData
->ClientType
.nUpdateMode
!= nMode
)
298 pImplData
->ClientType
.nUpdateMode
= nMode
;
305 void SvBaseLink::clearStreamToLoadFrom()
307 m_xInputStreamToLoadFrom
.clear();
310 xObj
->clearStreamToLoadFrom();
314 bool SvBaseLink::Update()
316 if( isClientType(mnObjType
) )
325 xObj
->setStreamToLoadFrom(m_xInputStreamToLoadFrom
,m_bIsReadOnly
);
326 OUString
sMimeType( SotExchange::GetFormatMimeType(
327 pImplData
->ClientType
.nCntntType
));
330 if( xObj
->GetData( aData
, sMimeType
) )
332 UpdateResult eRes
= DataChanged(sMimeType
, aData
);
333 bool bSuccess
= eRes
== SUCCESS
;
334 //for manual Updates there is no need to hold the ServerObject
335 if( SvBaseLinkObjectType::ClientDde
== mnObjType
&&
336 SfxLinkUpdateMode::ONCALL
== GetUpdateMode() && xObj
.is() )
337 xObj
->RemoveAllDataAdvise( this );
342 // should be asynchronous?
343 if( xObj
->IsPending() )
346 // we do not need the object anymore
357 SfxLinkUpdateMode
SvBaseLink::GetUpdateMode() const
359 return isClientType(mnObjType
)
360 ? pImplData
->ClientType
.nUpdateMode
361 : SfxLinkUpdateMode::ONCALL
;
365 void SvBaseLink::GetRealObject_( bool bConnect
)
367 if( !pImpl
->m_pLinkMgr
)
370 DBG_ASSERT( !xObj
.is(), "object already exist" );
372 if( SvBaseLinkObjectType::ClientDde
== mnObjType
)
375 if( sfx2::LinkManager::GetDisplayNames( this, &sServer
) &&
376 sServer
== Application::GetAppName() ) // internal Link !!!
378 // so that the Internal link can be created!
379 mnObjType
= SvBaseLinkObjectType::Internal
;
380 xObj
= sfx2::LinkManager::CreateObj( this );
382 pImplData
->ClientType
.bIntrnlLnk
= true;
383 mnObjType
= SvBaseLinkObjectType::ClientDde
; // so we know what it once was!
387 pImplData
->ClientType
.bIntrnlLnk
= false;
388 xObj
= sfx2::LinkManager::CreateObj( this );
391 else if( isClientType(mnObjType
) )
392 xObj
= sfx2::LinkManager::CreateObj( this );
394 if( bConnect
&& ( !xObj
.is() || !xObj
->Connect( this ) ) )
398 SotClipboardFormatId
SvBaseLink::GetContentType() const
400 if( isClientType(mnObjType
) )
401 return pImplData
->ClientType
.nCntntType
;
403 return SotClipboardFormatId::NONE
; // all Formats ?
407 void SvBaseLink::SetContentType( SotClipboardFormatId nType
)
409 if( isClientType(mnObjType
) )
411 pImplData
->ClientType
.nCntntType
= nType
;
415 LinkManager
* SvBaseLink::GetLinkManager()
417 return pImpl
->m_pLinkMgr
;
420 const LinkManager
* SvBaseLink::GetLinkManager() const
422 return pImpl
->m_pLinkMgr
;
425 void SvBaseLink::SetLinkManager( LinkManager
* _pMgr
)
427 pImpl
->m_pLinkMgr
= _pMgr
;
430 void SvBaseLink::Disconnect()
434 xObj
->RemoveAllDataAdvise( this );
435 xObj
->RemoveConnectAdvise( this );
440 SvBaseLink::UpdateResult
SvBaseLink::DataChanged( const OUString
&, const css::uno::Any
& )
442 if ( mnObjType
== SvBaseLinkObjectType::DdeExternal
)
444 if( pImplData
->DDEType
.pItem
)
445 pImplData
->DDEType
.pItem
->Notify();
450 void SvBaseLink::Edit(weld::Window
* pParent
, const Link
<SvBaseLink
&,void>& rEndEditHdl
)
452 pImpl
->m_pParentWin
= pParent
;
453 pImpl
->m_aEndEditLink
= rEndEditHdl
;
454 pImpl
->m_bIsConnect
= xObj
.is();
455 if( !pImpl
->m_bIsConnect
)
456 GetRealObject_( xObj
.is() );
459 Link
<const OUString
&, void> aLink
= LINK( this, SvBaseLink
, EndEditHdl
);
461 if( isClientType(mnObjType
) && pImplData
->ClientType
.bIntrnlLnk
)
463 if( pImpl
->m_pLinkMgr
)
465 SvLinkSourceRef ref
= sfx2::LinkManager::CreateObj( this );
468 ref
->Edit( pParent
, this, aLink
);
475 xObj
->Edit( pParent
, this, aLink
);
481 ExecuteEdit( OUString() );
482 bWasLastEditOK
= false;
483 pImpl
->m_aEndEditLink
.Call( *this );
487 bool SvBaseLink::ExecuteEdit( const OUString
& _rNewName
)
489 if( !_rNewName
.isEmpty() )
491 SetLinkSourceName( _rNewName
);
494 OUString sApp
, sTopic
, sItem
, sError
;
495 sfx2::LinkManager::GetDisplayNames( this, &sApp
, &sTopic
, &sItem
);
496 if( mnObjType
== SvBaseLinkObjectType::ClientDde
)
498 sError
= SfxResId(STR_DDE_ERROR
);
500 sal_Int32 nFndPos
= sError
.indexOf( "%1" );
503 sError
= sError
.replaceAt( nFndPos
, 2, sApp
);
504 nFndPos
= nFndPos
+ sApp
.getLength();
506 if( -1 != ( nFndPos
= sError
.indexOf( "%2", nFndPos
)))
508 sError
= sError
.replaceAt( nFndPos
, 2, sTopic
);
509 nFndPos
= nFndPos
+ sTopic
.getLength();
511 if( -1 != ( nFndPos
= sError
.indexOf( "%3", nFndPos
)))
512 sError
= sError
.replaceAt( nFndPos
, 2, sItem
);
519 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pImpl
->m_pParentWin
,
520 VclMessageType::Warning
, VclButtonsType::Ok
, sError
));
524 else if( !pImpl
->m_bIsConnect
)
526 pImpl
->m_bIsConnect
= false;
530 void SvBaseLink::Closed()
533 xObj
->RemoveAllDataAdvise( this );
536 FileDialogHelper
& SvBaseLink::GetInsertFileDialog(const OUString
& rFactory
) const
538 pImpl
->m_pFileDlg
.reset( new FileDialogHelper(
539 ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
,
540 FileDialogFlags::Insert
, rFactory
, SfxFilterFlags::NONE
, SfxFilterFlags::NONE
, pImpl
->m_pParentWin
) );
541 return *pImpl
->m_pFileDlg
;
544 ImplDdeItem::~ImplDdeItem()
547 // So that no-one gets the idea to delete the pointer when Disconnecting!
548 tools::SvRef
<SvBaseLink
> aRef( pLink
);
552 DdeData
* ImplDdeItem::Get( SotClipboardFormatId nFormat
)
554 if( pLink
->GetObj() )
556 // is it still valid?
557 if( bIsValidData
&& nFormat
== aData
.GetFormat() )
561 OUString
sMimeType( SotExchange::GetFormatMimeType( nFormat
));
562 if( pLink
->GetObj()->GetData( aValue
, sMimeType
) )
564 if( aValue
>>= aSeq
)
566 aData
= DdeData( aSeq
.getConstArray(), aSeq
.getLength(), nFormat
);
574 bIsValidData
= false;
579 bool ImplDdeItem::Put( const DdeData
* )
581 OSL_FAIL( "ImplDdeItem::Put not implemented" );
586 void ImplDdeItem::AdviseLoop( bool bOpen
)
588 // Connection is closed, so also unsubscribe link
589 if( !pLink
->GetObj() )
594 // A connection is re-established
595 if( SvBaseLinkObjectType::DdeExternal
== pLink
->GetObjType() )
597 pLink
->GetObj()->AddDataAdvise( pLink
, "text/plain;charset=utf-16", ADVISEMODE_NODATA
);
598 pLink
->GetObj()->AddConnectAdvise( pLink
);
603 // So that no-one gets the idea to delete the pointer
604 // when Disconnecting!
605 tools::SvRef
<SvBaseLink
> aRef( pLink
);
612 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */