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>
52 using ::com::sun::star::uno::Any
;
53 using ::com::sun::star::uno::Exception
;
54 using ::com::sun::star::uno::Reference
;
55 using ::com::sun::star::uno::Sequence
;
56 using ::com::sun::star::uno::UNO_QUERY
;
57 using ::com::sun::star::uno::UNO_QUERY_THROW
;
58 using ::com::sun::star::container::XIndexAccess
;
59 using ::com::sun::star::embed::XClassifiedObject
;
60 using ::com::sun::star::drawing::XShape
;
61 using ::com::sun::star::awt::XControlModel
;
62 using ::com::sun::star::beans::XPropertySet
;
63 using ::com::sun::star::uno::Any
;
64 using ::com::sun::star::form::XFormsSupplier
;
65 using ::com::sun::star::io::XOutputStream
;
66 using ::com::sun::star::script::ScriptEventDescriptor
;
67 using ::com::sun::star::script::XEventAttacherManager
;
69 XclEscherExGlobal::XclEscherExGlobal( const XclExpRoot
& rRoot
)
73 SetBaseURI( GetMedium().GetBaseURL( true ) );
76 SvStream
* XclEscherExGlobal::ImplQueryPictureStream()
78 mxPicTempFile
.reset( new ::utl::TempFileFast
);
79 mpPicStrm
= mxPicTempFile
->GetStream( StreamMode::READWRITE
);
80 mpPicStrm
->SetEndian( SvStreamEndian::LITTLE
);
84 XclEscherEx::XclEscherEx( const XclExpRoot
& rRoot
, XclExpObjectManager
& rObjMgr
, SvStream
& rStrm
, const XclEscherEx
* pParent
) :
85 EscherEx( pParent
? pParent
->mxGlobal
: std::make_shared
<XclEscherExGlobal
>( rRoot
), &rStrm
),
88 pCurrXclObj( nullptr ),
89 pTheClientData( new XclEscherClientData
),
90 pAdditionalText( nullptr ),
93 mbIsRootDff( pParent
== nullptr )
95 InsertPersistOffset( mnNextKey
, 0 );
98 XclEscherEx::~XclEscherEx()
100 OSL_ENSURE( aStack
.empty(), "~XclEscherEx: stack not empty" );
102 pTheClientData
.reset();
105 sal_uInt32
XclEscherEx::InitNextDffFragment()
107 /* Current value of mnNextKey will be used by caller to refer to the
108 starting point of the DFF fragment. The key exists already in the
109 PersistTable (has been inserted by c'tor of previous call of
110 InitNextDffFragment(), has been updated by UpdateDffFragmentEnd(). */
111 sal_uInt32 nPersistKey
= mnNextKey
;
113 /* Prepare the next key that is used by caller as end point of the DFF
114 fragment. Will be updated by caller when writing to the DFF stream,
115 using the UpdateDffFragmentEnd() function. This is needed to find DFF
116 data written by the SVX base class implementation without interaction,
117 e.g. the solver container that will be written after the last shape. */
119 InsertPersistOffset( mnNextKey
, mpOutStrm
->Tell() );
124 void XclEscherEx::UpdateDffFragmentEnd()
126 // update existing fragment key with new stream position
127 ReplacePersistOffset( mnNextKey
, mpOutStrm
->Tell() );
130 sal_uInt32
XclEscherEx::GetDffFragmentPos( sal_uInt32 nFragmentKey
)
132 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
133 is non-const due to tools/List usage. */
134 return GetPersistOffset( nFragmentKey
);
137 sal_uInt32
XclEscherEx::GetDffFragmentSize( sal_uInt32 nFragmentKey
)
139 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
140 is non-const due to tools/List usage. */
141 return GetDffFragmentPos( nFragmentKey
+ 1 ) - GetDffFragmentPos( nFragmentKey
);
144 bool XclEscherEx::HasPendingDffData()
146 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
147 is non-const due to tools/List usage. */
148 return GetDffFragmentPos( mnNextKey
) < GetStreamPos();
151 XclExpDffAnchorBase
* XclEscherEx::CreateDffAnchor( const SdrObject
& rSdrObj
) const
153 // the object manager creates the correct anchor type according to context
154 XclExpDffAnchorBase
* pAnchor
= mrObjMgr
.CreateDffAnchor();
155 // pass the drawing object, that will calculate the anchor position
156 pAnchor
->SetSdrObject( rSdrObj
);
162 bool lcl_IsFontwork( const SdrObject
* pObj
)
164 bool bIsFontwork
= false;
165 if( pObj
->GetObjIdentifier() == SdrObjKind::CustomShape
)
167 static const OUStringLiteral aTextPath
= u
"TextPath";
168 const SdrCustomShapeGeometryItem
& rGeometryItem
=
169 pObj
->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
);
170 if( const Any
* pAny
= rGeometryItem
.GetPropertyValueByName( aTextPath
, aTextPath
) )
171 *pAny
>>= bIsFontwork
;
178 EscherExHostAppData
* XclEscherEx::StartShape( const Reference
< XShape
>& rxShape
, const tools::Rectangle
* pChildAnchor
)
180 if ( nAdditionalText
)
182 bool bInGroup
= ( pCurrXclObj
!= nullptr );
184 { // stacked recursive group object
185 if ( !pCurrAppData
->IsStackedGroup() )
186 { //! UpdateDffFragmentEnd only once
187 pCurrAppData
->SetStackedGroup( true );
188 UpdateDffFragmentEnd();
191 aStack
.push( std::make_pair( pCurrXclObj
, std::move(pCurrAppData
) ) );
192 pCurrAppData
.reset( new XclEscherHostAppData
);
193 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(rxShape
);
194 //added for exporting OCX control
195 sal_Int16 nMsCtlType
= 0;
197 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() ); // just what is it?!?
200 pCurrXclObj
= nullptr;
201 SdrObjKind nObjType
= pObj
->GetObjIdentifier();
203 if( nObjType
== SdrObjKind::OLE2
)
205 // no OLE objects in embedded drawings (chart shapes)
208 //! not-const because GetObjRef may load the OLE object
209 Reference
< XClassifiedObject
> xObj( static_cast<SdrOle2Obj
*>(pObj
)->GetObjRef() );
212 SvGlobalName
aObjClsId( xObj
->getClassID() );
213 if ( SotExchange::IsChart( aObjClsId
) )
214 { // yes, it's a chart diagram
215 mrObjMgr
.AddObj( std::make_unique
<XclExpChartObj
>( mrObjMgr
, rxShape
, pChildAnchor
, &GetDoc() ) );
216 pCurrXclObj
= nullptr; // no metafile or whatsoever
218 else // metafile and OLE object
219 pCurrXclObj
= new XclObjOle( mrObjMgr
, *pObj
);
221 else // just a metafile
222 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() );
225 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() );
227 else if( nObjType
== SdrObjKind::UNO
)
229 //added for exporting OCX control
230 Reference
< XPropertySet
> xPropSet( rxShape
, UNO_QUERY
);
234 aAny
= xPropSet
->getPropertyValue("ControlTypeinMSO");
237 catch(const Exception
&)
239 SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ControlTypeinMSO!");
241 if( nMsCtlType
== 2 ) //OCX Form Control
242 pCurrXclObj
= CreateOCXCtrlObj( rxShape
, pChildAnchor
).release();
243 else //TBX Form Control
244 pCurrXclObj
= CreateTBXCtrlObj( rxShape
, pChildAnchor
).release();
246 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
, &GetDoc() ); // just a metafile
248 else if( !ScDrawLayer::IsNoteCaption( pObj
) )
250 // ignore permanent note shapes
251 // #i12190# do not ignore callouts (do not filter by object type ID)
252 pCurrXclObj
= ShapeInteractionHelper::CreateShapeObj( mrObjMgr
, rxShape
, &GetDoc() );
253 ShapeInteractionHelper::PopulateShapeInteractionInfo( mrObjMgr
, rxShape
, *pCurrAppData
);
258 if ( !mrObjMgr
.AddObj( std::unique_ptr
<XclObj
>(pCurrXclObj
) ) )
259 { // maximum count reached, object got deleted
260 pCurrXclObj
= nullptr;
264 pCurrAppData
->SetClientData( pTheClientData
.get() );
265 if ( nAdditionalText
== 0 )
271 /* Create a dummy anchor carrying the flags. Real
272 coordinates are calculated later in virtual call of
273 WriteData(EscherEx&,const Rectangle&). */
274 XclExpDffAnchorBase
* pAnchor
= mrObjMgr
.CreateDffAnchor();
275 pAnchor
->SetFlags( *pObj
);
276 pCurrAppData
->SetClientAnchor( pAnchor
);
278 const SdrTextObj
* pTextObj
= DynCastSdrTextObj( pObj
);
279 if( pTextObj
&& !lcl_IsFontwork( pTextObj
) && (pObj
->GetObjIdentifier() != SdrObjKind::Caption
) )
281 const OutlinerParaObject
* pParaObj
= pTextObj
->GetOutlinerParaObject();
283 pCurrAppData
->SetClientTextbox(
284 new XclEscherClientTextbox( GetRoot(), *pTextObj
, pCurrXclObj
) );
290 pCurrAppData
->SetClientAnchor( mrObjMgr
.CreateDffAnchor() );
293 else if ( nAdditionalText
== 3 )
295 if ( pAdditionalText
)
297 pAdditionalText
->SetXclObj( pCurrXclObj
);
298 pCurrAppData
->SetClientTextbox( pAdditionalText
);
305 //add for exporting OCX control
306 //for OCX control import from MS office file,we need keep the id value as MS office file.
307 //GetOldRoot().pObjRecs->Add( pCurrXclObj ) statement has generated the id value as obj id rule;
308 //but we trick it here.
309 SdrObjKind nObjType
= pObj
->GetObjIdentifier();
310 if( nObjType
== SdrObjKind::UNO
&& pCurrXclObj
)
312 Reference
< XPropertySet
> xPropSet( rxShape
, UNO_QUERY
);
316 aAny
= xPropSet
->getPropertyValue("ObjIDinMSO");
318 catch(const Exception
&)
320 SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ObjIDinMSO!");
322 sal_uInt16 nObjIDinMSO
= 0xFFFF;
323 aAny
>>= nObjIDinMSO
;
324 if( nObjIDinMSO
!= 0xFFFF && nMsCtlType
== 2) //OCX
326 pCurrXclObj
->SetId(nObjIDinMSO
);
331 pCurrAppData
->SetDontWriteShape( true );
332 return pCurrAppData
.get();
335 void XclEscherEx::EndShape( sal_uInt16 nShapeType
, sal_uInt32 nShapeID
)
337 // own escher data created? -> never delete such objects
338 bool bOwnEscher
= pCurrXclObj
&& pCurrXclObj
->IsOwnEscher();
340 // post process the current object - not for objects with own escher data
341 if( pCurrXclObj
&& !bOwnEscher
)
343 // escher data of last shape not written? -> delete it from object list
346 std::unique_ptr
<XclObj
> pLastObj
= mrObjMgr
.RemoveLastObj();
347 OSL_ENSURE( pLastObj
.get() == pCurrXclObj
, "XclEscherEx::EndShape - wrong object" );
348 pCurrXclObj
= nullptr;
354 if ( pCurrAppData
->IsStackedGroup() )
355 pCurrXclObj
->SetEscherShapeTypeGroup();
358 pCurrXclObj
->SetEscherShapeType( nShapeType
);
359 UpdateDffFragmentEnd();
364 // get next object from stack
368 pCurrXclObj
= nullptr;
369 pCurrAppData
= nullptr;
373 pCurrXclObj
= aStack
.top().first
;
374 pCurrAppData
= std::move(aStack
.top().second
);
377 if( nAdditionalText
== 3 )
381 EscherExHostAppData
* XclEscherEx::EnterAdditionalTextGroup()
384 pAdditionalText
= static_cast<XclEscherClientTextbox
*>( pCurrAppData
->GetClientTextbox() );
385 pCurrAppData
->SetClientTextbox( nullptr );
386 return pCurrAppData
.get();
389 void XclEscherEx::EndDocument()
392 Flush( static_cast< XclEscherExGlobal
& >( *mxGlobal
).GetPictureStream() );
394 // seek back DFF stream to prepare saving the MSODRAWING[GROUP] records
395 mpOutStrm
->Seek( 0 );
398 std::unique_ptr
<XclExpOcxControlObj
> XclEscherEx::CreateOCXCtrlObj( Reference
< XShape
> const & xShape
, const tools::Rectangle
* pChildAnchor
)
400 ::std::unique_ptr
< XclExpOcxControlObj
> xOcxCtrl
;
402 Reference
< XControlModel
> xCtrlModel
= XclControlHelper::GetControlModel( xShape
);
403 if( xCtrlModel
.is() )
406 if( !mxCtlsStrm
.is() )
407 mxCtlsStrm
= OpenStream( EXC_STREAM_CTLS
);
408 if( mxCtlsStrm
.is() )
411 sal_uInt32 nStrmStart
= static_cast< sal_uInt32
>( mxCtlsStrm
->Tell() );
413 // writes from xCtrlModel into mxCtlsStrm, raw class name returned in aClassName
414 Reference
< XOutputStream
> xOut( new utl::OSeekableOutputStreamWrapper( *mxCtlsStrm
) );
415 Reference
< css::frame::XModel
> xModel( GetDocShell() ? GetDocShell()->GetModel() : nullptr );
416 if( xModel
.is() && xOut
.is() && oox::ole::MSConvertOCXControls::WriteOCXExcelKludgeStream( xModel
, xOut
, xCtrlModel
, xShape
->getSize(), aClassName
) )
418 sal_uInt32 nStrmSize
= static_cast< sal_uInt32
>( mxCtlsStrm
->Tell() - nStrmStart
);
419 // adjust the class name to "Forms.***.1"
420 aClassName
= "Forms." + aClassName
+ ".1";
421 xOcxCtrl
.reset( new XclExpOcxControlObj( mrObjMgr
, xShape
, pChildAnchor
, aClassName
, nStrmStart
, nStrmSize
) );
428 std::unique_ptr
<XclExpTbxControlObj
> XclEscherEx::CreateTBXCtrlObj( Reference
< XShape
> const & xShape
, const tools::Rectangle
* pChildAnchor
)
430 ::std::unique_ptr
< XclExpTbxControlObj
> xTbxCtrl( new XclExpTbxControlObj( mrObjMgr
, xShape
, pChildAnchor
) );
431 if( xTbxCtrl
->GetObjType() == EXC_OBJTYPE_UNKNOWN
)
435 // find attached macro
436 Reference
< XControlModel
> xCtrlModel
= XclControlHelper::GetControlModel( xShape
);
437 ConvertTbxMacro( *xTbxCtrl
, xCtrlModel
);
442 void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj
& rTbxCtrlObj
, Reference
< XControlModel
> const & xCtrlModel
)
444 SdrPage
* pSdrPage
= GetSdrPage( GetCurrScTab() );
445 if( !(xCtrlModel
.is() && GetDocShell() && pSdrPage
) )
450 Reference
< XFormsSupplier
> xFormsSupplier( pSdrPage
->getUnoPage(), UNO_QUERY_THROW
);
451 Reference
< XIndexAccess
> xFormsIA( xFormsSupplier
->getForms(), UNO_QUERY_THROW
);
453 // 1) try to find the index of the processed control in the form
455 Reference
< XIndexAccess
> xFormIA
; // needed in step 2) below
456 sal_Int32 nFoundIdx
= -1;
458 // search all existing forms in the draw page
459 for( sal_Int32 nFormIdx
= 0, nFormCount
= xFormsIA
->getCount();
460 (nFoundIdx
< 0) && (nFormIdx
< nFormCount
); ++nFormIdx
)
462 // get the XIndexAccess interface of the form with index nFormIdx
463 if( xFormIA
.set( xFormsIA
->getByIndex( nFormIdx
), UNO_QUERY
) )
465 // search all elements (controls) of the current form by index
466 for( sal_Int32 nCtrlIdx
= 0, nCtrlCount
= xFormIA
->getCount();
467 (nFoundIdx
< 0) && (nCtrlIdx
< nCtrlCount
); ++nCtrlIdx
)
469 // compare implementation pointers of the control models
470 Reference
< XControlModel
> xCurrModel( xFormIA
->getByIndex( nCtrlIdx
), UNO_QUERY
);
471 if( xCtrlModel
.get() == xCurrModel
.get() )
472 nFoundIdx
= nCtrlIdx
;
477 // 2) try to find an attached macro
479 if( xFormIA
.is() && (nFoundIdx
>= 0) )
481 Reference
< XEventAttacherManager
> xEventMgr( xFormIA
, UNO_QUERY_THROW
);
482 // loop over all events attached to the found control
483 const Sequence
< ScriptEventDescriptor
> aEventSeq( xEventMgr
->getScriptEvents( nFoundIdx
) );
484 for( const auto& rEvent
: aEventSeq
)
486 // try to set the event data at the Excel control object, returns true on success
487 if (rTbxCtrlObj
.SetMacroLink( rEvent
))
497 void XclEscherEx::DeleteCurrAppData()
501 delete pCurrAppData
->GetClientAnchor();
502 delete pCurrAppData
->GetClientTextbox();
503 delete pCurrAppData
->GetInteractionInfo();
504 pCurrAppData
.reset();
508 // --- class XclEscherClientData -------------------------------------
510 void XclEscherClientData::WriteData( EscherEx
& rEx
) const
511 { // actual data is in the following OBJ record
512 rEx
.AddAtom( 0, ESCHER_ClientData
);
515 // --- class XclEscherClientTextbox -------------------------------------
517 XclEscherClientTextbox::XclEscherClientTextbox( const XclExpRoot
& rRoot
,
518 const SdrTextObj
& rObj
, XclObj
* pObj
)
526 void XclEscherClientTextbox::WriteData( EscherEx
& /*rEx*/ ) const
528 pXclObj
->SetText( GetRoot(), rTextObj
);
532 ShapeInteractionHelper::CreateShapeObj( XclExpObjectManager
& rObjMgr
, const Reference
< XShape
>& xShape
, ScDocument
* pDoc
)
534 return new XclExpShapeObj( rObjMgr
, xShape
, pDoc
);
537 void ShapeInteractionHelper::PopulateShapeInteractionInfo(const XclExpObjectManager
& rObjMgr
,
538 const Reference
<XShape
>& xShape
,
539 EscherExHostAppData
& rHostAppData
)
543 SvMemoryStream
* pMemStrm
= nullptr;
546 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(xShape
);
548 sHyperLink
= pObj
->getHyperlink();
549 if (ScMacroInfo
* pInfo
= ScDrawLayer::GetMacroInfo(pObj
))
551 sMacro
= pInfo
->GetMacro();
553 if (!sHyperLink
.isEmpty())
555 pMemStrm
= new SvMemoryStream();
556 XclExpStream
tmpStream(*pMemStrm
, rObjMgr
.GetRoot());
557 ScAddress dummyAddress
;
558 SvxURLField aUrlField
;
559 aUrlField
.SetURL(sHyperLink
);
560 XclExpHyperlink
hExpHlink(rObjMgr
.GetRoot(), aUrlField
, dummyAddress
);
561 hExpHlink
.WriteEmbeddedData(tmpStream
);
563 if (!sHyperLink
.isEmpty() || !sMacro
.isEmpty())
564 rHostAppData
.SetInteractionInfo(new InteractionInfo(pMemStrm
));
571 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */