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 .
21 #include <com/sun/star/awt/XControlModel.hpp>
22 #include <com/sun/star/embed/XClassifiedObject.hpp>
23 #include <com/sun/star/embed/XEmbeddedObject.hpp>
24 #include <com/sun/star/form/XFormsSupplier.hpp>
25 #include <com/sun/star/script/XEventAttacherManager.hpp>
26 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <svx/svdpage.hxx>
29 #include <svx/svdotext.hxx>
30 #include <svx/svdobj.hxx>
31 #include <svx/svdoole2.hxx>
32 #include <unotools/tempfile.hxx>
33 #include <unotools/ucbstreamhelper.hxx>
34 #include <svx/sdasitm.hxx>
35 #include <sfx2/docfile.hxx>
36 #include <sal/log.hxx>
38 #include <sot/exchange.hxx>
39 #include <sot/storage.hxx>
40 #include <xeescher.hxx>
42 #include <drwlayer.hxx>
43 #include <xecontent.hxx>
44 #include <editeng/flditem.hxx>
45 #include <userdat.hxx>
46 #include <xcl97rec.hxx>
47 #include <xcl97esc.hxx>
48 #include <unotools/streamwrap.hxx>
49 #include <oox/ole/olehelper.hxx>
50 #include <sfx2/objsh.hxx>
53 using ::com::sun::star::uno::Any
;
54 using ::com::sun::star::uno::Exception
;
55 using ::com::sun::star::uno::Reference
;
56 using ::com::sun::star::uno::Sequence
;
57 using ::com::sun::star::uno::UNO_QUERY
;
58 using ::com::sun::star::uno::UNO_QUERY_THROW
;
59 using ::com::sun::star::container::XIndexAccess
;
60 using ::com::sun::star::embed::XClassifiedObject
;
61 using ::com::sun::star::drawing::XShape
;
62 using ::com::sun::star::awt::XControlModel
;
63 using ::com::sun::star::beans::XPropertySet
;
64 using ::com::sun::star::uno::Any
;
65 using ::com::sun::star::form::XFormsSupplier
;
66 using ::com::sun::star::io::XOutputStream
;
67 using ::com::sun::star::script::ScriptEventDescriptor
;
68 using ::com::sun::star::script::XEventAttacherManager
;
70 XclEscherExGlobal::XclEscherExGlobal( const XclExpRoot
& rRoot
)
74 SetBaseURI( GetMedium().GetBaseURL( true ) );
77 SvStream
* XclEscherExGlobal::ImplQueryPictureStream()
79 moPicTempFile
.emplace();
80 mpPicStrm
= moPicTempFile
->GetStream( StreamMode::READWRITE
);
81 mpPicStrm
->SetEndian( SvStreamEndian::LITTLE
);
85 XclEscherEx::XclEscherEx( const XclExpRoot
& rRoot
, XclExpObjectManager
& rObjMgr
, SvStream
& rStrm
, const XclEscherEx
* pParent
) :
86 EscherEx( pParent
? pParent
->mxGlobal
: std::make_shared
<XclEscherExGlobal
>( rRoot
), &rStrm
),
89 pCurrXclObj( nullptr ),
90 pTheClientData( new XclEscherClientData
),
91 pAdditionalText( nullptr ),
94 mbIsRootDff( pParent
== nullptr )
96 InsertPersistOffset( mnNextKey
, 0 );
99 XclEscherEx::~XclEscherEx()
101 OSL_ENSURE( aStack
.empty(), "~XclEscherEx: stack not empty" );
103 pTheClientData
.reset();
106 sal_uInt32
XclEscherEx::InitNextDffFragment()
108 /* Current value of mnNextKey will be used by caller to refer to the
109 starting point of the DFF fragment. The key exists already in the
110 PersistTable (has been inserted by c'tor of previous call of
111 InitNextDffFragment(), has been updated by UpdateDffFragmentEnd(). */
112 sal_uInt32 nPersistKey
= mnNextKey
;
114 /* Prepare the next key that is used by caller as end point of the DFF
115 fragment. Will be updated by caller when writing to the DFF stream,
116 using the UpdateDffFragmentEnd() function. This is needed to find DFF
117 data written by the SVX base class implementation without interaction,
118 e.g. the solver container that will be written after the last shape. */
120 InsertPersistOffset( mnNextKey
, mpOutStrm
->Tell() );
125 void XclEscherEx::UpdateDffFragmentEnd()
127 // update existing fragment key with new stream position
128 ReplacePersistOffset( mnNextKey
, mpOutStrm
->Tell() );
131 sal_uInt32
XclEscherEx::GetDffFragmentPos( sal_uInt32 nFragmentKey
)
133 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
134 is non-const due to tools/List usage. */
135 return GetPersistOffset( nFragmentKey
);
138 sal_uInt32
XclEscherEx::GetDffFragmentSize( sal_uInt32 nFragmentKey
)
140 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
141 is non-const due to tools/List usage. */
142 return GetDffFragmentPos( nFragmentKey
+ 1 ) - GetDffFragmentPos( nFragmentKey
);
145 bool XclEscherEx::HasPendingDffData()
147 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
148 is non-const due to tools/List usage. */
149 return GetDffFragmentPos( mnNextKey
) < GetStreamPos();
152 XclExpDffAnchorBase
* XclEscherEx::CreateDffAnchor( const SdrObject
& rSdrObj
) const
154 // the object manager creates the correct anchor type according to context
155 XclExpDffAnchorBase
* pAnchor
= mrObjMgr
.CreateDffAnchor();
156 // pass the drawing object, that will calculate the anchor position
157 pAnchor
->SetSdrObject( rSdrObj
);
163 bool lcl_IsFontwork( const SdrObject
* pObj
)
165 bool bIsFontwork
= false;
166 if( pObj
->GetObjIdentifier() == SdrObjKind::CustomShape
)
168 static constexpr OUString aTextPath
= u
"TextPath"_ustr
;
169 const SdrCustomShapeGeometryItem
& rGeometryItem
=
170 pObj
->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
);
171 if( const Any
* pAny
= rGeometryItem
.GetPropertyValueByName( aTextPath
, aTextPath
) )
172 *pAny
>>= bIsFontwork
;
179 EscherExHostAppData
* XclEscherEx::StartShape( const Reference
< XShape
>& rxShape
, const tools::Rectangle
* pChildAnchor
)
181 if ( nAdditionalText
)
183 bool bInGroup
= ( pCurrXclObj
!= nullptr );
185 { // stacked recursive group object
186 if ( !pCurrAppData
->IsStackedGroup() )
187 { //! UpdateDffFragmentEnd only once
188 pCurrAppData
->SetStackedGroup( true );
189 UpdateDffFragmentEnd();
192 aStack
.push( std::make_pair( pCurrXclObj
, std::move(pCurrAppData
) ) );
193 pCurrAppData
.reset( new XclEscherHostAppData
);
194 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(rxShape
);
195 //added for exporting OCX control
196 sal_Int16 nMsCtlType
= 0;
198 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() ); // just what is it?!?
201 pCurrXclObj
= nullptr;
202 SdrObjKind nObjType
= pObj
->GetObjIdentifier();
204 if( nObjType
== SdrObjKind::OLE2
)
206 // no OLE objects in embedded drawings (chart shapes)
209 //! not-const because GetObjRef may load the OLE object
210 Reference
< XClassifiedObject
> xObj( static_cast<SdrOle2Obj
*>(pObj
)->GetObjRef() );
213 SvGlobalName
aObjClsId( xObj
->getClassID() );
214 if ( SotExchange::IsChart( aObjClsId
) )
215 { // yes, it's a chart diagram
216 mrObjMgr
.AddObj( std::make_unique
<XclExpChartObj
>( mrObjMgr
, rxShape
, pChildAnchor
, &GetDoc() ) );
217 pCurrXclObj
= nullptr; // no metafile or whatsoever
219 else // metafile and OLE object
220 pCurrXclObj
= new XclObjOle( mrObjMgr
, *static_cast<SdrOle2Obj
*>(pObj
) );
222 else // just a metafile
223 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() );
226 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() );
228 else if( nObjType
== SdrObjKind::UNO
)
230 //added for exporting OCX control
231 Reference
< XPropertySet
> xPropSet( rxShape
, UNO_QUERY
);
235 aAny
= xPropSet
->getPropertyValue(u
"ControlTypeinMSO"_ustr
);
238 catch(const Exception
&)
240 SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ControlTypeinMSO!");
242 if( nMsCtlType
== 2 ) //OCX Form Control
243 pCurrXclObj
= CreateOCXCtrlObj( rxShape
, pChildAnchor
).release();
244 else //TBX Form Control
245 pCurrXclObj
= CreateTBXCtrlObj( rxShape
, pChildAnchor
).release();
247 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() ); // just a metafile
249 else if( !ScDrawLayer::IsNoteCaption( pObj
) )
251 // ignore permanent note shapes
252 // #i12190# do not ignore callouts (do not filter by object type ID)
253 pCurrXclObj
= ShapeInteractionHelper::CreateShapeObj( mrObjMgr
, rxShape
, &GetDoc() );
254 ShapeInteractionHelper::PopulateShapeInteractionInfo( mrObjMgr
, rxShape
, *pCurrAppData
);
259 if ( !mrObjMgr
.AddObj( std::unique_ptr
<XclObj
>(pCurrXclObj
) ) )
260 { // maximum count reached, object got deleted
261 pCurrXclObj
= nullptr;
265 pCurrAppData
->SetClientData( pTheClientData
.get() );
266 if ( nAdditionalText
== 0 )
272 /* Create a dummy anchor carrying the flags. Real
273 coordinates are calculated later in virtual call of
274 WriteData(EscherEx&,const Rectangle&). */
275 XclExpDffAnchorBase
* pAnchor
= mrObjMgr
.CreateDffAnchor();
276 pAnchor
->SetFlags( *pObj
);
277 pCurrAppData
->SetClientAnchor( pAnchor
);
279 const SdrTextObj
* pTextObj
= DynCastSdrTextObj( pObj
);
280 if( pTextObj
&& !lcl_IsFontwork( pTextObj
) && (pObj
->GetObjIdentifier() != SdrObjKind::Caption
) )
282 const OutlinerParaObject
* pParaObj
= pTextObj
->GetOutlinerParaObject();
284 pCurrAppData
->SetClientTextbox(
285 new XclEscherClientTextbox( GetRoot(), *pTextObj
, pCurrXclObj
) );
291 pCurrAppData
->SetClientAnchor( mrObjMgr
.CreateDffAnchor() );
294 else if ( nAdditionalText
== 3 )
296 if ( pAdditionalText
)
298 pAdditionalText
->SetXclObj( pCurrXclObj
);
299 pCurrAppData
->SetClientTextbox( pAdditionalText
);
306 //add for exporting OCX control
307 //for OCX control import from MS office file,we need keep the id value as MS office file.
308 //GetOldRoot().pObjRecs->Add( pCurrXclObj ) statement has generated the id value as obj id rule;
309 //but we trick it here.
310 SdrObjKind nObjType
= pObj
->GetObjIdentifier();
311 if( nObjType
== SdrObjKind::UNO
&& pCurrXclObj
)
313 Reference
< XPropertySet
> xPropSet( rxShape
, UNO_QUERY
);
317 aAny
= xPropSet
->getPropertyValue(u
"ObjIDinMSO"_ustr
);
319 catch(const Exception
&)
321 SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ObjIDinMSO!");
323 sal_uInt16 nObjIDinMSO
= 0xFFFF;
324 aAny
>>= nObjIDinMSO
;
325 if( nObjIDinMSO
!= 0xFFFF && nMsCtlType
== 2) //OCX
327 pCurrXclObj
->SetId(nObjIDinMSO
);
332 pCurrAppData
->SetDontWriteShape( true );
333 return pCurrAppData
.get();
336 void XclEscherEx::EndShape( sal_uInt16 nShapeType
, sal_uInt32 nShapeID
)
338 // own escher data created? -> never delete such objects
339 bool bOwnEscher
= pCurrXclObj
&& pCurrXclObj
->IsOwnEscher();
341 // post process the current object - not for objects with own escher data
342 if( pCurrXclObj
&& !bOwnEscher
)
344 // escher data of last shape not written? -> delete it from object list
347 std::unique_ptr
<XclObj
> pLastObj
= mrObjMgr
.RemoveLastObj();
348 OSL_ENSURE( pLastObj
.get() == pCurrXclObj
, "XclEscherEx::EndShape - wrong object" );
349 pCurrXclObj
= nullptr;
355 if ( pCurrAppData
->IsStackedGroup() )
356 pCurrXclObj
->SetEscherShapeTypeGroup();
359 pCurrXclObj
->SetEscherShapeType( nShapeType
);
360 UpdateDffFragmentEnd();
365 // get next object from stack
369 pCurrXclObj
= nullptr;
370 pCurrAppData
= nullptr;
374 pCurrXclObj
= aStack
.top().first
;
375 pCurrAppData
= std::move(aStack
.top().second
);
378 if( nAdditionalText
== 3 )
382 EscherExHostAppData
* XclEscherEx::EnterAdditionalTextGroup()
385 pAdditionalText
= static_cast<XclEscherClientTextbox
*>( pCurrAppData
->GetClientTextbox() );
386 pCurrAppData
->SetClientTextbox( nullptr );
387 return pCurrAppData
.get();
390 void XclEscherEx::EndDocument()
393 Flush( static_cast< XclEscherExGlobal
& >( *mxGlobal
).GetPictureStream() );
395 // seek back DFF stream to prepare saving the MSODRAWING[GROUP] records
396 mpOutStrm
->Seek( 0 );
399 std::unique_ptr
<XclExpOcxControlObj
> XclEscherEx::CreateOCXCtrlObj( Reference
< XShape
> const & xShape
, const tools::Rectangle
* pChildAnchor
)
401 ::std::unique_ptr
< XclExpOcxControlObj
> xOcxCtrl
;
403 Reference
< XControlModel
> xCtrlModel
= XclControlHelper::GetControlModel( xShape
);
404 if( xCtrlModel
.is() )
407 if( !mxCtlsStrm
.is() )
408 mxCtlsStrm
= OpenStream( EXC_STREAM_CTLS
);
409 if( mxCtlsStrm
.is() )
412 sal_uInt32 nStrmStart
= static_cast< sal_uInt32
>( mxCtlsStrm
->Tell() );
414 // writes from xCtrlModel into mxCtlsStrm, raw class name returned in aClassName
415 Reference
< XOutputStream
> xOut( new utl::OSeekableOutputStreamWrapper( *mxCtlsStrm
) );
416 Reference
< css::frame::XModel
> xModel( GetDocShell() ? GetDocShell()->GetModel() : nullptr );
417 if( xModel
.is() && xOut
.is() && oox::ole::MSConvertOCXControls::WriteOCXExcelKludgeStream( xModel
, xOut
, xCtrlModel
, xShape
->getSize(), aClassName
) )
419 sal_uInt32 nStrmSize
= static_cast< sal_uInt32
>( mxCtlsStrm
->Tell() - nStrmStart
);
420 // adjust the class name to "Forms.***.1"
421 aClassName
= "Forms." + aClassName
+ ".1";
422 xOcxCtrl
.reset( new XclExpOcxControlObj( mrObjMgr
, xShape
, pChildAnchor
, aClassName
, nStrmStart
, nStrmSize
) );
429 std::unique_ptr
<XclExpTbxControlObj
> XclEscherEx::CreateTBXCtrlObj( Reference
< XShape
> const & xShape
, const tools::Rectangle
* pChildAnchor
)
431 ::std::unique_ptr
< XclExpTbxControlObj
> xTbxCtrl( new XclExpTbxControlObj( mrObjMgr
, xShape
, pChildAnchor
) );
432 if( xTbxCtrl
->GetObjType() == EXC_OBJTYPE_UNKNOWN
)
436 // find attached macro
437 Reference
< XControlModel
> xCtrlModel
= XclControlHelper::GetControlModel( xShape
);
438 ConvertTbxMacro( *xTbxCtrl
, xCtrlModel
);
443 void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj
& rTbxCtrlObj
, Reference
< XControlModel
> const & xCtrlModel
)
445 SdrPage
* pSdrPage
= GetSdrPage( GetCurrScTab() );
446 if( !(xCtrlModel
.is() && GetDocShell() && pSdrPage
) )
451 Reference
< XFormsSupplier
> xFormsSupplier( pSdrPage
->getUnoPage(), UNO_QUERY_THROW
);
452 Reference
< XIndexAccess
> xFormsIA( xFormsSupplier
->getForms(), UNO_QUERY_THROW
);
454 // 1) try to find the index of the processed control in the form
456 Reference
< XIndexAccess
> xFormIA
; // needed in step 2) below
457 sal_Int32 nFoundIdx
= -1;
459 // search all existing forms in the draw page
460 for( sal_Int32 nFormIdx
= 0, nFormCount
= xFormsIA
->getCount();
461 (nFoundIdx
< 0) && (nFormIdx
< nFormCount
); ++nFormIdx
)
463 // get the XIndexAccess interface of the form with index nFormIdx
464 if( xFormIA
.set( xFormsIA
->getByIndex( nFormIdx
), UNO_QUERY
) )
466 // search all elements (controls) of the current form by index
467 for( sal_Int32 nCtrlIdx
= 0, nCtrlCount
= xFormIA
->getCount();
468 (nFoundIdx
< 0) && (nCtrlIdx
< nCtrlCount
); ++nCtrlIdx
)
470 // compare implementation pointers of the control models
471 Reference
< XControlModel
> xCurrModel( xFormIA
->getByIndex( nCtrlIdx
), UNO_QUERY
);
472 if( xCtrlModel
.get() == xCurrModel
.get() )
473 nFoundIdx
= nCtrlIdx
;
478 // 2) try to find an attached macro
480 if( xFormIA
.is() && (nFoundIdx
>= 0) )
482 Reference
< XEventAttacherManager
> xEventMgr( xFormIA
, UNO_QUERY_THROW
);
483 // loop over all events attached to the found control
484 const Sequence
< ScriptEventDescriptor
> aEventSeq( xEventMgr
->getScriptEvents( nFoundIdx
) );
485 for( const auto& rEvent
: aEventSeq
)
487 // try to set the event data at the Excel control object, returns true on success
488 if (rTbxCtrlObj
.SetMacroLink( rEvent
))
498 void XclEscherEx::DeleteCurrAppData()
502 delete pCurrAppData
->GetClientAnchor();
503 delete pCurrAppData
->GetClientTextbox();
504 delete pCurrAppData
->GetInteractionInfo();
505 pCurrAppData
.reset();
509 // --- class XclEscherClientData -------------------------------------
511 void XclEscherClientData::WriteData( EscherEx
& rEx
) const
512 { // actual data is in the following OBJ record
513 rEx
.AddAtom( 0, ESCHER_ClientData
);
516 // --- class XclEscherClientTextbox -------------------------------------
518 XclEscherClientTextbox::XclEscherClientTextbox( const XclExpRoot
& rRoot
,
519 const SdrTextObj
& rObj
, XclObj
* pObj
)
527 void XclEscherClientTextbox::WriteData( EscherEx
& /*rEx*/ ) const
529 pXclObj
->SetText( GetRoot(), rTextObj
);
533 ShapeInteractionHelper::CreateShapeObj( XclExpObjectManager
& rObjMgr
, const Reference
< XShape
>& xShape
, ScDocument
* pDoc
)
535 return new XclExpShapeObj( rObjMgr
, xShape
, pDoc
);
538 void ShapeInteractionHelper::PopulateShapeInteractionInfo(const XclExpObjectManager
& rObjMgr
,
539 const Reference
<XShape
>& xShape
,
540 EscherExHostAppData
& rHostAppData
)
544 SvMemoryStream
* pMemStrm
= nullptr;
547 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(xShape
);
549 sHyperLink
= pObj
->getHyperlink();
550 if (ScMacroInfo
* pInfo
= ScDrawLayer::GetMacroInfo(pObj
))
552 sMacro
= pInfo
->GetMacro();
554 if (!sHyperLink
.isEmpty())
556 pMemStrm
= new SvMemoryStream();
557 XclExpStream
tmpStream(*pMemStrm
, rObjMgr
.GetRoot());
558 ScAddress dummyAddress
;
559 SvxURLField aUrlField
;
560 aUrlField
.SetURL(sHyperLink
);
561 XclExpHyperlink
hExpHlink(rObjMgr
.GetRoot(), aUrlField
, dummyAddress
);
562 hExpHlink
.WriteEmbeddedData(tmpStream
);
564 if (!sHyperLink
.isEmpty() || !sMacro
.isEmpty())
565 rHostAppData
.SetInteractionInfo(new InteractionInfo(pMemStrm
));
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */