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>
36 #include <officecfg/Office/Common.hxx>
38 using namespace ::com::sun::star
;
39 using namespace ::com::sun::star::uno
;
48 // only for internal management
49 struct ImplBaseLinkData
53 // applies for all links
54 SotClipboardFormatId nCntntType
; // Update Format
56 bool bIntrnlLnk
; // It is an internal link
57 SfxLinkUpdateMode nUpdateMode
; // UpdateMode
66 tClientType ClientType
;
71 ClientType
.nCntntType
= SotClipboardFormatId::NONE
;
72 ClientType
.bIntrnlLnk
= false;
73 ClientType
.nUpdateMode
= SfxLinkUpdateMode::NONE
;
74 DDEType
.pItem
= nullptr;
80 class ImplDdeItem
: public DdeGetPutItem
84 Sequence
< sal_Int8
> aSeq
; // Datacontainer for DdeData !!!
85 bool bIsValidData
: 1;
89 ImplDdeItem( SvBaseLink
& rLink
, const OUString
& rStr
)
90 : DdeGetPutItem( rStr
), pLink( &rLink
), bIsValidData( false ),
94 virtual ~ImplDdeItem() override
;
96 virtual DdeData
* Get( SotClipboardFormatId
) override
;
97 virtual bool Put( const DdeData
* ) override
;
98 virtual void AdviseLoop( bool ) override
;
102 bIsValidData
= false;
103 DdeGetPutItem::NotifyClient();
106 bool IsInDTOR() const { return bIsInDTOR
; }
111 SvBaseLink::SvBaseLink()
112 : m_pLinkMgr( nullptr )
113 , m_pParentWin( nullptr )
114 , m_bIsConnect( false )
115 , m_bIsReadOnly(false)
117 mnObjType
= SvBaseLinkObjectType::ClientSo
;
118 pImplData
.reset( new ImplBaseLinkData
);
119 bVisible
= bSynchron
= true;
120 bWasLastEditOK
= false;
124 SvBaseLink::SvBaseLink( SfxLinkUpdateMode nUpdateMode
, SotClipboardFormatId nContentType
)
125 : m_pLinkMgr( nullptr )
126 , m_pParentWin( nullptr )
127 , m_bIsConnect( false )
128 , m_bIsReadOnly(false)
130 mnObjType
= SvBaseLinkObjectType::ClientSo
;
131 pImplData
.reset( new ImplBaseLinkData
);
132 bVisible
= bSynchron
= true;
133 bWasLastEditOK
= false;
135 // It is going to be an OLE-Link,
136 pImplData
->ClientType
.nUpdateMode
= nUpdateMode
;
137 pImplData
->ClientType
.nCntntType
= nContentType
;
138 pImplData
->ClientType
.bIntrnlLnk
= false;
143 static DdeTopic
* FindTopic( const OUString
& rLinkName
, sal_uInt16
* pItemStt
)
145 if( rLinkName
.isEmpty() )
148 OUString
sNm( rLinkName
);
149 sal_Int32 nTokenPos
= 0;
150 OUString
sService( sNm
.getToken( 0, cTokenSeparator
, nTokenPos
) );
152 DdeServices
& rSvc
= DdeService::GetServices();
153 for (auto const& elem
: rSvc
)
155 if(elem
->GetName() == sService
)
157 // then we search for the Topic
158 OUString
sTopic( sNm
.getToken( 0, cTokenSeparator
, nTokenPos
) );
160 *pItemStt
= nTokenPos
;
162 std::vector
<DdeTopic
*>& rTopics
= elem
->GetTopics();
164 for (auto const& topic
: rTopics
)
165 if( topic
->GetName() == sTopic
)
173 SvBaseLink::SvBaseLink( const OUString
& rLinkName
, SvBaseLinkObjectType nObjectType
, SvLinkSource
* pObj
)
174 : m_pLinkMgr( nullptr )
175 , m_pParentWin( nullptr )
176 , m_bIsConnect( false )
177 , m_bIsReadOnly(false)
179 bVisible
= bSynchron
= true;
180 bWasLastEditOK
= false;
181 aLinkName
= rLinkName
;
182 pImplData
.reset( new ImplBaseLinkData
);
183 mnObjType
= nObjectType
;
187 DBG_ASSERT( pObj
, "Where is my left-most object" );
191 if( SvBaseLinkObjectType::DdeExternal
== mnObjType
)
193 sal_uInt16 nItemStt
= 0;
194 DdeTopic
* pTopic
= FindTopic( aLinkName
, &nItemStt
);
197 // then we have it all together
198 // MM_TODO how do I get the name
199 OUString aStr
= aLinkName
; // xLinkName->GetDisplayName();
200 aStr
= aStr
.copy( nItemStt
);
201 pImplData
->DDEType
.pItem
= new ImplDdeItem( *this, aStr
);
202 pTopic
->InsertItem( pImplData
->DDEType
.pItem
);
208 else if( pObj
->Connect( this ) )
214 SvBaseLink::~SvBaseLink()
218 if( mnObjType
== SvBaseLinkObjectType::DdeExternal
)
220 if( !pImplData
->DDEType
.pItem
->IsInDTOR() )
221 delete pImplData
->DDEType
.pItem
;
227 IMPL_LINK( SvBaseLink
, EndEditHdl
, const OUString
&, _rNewName
, void )
229 OUString sNewName
= _rNewName
;
230 if ( !ExecuteEdit( sNewName
) )
232 bWasLastEditOK
= !sNewName
.isEmpty();
233 m_aEndEditLink
.Call( *this );
237 void SvBaseLink::SetObjType( SvBaseLinkObjectType mnObjTypeP
)
239 DBG_ASSERT( mnObjType
!= SvBaseLinkObjectType::ClientDde
, "type already set" );
240 DBG_ASSERT( !xObj
.is(), "object exist" );
242 mnObjType
= mnObjTypeP
;
246 void SvBaseLink::SetName( const OUString
& rNm
)
252 void SvBaseLink::SetObj( SvLinkSource
* pObj
)
254 DBG_ASSERT( (isClientType(mnObjType
) &&
255 pImplData
->ClientType
.bIntrnlLnk
) ||
256 mnObjType
== SvBaseLinkObjectType::ClientGraphic
,
262 void SvBaseLink::SetLinkSourceName( const OUString
& rLnkNm
)
264 if( aLinkName
== rLnkNm
)
267 AddNextRef(); // should be superfluous
268 // remove old connection
275 ReleaseRef(); // should be superfluous
279 void SvBaseLink::SetUpdateMode( SfxLinkUpdateMode nMode
)
281 if( isClientType(mnObjType
) &&
282 pImplData
->ClientType
.nUpdateMode
!= nMode
)
287 pImplData
->ClientType
.nUpdateMode
= nMode
;
294 void SvBaseLink::clearStreamToLoadFrom()
296 m_xInputStreamToLoadFrom
.clear();
299 xObj
->clearStreamToLoadFrom();
303 bool SvBaseLink::Update()
305 if(officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
308 if( isClientType(mnObjType
) )
317 xObj
->setStreamToLoadFrom(m_xInputStreamToLoadFrom
,m_bIsReadOnly
);
318 OUString
sMimeType( SotExchange::GetFormatMimeType(
319 pImplData
->ClientType
.nCntntType
));
322 if( xObj
->GetData( aData
, sMimeType
) )
324 UpdateResult eRes
= DataChanged(sMimeType
, aData
);
325 bool bSuccess
= eRes
== SUCCESS
;
326 //for manual Updates there is no need to hold the ServerObject
327 if( SvBaseLinkObjectType::ClientDde
== mnObjType
&&
328 SfxLinkUpdateMode::ONCALL
== GetUpdateMode() && xObj
.is() )
329 xObj
->RemoveAllDataAdvise( this );
334 // should be asynchronous?
335 if( xObj
->IsPending() )
338 // we do not need the object anymore
349 SfxLinkUpdateMode
SvBaseLink::GetUpdateMode() const
351 return isClientType(mnObjType
)
352 ? pImplData
->ClientType
.nUpdateMode
353 : SfxLinkUpdateMode::ONCALL
;
357 void SvBaseLink::GetRealObject_( bool bConnect
)
362 DBG_ASSERT( !xObj
.is(), "object already exist" );
364 if( SvBaseLinkObjectType::ClientDde
== mnObjType
)
367 if( sfx2::LinkManager::GetDisplayNames( this, &sServer
) &&
368 sServer
== Application::GetAppName() ) // internal Link !!!
370 // so that the Internal link can be created!
371 mnObjType
= SvBaseLinkObjectType::Internal
;
372 xObj
= sfx2::LinkManager::CreateObj( this );
374 pImplData
->ClientType
.bIntrnlLnk
= true;
375 mnObjType
= SvBaseLinkObjectType::ClientDde
; // so we know what it once was!
379 pImplData
->ClientType
.bIntrnlLnk
= false;
380 xObj
= sfx2::LinkManager::CreateObj( this );
383 else if( isClientType(mnObjType
) )
384 xObj
= sfx2::LinkManager::CreateObj( this );
386 if( bConnect
&& ( !xObj
.is() || !xObj
->Connect( this ) ) )
390 SotClipboardFormatId
SvBaseLink::GetContentType() const
392 if( isClientType(mnObjType
) )
393 return pImplData
->ClientType
.nCntntType
;
395 return SotClipboardFormatId::NONE
; // all Formats ?
399 void SvBaseLink::SetContentType( SotClipboardFormatId nType
)
401 if( isClientType(mnObjType
) )
403 pImplData
->ClientType
.nCntntType
= nType
;
407 LinkManager
* SvBaseLink::GetLinkManager()
412 const LinkManager
* SvBaseLink::GetLinkManager() const
417 void SvBaseLink::SetLinkManager( LinkManager
* _pMgr
)
422 void SvBaseLink::Disconnect()
426 xObj
->RemoveAllDataAdvise( this );
427 xObj
->RemoveConnectAdvise( this );
432 SvBaseLink::UpdateResult
SvBaseLink::DataChanged( const OUString
&, const css::uno::Any
& )
434 if ( mnObjType
== SvBaseLinkObjectType::DdeExternal
)
436 if( pImplData
->DDEType
.pItem
)
437 pImplData
->DDEType
.pItem
->Notify();
442 void SvBaseLink::Edit(weld::Window
* pParent
, const Link
<SvBaseLink
&,void>& rEndEditHdl
)
444 m_pParentWin
= pParent
;
445 m_aEndEditLink
= rEndEditHdl
;
446 m_bIsConnect
= xObj
.is();
448 GetRealObject_( xObj
.is() );
451 Link
<const OUString
&, void> aLink
= LINK( this, SvBaseLink
, EndEditHdl
);
453 if( isClientType(mnObjType
) && pImplData
->ClientType
.bIntrnlLnk
)
457 SvLinkSourceRef ref
= sfx2::LinkManager::CreateObj( this );
460 ref
->Edit( pParent
, this, aLink
);
467 xObj
->Edit( pParent
, this, aLink
);
473 ExecuteEdit( OUString() );
474 bWasLastEditOK
= false;
475 m_aEndEditLink
.Call( *this );
479 bool SvBaseLink::ExecuteEdit( const OUString
& _rNewName
)
481 if( !_rNewName
.isEmpty() )
483 SetLinkSourceName( _rNewName
);
486 OUString sApp
, sTopic
, sItem
, sError
;
487 sfx2::LinkManager::GetDisplayNames( this, &sApp
, &sTopic
, &sItem
);
488 if( mnObjType
== SvBaseLinkObjectType::ClientDde
)
490 sError
= SfxResId(STR_DDE_ERROR
);
492 sal_Int32 nFndPos
= sError
.indexOf( "%1" );
495 sError
= sError
.replaceAt( nFndPos
, 2, sApp
);
496 nFndPos
= nFndPos
+ sApp
.getLength();
498 if( -1 != ( nFndPos
= sError
.indexOf( "%2", nFndPos
)))
500 sError
= sError
.replaceAt( nFndPos
, 2, sTopic
);
501 nFndPos
= nFndPos
+ sTopic
.getLength();
503 if( -1 != ( nFndPos
= sError
.indexOf( "%3", nFndPos
)))
504 sError
= sError
.replaceAt( nFndPos
, 2, sItem
);
511 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(m_pParentWin
,
512 VclMessageType::Warning
, VclButtonsType::Ok
, sError
));
516 else if( !m_bIsConnect
)
518 m_bIsConnect
= false;
522 void SvBaseLink::Closed()
525 xObj
->RemoveAllDataAdvise( this );
528 FileDialogHelper
& SvBaseLink::GetInsertFileDialog(const OUString
& rFactory
)
530 m_pFileDlg
.reset( new FileDialogHelper(
531 ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
,
532 FileDialogFlags::Insert
, rFactory
, SfxFilterFlags::NONE
, SfxFilterFlags::NONE
, m_pParentWin
) );
536 ImplDdeItem::~ImplDdeItem()
539 // So that no-one gets the idea to delete the pointer when Disconnecting!
540 tools::SvRef
<SvBaseLink
> aRef( pLink
);
544 DdeData
* ImplDdeItem::Get( SotClipboardFormatId nFormat
)
546 if( pLink
->GetObj() )
548 // is it still valid?
549 if( bIsValidData
&& nFormat
== aData
.GetFormat() )
553 OUString
sMimeType( SotExchange::GetFormatMimeType( nFormat
));
554 if( pLink
->GetObj()->GetData( aValue
, sMimeType
) )
556 if( aValue
>>= aSeq
)
558 aData
= DdeData( aSeq
.getConstArray(), aSeq
.getLength(), nFormat
);
566 bIsValidData
= false;
571 bool ImplDdeItem::Put( const DdeData
* )
573 OSL_FAIL( "ImplDdeItem::Put not implemented" );
578 void ImplDdeItem::AdviseLoop( bool bOpen
)
580 // Connection is closed, so also unsubscribe link
581 if( !pLink
->GetObj() )
586 // A connection is re-established
587 if( SvBaseLinkObjectType::DdeExternal
== pLink
->GetObjType() )
589 pLink
->GetObj()->AddDataAdvise( pLink
, u
"text/plain;charset=utf-16"_ustr
, ADVISEMODE_NODATA
);
590 pLink
->GetObj()->AddConnectAdvise( pLink
);
595 // So that no-one gets the idea to delete the pointer
596 // when Disconnecting!
597 tools::SvRef
<SvBaseLink
> aRef( pLink
);
604 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */