Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / sc / source / filter / xcl97 / xcl97esc.cxx
blob03336e90b5772a7f20e08324f7b34fc11e5cb90e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <memory>
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 )
70 : XclExpRoot(rRoot)
71 , mpPicStrm(nullptr)
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 );
81 return mpPicStrm;
84 XclEscherEx::XclEscherEx( const XclExpRoot& rRoot, XclExpObjectManager& rObjMgr, SvStream& rStrm, const XclEscherEx* pParent ) :
85 EscherEx( pParent ? pParent->mxGlobal : std::make_shared<XclEscherExGlobal>( rRoot ), &rStrm ),
86 XclExpRoot( rRoot ),
87 mrObjMgr( rObjMgr ),
88 pCurrXclObj( nullptr ),
89 pTheClientData( new XclEscherClientData ),
90 pAdditionalText( nullptr ),
91 nAdditionalText( 0 ),
92 mnNextKey( 0 ),
93 mbIsRootDff( pParent == nullptr )
95 InsertPersistOffset( mnNextKey, 0 );
98 XclEscherEx::~XclEscherEx()
100 OSL_ENSURE( aStack.empty(), "~XclEscherEx: stack not empty" );
101 DeleteCurrAppData();
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. */
118 ++mnNextKey;
119 InsertPersistOffset( mnNextKey, mpOutStrm->Tell() );
121 return nPersistKey;
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 );
157 return pAnchor;
160 namespace {
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;
173 return bIsFontwork;
176 } // namespace
178 EscherExHostAppData* XclEscherEx::StartShape( const Reference< XShape >& rxShape, const tools::Rectangle* pChildAnchor )
180 if ( nAdditionalText )
181 nAdditionalText++;
182 bool bInGroup = ( pCurrXclObj != nullptr );
183 if ( bInGroup )
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;
196 if ( !pObj )
197 pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() ); // just what is it?!?
198 else
200 pCurrXclObj = nullptr;
201 SdrObjKind nObjType = pObj->GetObjIdentifier();
203 if( nObjType == SdrObjKind::OLE2 )
205 // no OLE objects in embedded drawings (chart shapes)
206 if( mbIsRootDff )
208 //! not-const because GetObjRef may load the OLE object
209 Reference < XClassifiedObject > xObj( static_cast<SdrOle2Obj*>(pObj)->GetObjRef() );
210 if ( xObj.is() )
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() );
224 else
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 );
231 Any aAny;
234 aAny = xPropSet->getPropertyValue("ControlTypeinMSO");
235 aAny >>= nMsCtlType;
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();
245 if( !pCurrXclObj )
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 );
256 if ( pCurrXclObj )
258 if ( !mrObjMgr.AddObj( std::unique_ptr<XclObj>(pCurrXclObj) ) )
259 { // maximum count reached, object got deleted
260 pCurrXclObj = nullptr;
262 else
264 pCurrAppData->SetClientData( pTheClientData.get() );
265 if ( nAdditionalText == 0 )
267 if ( pObj )
269 if ( !bInGroup )
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();
282 if( pParaObj )
283 pCurrAppData->SetClientTextbox(
284 new XclEscherClientTextbox( GetRoot(), *pTextObj, pCurrXclObj ) );
287 else
289 if ( !bInGroup )
290 pCurrAppData->SetClientAnchor( mrObjMgr.CreateDffAnchor() );
293 else if ( nAdditionalText == 3 )
295 if ( pAdditionalText )
297 pAdditionalText->SetXclObj( pCurrXclObj );
298 pCurrAppData->SetClientTextbox( pAdditionalText );
303 if(pObj)
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 );
313 Any aAny;
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);
330 if ( !pCurrXclObj )
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
344 if( nShapeID == 0 )
346 std::unique_ptr<XclObj> pLastObj = mrObjMgr.RemoveLastObj();
347 OSL_ENSURE( pLastObj.get() == pCurrXclObj, "XclEscherEx::EndShape - wrong object" );
348 pCurrXclObj = nullptr;
351 if( pCurrXclObj )
353 // set shape type
354 if ( pCurrAppData->IsStackedGroup() )
355 pCurrXclObj->SetEscherShapeTypeGroup();
356 else
358 pCurrXclObj->SetEscherShapeType( nShapeType );
359 UpdateDffFragmentEnd();
364 // get next object from stack
365 DeleteCurrAppData();
366 if (aStack.empty())
368 pCurrXclObj = nullptr;
369 pCurrAppData = nullptr;
371 else
373 pCurrXclObj = aStack.top().first;
374 pCurrAppData = std::move(aStack.top().second);
375 aStack.pop();
377 if( nAdditionalText == 3 )
378 nAdditionalText = 0;
381 EscherExHostAppData* XclEscherEx::EnterAdditionalTextGroup()
383 nAdditionalText = 1;
384 pAdditionalText = static_cast<XclEscherClientTextbox*>( pCurrAppData->GetClientTextbox() );
385 pCurrAppData->SetClientTextbox( nullptr );
386 return pCurrAppData.get();
389 void XclEscherEx::EndDocument()
391 if( mbIsRootDff )
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() )
405 // output stream
406 if( !mxCtlsStrm.is() )
407 mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
408 if( mxCtlsStrm.is() )
410 OUString aClassName;
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 ) );
425 return xOcxCtrl;
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 )
432 xTbxCtrl.reset();
433 else
435 // find attached macro
436 Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
437 ConvertTbxMacro( *xTbxCtrl, xCtrlModel );
439 return xTbxCtrl;
442 void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj& rTbxCtrlObj, Reference< XControlModel > const & xCtrlModel )
444 SdrPage* pSdrPage = GetSdrPage( GetCurrScTab() );
445 if( !(xCtrlModel.is() && GetDocShell() && pSdrPage) )
446 return;
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 ))
488 break;
492 catch( Exception& )
497 void XclEscherEx::DeleteCurrAppData()
499 if ( pCurrAppData )
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 )
520 XclExpRoot( rRoot ),
521 rTextObj( rObj ),
522 pXclObj( pObj )
526 void XclEscherClientTextbox::WriteData( EscherEx& /*rEx*/ ) const
528 pXclObj->SetText( GetRoot(), rTextObj );
531 XclExpShapeObj*
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;
544 OUString sHyperLink;
545 OUString sMacro;
546 SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
547 if (pObj)
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));
566 catch (Exception&)
571 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */