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 .
20 #include <com/sun/star/awt/XControlModel.hpp>
21 #include <com/sun/star/embed/XClassifiedObject.hpp>
22 #include <com/sun/star/form/XFormsSupplier.hpp>
23 #include <com/sun/star/script/ScriptEventDescriptor.hpp>
24 #include <com/sun/star/script/XEventAttacherManager.hpp>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/form/XForm.hpp>
28 #include <svx/svdpage.hxx>
29 #include <editeng/outlobj.hxx>
30 #include <svx/svdotext.hxx>
31 #include <svx/svdobj.hxx>
32 #include <svx/svdoole2.hxx>
33 #include <svx/unoapi.hxx>
34 #include <svx/fmglob.hxx>
35 #include <vcl/outdev.hxx>
36 #include <unotools/tempfile.hxx>
37 #include <unotools/ucbstreamhelper.hxx>
38 #include <svx/sdasitm.hxx>
39 #include <sfx2/docfile.hxx>
41 #include <sot/exchange.hxx>
42 #include "xeescher.hxx"
45 #include "document.hxx"
46 #include "drwlayer.hxx"
47 #include "xecontent.hxx"
48 #include <editeng/flditem.hxx>
49 #include "userdat.hxx"
50 #include "xcl97rec.hxx"
51 #include "xehelper.hxx"
52 #include "xechart.hxx"
53 #include "xcl97esc.hxx"
54 #include <unotools/streamwrap.hxx>
55 #include <oox/ole/olehelper.hxx>
56 #include <sfx2/objsh.hxx>
58 using ::com::sun::star::uno::Any
;
59 using ::com::sun::star::uno::Exception
;
60 using ::com::sun::star::uno::Reference
;
61 using ::com::sun::star::uno::Sequence
;
62 using ::com::sun::star::uno::UNO_QUERY
;
63 using ::com::sun::star::uno::UNO_QUERY_THROW
;
64 using ::com::sun::star::container::XIndexAccess
;
65 using ::com::sun::star::embed::XClassifiedObject
;
66 using ::com::sun::star::drawing::XShape
;
67 using ::com::sun::star::awt::XControlModel
;
68 using ::com::sun::star::beans::XPropertySet
;
69 using ::com::sun::star::uno::Any
;
70 using ::com::sun::star::form::XForm
;
71 using ::com::sun::star::form::XFormComponent
;
72 using ::com::sun::star::form::XFormsSupplier
;
73 using ::com::sun::star::io::XOutputStream
;
74 using ::com::sun::star::script::ScriptEventDescriptor
;
75 using ::com::sun::star::script::XEventAttacherManager
;
77 XclEscherExGlobal::XclEscherExGlobal( const XclExpRoot
& rRoot
) :
80 SetBaseURI( GetMedium().GetBaseURL( true ) );
83 SvStream
* XclEscherExGlobal::ImplQueryPictureStream()
85 mxPicTempFile
.reset( new ::utl::TempFile
);
86 if( mxPicTempFile
->IsValid() )
88 mxPicTempFile
->EnableKillingFile();
89 mxPicStrm
.reset( ::utl::UcbStreamHelper::CreateStream( mxPicTempFile
->GetURL(), STREAM_STD_READWRITE
) );
90 mxPicStrm
->SetEndian( SvStreamEndian::LITTLE
);
92 return mxPicStrm
.get();
95 XclEscherEx::XclEscherEx( const XclExpRoot
& rRoot
, XclExpObjectManager
& rObjMgr
, SvStream
& rStrm
, const XclEscherEx
* pParent
) :
96 EscherEx( pParent
? pParent
->mxGlobal
: EscherExGlobalRef( new XclEscherExGlobal( rRoot
) ), &rStrm
),
100 pCurrAppData( NULL
),
101 pTheClientData( new XclEscherClientData
),
102 pAdditionalText( NULL
),
103 nAdditionalText( 0 ),
105 mbIsRootDff( pParent
== 0 )
107 InsertPersistOffset( mnNextKey
, 0 );
110 XclEscherEx::~XclEscherEx()
112 OSL_ENSURE( !aStack
.empty(), "~XclEscherEx: stack not empty" );
114 delete pTheClientData
;
117 sal_uInt32
XclEscherEx::InitNextDffFragment()
119 /* Current value of mnNextKey will be used by caller to refer to the
120 starting point of the DFF fragment. The key exists already in the
121 PersistTable (has been inserted by c'tor of previous call of
122 InitNextDffFragment(), has been updated by UpdateDffFragmentEnd(). */
123 sal_uInt32 nPersistKey
= mnNextKey
;
125 /* Prepare the next key that is used by caller as end point of the DFF
126 fragment. Will be updated by caller when writing to the DFF stream,
127 using the UpdateDffFragmentEnd() function. This is needed to find DFF
128 data written by the SVX base class implementation without interaction,
129 e.g. the solver container that will be written after the last shape. */
131 InsertPersistOffset( mnNextKey
, mpOutStrm
->Tell() );
136 void XclEscherEx::UpdateDffFragmentEnd()
138 // update existing fragment key with new stream position
139 ReplacePersistOffset( mnNextKey
, mpOutStrm
->Tell() );
142 sal_uInt32
XclEscherEx::GetDffFragmentPos( sal_uInt32 nFragmentKey
)
144 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
145 is non-const due to tools/List usage. */
146 return GetPersistOffset( nFragmentKey
);
149 sal_uInt32
XclEscherEx::GetDffFragmentSize( sal_uInt32 nFragmentKey
)
151 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
152 is non-const due to tools/List usage. */
153 return GetDffFragmentPos( nFragmentKey
+ 1 ) - GetDffFragmentPos( nFragmentKey
);
156 bool XclEscherEx::HasPendingDffData()
158 /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
159 is non-const due to tools/List usage. */
160 return GetDffFragmentPos( mnNextKey
) < GetStreamPos();
163 XclExpDffAnchorBase
* XclEscherEx::CreateDffAnchor( const SdrObject
& rSdrObj
) const
165 // the object manager creates the correct anchor type according to context
166 XclExpDffAnchorBase
* pAnchor
= mrObjMgr
.CreateDffAnchor();
167 // pass the drawing object, that will calculate the anchor position
168 pAnchor
->SetSdrObject( rSdrObj
);
174 bool lcl_IsFontwork( const SdrObject
* pObj
)
176 bool bIsFontwork
= false;
177 if( pObj
->GetObjIdentifier() == OBJ_CUSTOMSHAPE
)
179 const OUString aTextPath
= "TextPath";
180 const SdrCustomShapeGeometryItem
& rGeometryItem
= static_cast<const SdrCustomShapeGeometryItem
&>(
181 pObj
->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
) );
182 if( const Any
* pAny
= rGeometryItem
.GetPropertyValueByName( aTextPath
, aTextPath
) )
183 *pAny
>>= bIsFontwork
;
190 EscherExHostAppData
* XclEscherEx::StartShape( const Reference
< XShape
>& rxShape
, const Rectangle
* pChildAnchor
)
192 if ( nAdditionalText
)
194 bool bInGroup
= ( pCurrXclObj
!= NULL
);
196 { // stacked recursive group object
197 if ( !pCurrAppData
->IsStackedGroup() )
198 { //! UpdateDffFragmentEnd only once
199 pCurrAppData
->SetStackedGroup( true );
200 UpdateDffFragmentEnd();
203 aStack
.push( std::make_pair( pCurrXclObj
, pCurrAppData
) );
204 pCurrAppData
= new XclEscherHostAppData
;
205 SdrObject
* pObj
= GetSdrObjectFromXShape( rxShape
);
206 //added for exporting OCX control
207 sal_Int16 nMsCtlType
= 0;
209 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
); // just what is it?!?
213 sal_uInt16 nObjType
= pObj
->GetObjIdentifier();
215 if( nObjType
== OBJ_OLE2
)
217 // no OLE objects in embedded drawings (chart shapes)
220 //! not-const because GetObjRef may load the OLE object
221 Reference
< XClassifiedObject
> xObj( static_cast<SdrOle2Obj
*>(pObj
)->GetObjRef(), UNO_QUERY
);
224 SvGlobalName
aObjClsId( xObj
->getClassID() );
225 if ( SotExchange::IsChart( aObjClsId
) )
226 { // yes, it's a chart diagram
227 mrObjMgr
.AddObj( new XclExpChartObj( mrObjMgr
, rxShape
, pChildAnchor
) );
228 pCurrXclObj
= NULL
; // no metafile or whatsoever
230 else // metafile and OLE object
231 pCurrXclObj
= new XclObjOle( mrObjMgr
, *pObj
);
233 else // just a metafile
234 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
);
237 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
);
239 else if( nObjType
== OBJ_UNO
)
241 //added for exporting OCX control
242 Reference
< XPropertySet
> xPropSet( rxShape
, UNO_QUERY
);
246 aAny
= xPropSet
->getPropertyValue("ControlTypeinMSO");
249 catch(const Exception
&)
251 OSL_TRACE("XclEscherEx::StartShape, this control can't get the property ControlTypeinMSO!");
253 if( nMsCtlType
== 2 ) //OCX Form Control
254 pCurrXclObj
= CreateOCXCtrlObj( rxShape
, pChildAnchor
);
255 else //TBX Form Control
256 pCurrXclObj
= CreateTBXCtrlObj( rxShape
, pChildAnchor
);
258 pCurrXclObj
= new XclObjAny( mrObjMgr
, rxShape
); // just a metafile
260 else if( !ScDrawLayer::IsNoteCaption( pObj
) )
262 // ignore permanent note shapes
263 // #i12190# do not ignore callouts (do not filter by object type ID)
264 pCurrXclObj
= ShapeInteractionHelper::CreateShapeObj( mrObjMgr
, rxShape
);
265 ShapeInteractionHelper::PopulateShapeInteractionInfo( mrObjMgr
, rxShape
, *pCurrAppData
);
270 if ( !mrObjMgr
.AddObj( pCurrXclObj
) )
271 { // maximum count reached, object got deleted
276 pCurrAppData
->SetClientData( pTheClientData
);
277 if ( nAdditionalText
== 0 )
283 /* Create a dummy anchor carrying the flags. Real
284 coordinates are calculated later in virtual call of
285 WriteData(EscherEx&,const Rectangle&). */
286 XclExpDffAnchorBase
* pAnchor
= mrObjMgr
.CreateDffAnchor();
287 pAnchor
->SetFlags( *pObj
);
288 pCurrAppData
->SetClientAnchor( pAnchor
);
290 const SdrTextObj
* pTextObj
= PTR_CAST( SdrTextObj
, pObj
);
291 if( pTextObj
&& !lcl_IsFontwork( pTextObj
) && (pObj
->GetObjIdentifier() != OBJ_CAPTION
) )
293 const OutlinerParaObject
* pParaObj
= pTextObj
->GetOutlinerParaObject();
295 pCurrAppData
->SetClientTextbox(
296 new XclEscherClientTextbox( GetRoot(), *pTextObj
, pCurrXclObj
) );
302 pCurrAppData
->SetClientAnchor( mrObjMgr
.CreateDffAnchor() );
305 else if ( nAdditionalText
== 3 )
307 if ( pAdditionalText
)
309 pAdditionalText
->SetXclObj( pCurrXclObj
);
310 pCurrAppData
->SetClientTextbox( pAdditionalText
);
317 //add for exporting OCX control
318 //for OCX control import from MS office file,we need keep the id value as MS office file.
319 //GetOldRoot().pObjRecs->Add( pCurrXclObj ) statement has generated the id value as obj id rule;
320 //but we trick it here.
321 sal_uInt16 nObjType
= pObj
->GetObjIdentifier();
322 if( nObjType
== OBJ_UNO
&& pCurrXclObj
)
324 Reference
< XPropertySet
> xPropSet( rxShape
, UNO_QUERY
);
328 aAny
= xPropSet
->getPropertyValue("ObjIDinMSO");
330 catch(const Exception
&)
332 OSL_TRACE("XclEscherEx::StartShape, this control can't get the property ObjIDinMSO!");
334 sal_uInt16 nObjIDinMSO
= 0xFFFF;
335 aAny
>>= nObjIDinMSO
;
336 if( nObjIDinMSO
!= 0xFFFF && nMsCtlType
== 2) //OCX
338 pCurrXclObj
->SetId(nObjIDinMSO
);
343 pCurrAppData
->SetDontWriteShape( true );
347 void XclEscherEx::EndShape( sal_uInt16 nShapeType
, sal_uInt32 nShapeID
)
349 // own escher data created? -> never delete such objects
350 bool bOwnEscher
= pCurrXclObj
&& pCurrXclObj
->IsOwnEscher();
352 // post process the current object - not for objects with own escher data
353 if( pCurrXclObj
&& !bOwnEscher
)
355 // escher data of last shape not written? -> delete it from object list
358 XclObj
* pLastObj
= mrObjMgr
.RemoveLastObj();
359 OSL_ENSURE( pLastObj
== pCurrXclObj
, "XclEscherEx::EndShape - wrong object" );
367 if ( pCurrAppData
->IsStackedGroup() )
368 pCurrXclObj
->SetEscherShapeTypeGroup();
371 pCurrXclObj
->SetEscherShapeType( nShapeType
);
372 UpdateDffFragmentEnd();
377 // get next object from stack
386 pCurrXclObj
= aStack
.top().first
;
387 pCurrAppData
= aStack
.top().second
;
390 if( nAdditionalText
== 3 )
394 EscherExHostAppData
* XclEscherEx::EnterAdditionalTextGroup()
397 pAdditionalText
= static_cast<XclEscherClientTextbox
*>( pCurrAppData
->GetClientTextbox() );
398 pCurrAppData
->SetClientTextbox( NULL
);
402 void XclEscherEx::EndDocument()
405 Flush( static_cast< XclEscherExGlobal
& >( *mxGlobal
).GetPictureStream() );
407 // seek back DFF stream to prepare saving the MSODRAWING[GROUP] records
408 mpOutStrm
->Seek( 0 );
411 XclExpOcxControlObj
* XclEscherEx::CreateOCXCtrlObj( Reference
< XShape
> xShape
, const Rectangle
* pChildAnchor
)
413 ::std::unique_ptr
< XclExpOcxControlObj
> xOcxCtrl
;
415 Reference
< XControlModel
> xCtrlModel
= XclControlHelper::GetControlModel( xShape
);
416 if( xCtrlModel
.is() )
419 if( !mxCtlsStrm
.Is() )
420 mxCtlsStrm
= OpenStream( EXC_STREAM_CTLS
);
421 if( mxCtlsStrm
.Is() )
424 sal_uInt32 nStrmStart
= static_cast< sal_uInt32
>( mxCtlsStrm
->Tell() );
426 // writes from xCtrlModel into mxCtlsStrm, raw class name returned in aClassName
427 Reference
< XOutputStream
> xOut( new utl::OSeekableOutputStreamWrapper( *mxCtlsStrm
) );
428 Reference
< com::sun::star::frame::XModel
> xModel( GetDocShell() ? GetDocShell()->GetModel() : NULL
);
429 if( xModel
.is() && xOut
.is() && oox::ole::MSConvertOCXControls::WriteOCXExcelKludgeStream( xModel
, xOut
, xCtrlModel
, xShape
->getSize(), aClassName
) )
431 sal_uInt32 nStrmSize
= static_cast< sal_uInt32
>( mxCtlsStrm
->Tell() - nStrmStart
);
432 // adjust the class name to "Forms.***.1"
433 aClassName
= "Forms." + aClassName
+ ".1";
434 xOcxCtrl
.reset( new XclExpOcxControlObj( mrObjMgr
, xShape
, pChildAnchor
, aClassName
, nStrmStart
, nStrmSize
) );
438 return xOcxCtrl
.release();
441 XclExpTbxControlObj
* XclEscherEx::CreateTBXCtrlObj( Reference
< XShape
> xShape
, const Rectangle
* pChildAnchor
)
443 ::std::unique_ptr
< XclExpTbxControlObj
> xTbxCtrl( new XclExpTbxControlObj( mrObjMgr
, xShape
, pChildAnchor
) );
444 if( xTbxCtrl
->GetObjType() == EXC_OBJTYPE_UNKNOWN
)
449 // find attached macro
450 Reference
< XControlModel
> xCtrlModel
= XclControlHelper::GetControlModel( xShape
);
451 ConvertTbxMacro( *xTbxCtrl
, xCtrlModel
);
453 return xTbxCtrl
.release();
456 void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj
& rTbxCtrlObj
, Reference
< XControlModel
> xCtrlModel
)
458 SdrPage
* pSdrPage
= GetSdrPage( GetCurrScTab() );
459 if( xCtrlModel
.is() && GetDocShell() && pSdrPage
) try
461 Reference
< XFormsSupplier
> xFormsSupplier( pSdrPage
->getUnoPage(), UNO_QUERY_THROW
);
462 Reference
< XIndexAccess
> xFormsIA( xFormsSupplier
->getForms(), UNO_QUERY_THROW
);
464 // 1) try to find the index of the processed control in the form
466 Reference
< XIndexAccess
> xFormIA
; // needed in step 2) below
467 sal_Int32 nFoundIdx
= -1;
469 // search all existing forms in the draw page
470 for( sal_Int32 nFormIdx
= 0, nFormCount
= xFormsIA
->getCount();
471 (nFoundIdx
< 0) && (nFormIdx
< nFormCount
); ++nFormIdx
)
473 // get the XIndexAccess interface of the form with index nFormIdx
474 if( xFormIA
.set( xFormsIA
->getByIndex( nFormIdx
), UNO_QUERY
) )
476 // search all elements (controls) of the current form by index
477 for( sal_Int32 nCtrlIdx
= 0, nCtrlCount
= xFormIA
->getCount();
478 (nFoundIdx
< 0) && (nCtrlIdx
< nCtrlCount
); ++nCtrlIdx
)
480 // compare implementation pointers of the control models
481 Reference
< XControlModel
> xCurrModel( xFormIA
->getByIndex( nCtrlIdx
), UNO_QUERY
);
482 if( xCtrlModel
.get() == xCurrModel
.get() )
483 nFoundIdx
= nCtrlIdx
;
488 // 2) try to find an attached macro
490 if( xFormIA
.is() && (nFoundIdx
>= 0) )
492 Reference
< XEventAttacherManager
> xEventMgr( xFormIA
, UNO_QUERY_THROW
);
493 // loop over all events attached to the found control
494 const Sequence
< ScriptEventDescriptor
> aEventSeq( xEventMgr
->getScriptEvents( nFoundIdx
) );
496 for( sal_Int32 nEventIdx
= 0, nEventCount
= aEventSeq
.getLength();
497 !bFound
&& (nEventIdx
< nEventCount
); ++nEventIdx
)
499 // try to set the event data at the Excel control object, returns true on success
500 bFound
= rTbxCtrlObj
.SetMacroLink( aEventSeq
[ nEventIdx
] );
509 void XclEscherEx::DeleteCurrAppData()
513 delete pCurrAppData
->GetClientAnchor();
514 // delete pCurrAppData->GetClientData();
515 delete pCurrAppData
->GetClientTextbox();
516 delete pCurrAppData
->GetInteractionInfo();
521 // --- class XclEscherClientData -------------------------------------
523 void XclEscherClientData::WriteData( EscherEx
& rEx
) const
524 { // actual data is in the following OBJ record
525 rEx
.AddAtom( 0, ESCHER_ClientData
);
528 // --- class XclEscherClientTextbox -------------------------------------
530 XclEscherClientTextbox::XclEscherClientTextbox( const XclExpRoot
& rRoot
,
531 const SdrTextObj
& rObj
, XclObj
* pObj
)
539 void XclEscherClientTextbox::WriteData( EscherEx
& /*rEx*/ ) const
541 pXclObj
->SetText( GetRoot(), rTextObj
);
545 ShapeInteractionHelper::CreateShapeObj( XclExpObjectManager
& rObjMgr
, const Reference
< XShape
>& xShape
)
547 return new XclExpShapeObj( rObjMgr
, xShape
);
551 ShapeInteractionHelper::PopulateShapeInteractionInfo( XclExpObjectManager
& rObjMgr
, const Reference
< XShape
>& xShape
, EscherExHostAppData
& rHostAppData
)
555 SvMemoryStream
* pMemStrm
= NULL
;
558 if ( ScMacroInfo
* pInfo
= ScDrawLayer::GetMacroInfo( ::GetSdrObjectFromXShape( xShape
) ) )
560 sHyperLink
= pInfo
->GetHlink();
561 sMacro
= pInfo
->GetMacro();
563 if ( !sHyperLink
.isEmpty() )
565 pMemStrm
= new SvMemoryStream();
566 XclExpStream
tmpStream( *pMemStrm
, rObjMgr
.GetRoot() );
567 ScAddress dummyAddress
;
568 SvxURLField aUrlField
;
569 aUrlField
.SetURL( sHyperLink
);
570 XclExpHyperlink
hExpHlink( rObjMgr
.GetRoot(), aUrlField
, dummyAddress
);
571 hExpHlink
.WriteEmbeddedData( tmpStream
);
573 if ( !sHyperLink
.isEmpty() || !sMacro
.isEmpty() )
574 rHostAppData
.SetInteractionInfo( new InteractionInfo( pMemStrm
, true ) );
581 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */