1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <memory>
21 #include <com/sun/star/awt/XControlModel.hpp>
22 #include <com/sun/star/embed/XClassifiedObject.hpp>
23 #include <com/sun/star/form/XFormsSupplier.hpp>
24 #include <com/sun/star/script/XEventAttacherManager.hpp>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 
27 #include <svx/svdpage.hxx>
28 #include <svx/svdotext.hxx>
29 #include <svx/svdobj.hxx>
30 #include <svx/svdoole2.hxx>
31 #include <svx/unoapi.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>
37 
38 #include <sot/exchange.hxx>
39 #include <sot/storage.hxx>
40 #include <xeescher.hxx>
41 
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>
51 
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;
68 
XclEscherExGlobal(const XclExpRoot & rRoot)69 XclEscherExGlobal::XclEscherExGlobal( const XclExpRoot& rRoot ) :
70     XclExpRoot( rRoot )
71 {
72     SetBaseURI( GetMedium().GetBaseURL( true ) );
73 }
74 
ImplQueryPictureStream()75 SvStream* XclEscherExGlobal::ImplQueryPictureStream()
76 {
77     mxPicTempFile.reset( new ::utl::TempFile );
78     if( mxPicTempFile->IsValid() )
79     {
80         mxPicTempFile->EnableKillingFile();
81         mxPicStrm = ::utl::UcbStreamHelper::CreateStream( mxPicTempFile->GetURL(), StreamMode::STD_READWRITE );
82         mxPicStrm->SetEndian( SvStreamEndian::LITTLE );
83     }
84     return mxPicStrm.get();
85 }
86 
XclEscherEx(const XclExpRoot & rRoot,XclExpObjectManager & rObjMgr,SvStream & rStrm,const XclEscherEx * pParent)87 XclEscherEx::XclEscherEx( const XclExpRoot& rRoot, XclExpObjectManager& rObjMgr, SvStream& rStrm, const XclEscherEx* pParent ) :
88     EscherEx( pParent ? pParent->mxGlobal : std::shared_ptr<EscherExGlobal>( new XclEscherExGlobal( rRoot ) ), &rStrm ),
89     XclExpRoot( rRoot ),
90     mrObjMgr( rObjMgr ),
91     pCurrXclObj( nullptr ),
92     pTheClientData( new XclEscherClientData ),
93     pAdditionalText( nullptr ),
94     nAdditionalText( 0 ),
95     mnNextKey( 0 ),
96     mbIsRootDff( pParent == nullptr )
97 {
98     InsertPersistOffset( mnNextKey, 0 );
99 }
100 
~XclEscherEx()101 XclEscherEx::~XclEscherEx()
102 {
103     OSL_ENSURE( aStack.empty(), "~XclEscherEx: stack not empty" );
104     DeleteCurrAppData();
105     pTheClientData.reset();
106 }
107 
InitNextDffFragment()108 sal_uInt32 XclEscherEx::InitNextDffFragment()
109 {
110     /*  Current value of mnNextKey will be used by caller to refer to the
111         starting point of the DFF fragment. The key exists already in the
112         PersistTable (has been inserted by c'tor of previous call of
113         InitNextDffFragment(), has been updated by UpdateDffFragmentEnd(). */
114     sal_uInt32 nPersistKey = mnNextKey;
115 
116     /*  Prepare the next key that is used by caller as end point of the DFF
117         fragment. Will be updated by caller when writing to the DFF stream,
118         using the UpdateDffFragmentEnd() function. This is needed to find DFF
119         data written by the SVX base class implementation without interaction,
120         e.g. the solver container that will be written after the last shape. */
121     ++mnNextKey;
122     InsertPersistOffset( mnNextKey, mpOutStrm->Tell() );
123 
124     return nPersistKey;
125 }
126 
UpdateDffFragmentEnd()127 void XclEscherEx::UpdateDffFragmentEnd()
128 {
129     // update existing fragment key with new stream position
130     ReplacePersistOffset( mnNextKey, mpOutStrm->Tell() );
131 }
132 
GetDffFragmentPos(sal_uInt32 nFragmentKey)133 sal_uInt32 XclEscherEx::GetDffFragmentPos( sal_uInt32 nFragmentKey )
134 {
135     /*  TODO: this function is non-const because PersistTable::PtGetOffsetByID()
136         is non-const due to tools/List usage. */
137     return GetPersistOffset( nFragmentKey );
138 }
139 
GetDffFragmentSize(sal_uInt32 nFragmentKey)140 sal_uInt32 XclEscherEx::GetDffFragmentSize( sal_uInt32 nFragmentKey )
141 {
142     /*  TODO: this function is non-const because PersistTable::PtGetOffsetByID()
143         is non-const due to tools/List usage. */
144     return GetDffFragmentPos( nFragmentKey + 1 ) - GetDffFragmentPos( nFragmentKey );
145 }
146 
HasPendingDffData()147 bool XclEscherEx::HasPendingDffData()
148 {
149     /*  TODO: this function is non-const because PersistTable::PtGetOffsetByID()
150         is non-const due to tools/List usage. */
151     return GetDffFragmentPos( mnNextKey ) < GetStreamPos();
152 }
153 
CreateDffAnchor(const SdrObject & rSdrObj) const154 XclExpDffAnchorBase* XclEscherEx::CreateDffAnchor( const SdrObject& rSdrObj ) const
155 {
156     // the object manager creates the correct anchor type according to context
157     XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
158     // pass the drawing object, that will calculate the anchor position
159     pAnchor->SetSdrObject( rSdrObj );
160     return pAnchor;
161 }
162 
163 namespace {
164 
lcl_IsFontwork(const SdrObject * pObj)165 bool lcl_IsFontwork( const SdrObject* pObj )
166 {
167     bool bIsFontwork = false;
168     if( pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE )
169     {
170         const OUString aTextPath = "TextPath";
171         const SdrCustomShapeGeometryItem& rGeometryItem =
172             pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
173         if( const Any* pAny = rGeometryItem.GetPropertyValueByName( aTextPath, aTextPath ) )
174             *pAny >>= bIsFontwork;
175     }
176     return bIsFontwork;
177 }
178 
179 } // namespace
180 
StartShape(const Reference<XShape> & rxShape,const tools::Rectangle * pChildAnchor)181 EscherExHostAppData* XclEscherEx::StartShape( const Reference< XShape >& rxShape, const tools::Rectangle* pChildAnchor )
182 {
183     if ( nAdditionalText )
184         nAdditionalText++;
185     bool bInGroup = ( pCurrXclObj != nullptr );
186     if ( bInGroup )
187     {   // stacked recursive group object
188         if ( !pCurrAppData->IsStackedGroup() )
189         {   //! UpdateDffFragmentEnd only once
190             pCurrAppData->SetStackedGroup( true );
191             UpdateDffFragmentEnd();
192         }
193     }
194     aStack.push( std::make_pair( pCurrXclObj, std::move(pCurrAppData) ) );
195     pCurrAppData.reset( new XclEscherHostAppData );
196     SdrObject* pObj = GetSdrObjectFromXShape( rxShape );
197     //added for exporting OCX control
198     sal_Int16 nMsCtlType = 0;
199     if ( !pObj )
200         pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );  // just what is it?!?
201     else
202     {
203         pCurrXclObj = nullptr;
204         sal_uInt16 nObjType = pObj->GetObjIdentifier();
205 
206         if( nObjType == OBJ_OLE2 )
207         {
208             // no OLE objects in embedded drawings (chart shapes)
209             if( mbIsRootDff )
210             {
211                 //! not-const because GetObjRef may load the OLE object
212                 Reference < XClassifiedObject > xObj( static_cast<SdrOle2Obj*>(pObj)->GetObjRef(), UNO_QUERY );
213                 if ( xObj.is() )
214                 {
215                     SvGlobalName aObjClsId( xObj->getClassID() );
216                     if ( SotExchange::IsChart( aObjClsId ) )
217                     {   // yes, it's a chart diagram
218                         mrObjMgr.AddObj( std::make_unique<XclExpChartObj>( mrObjMgr, rxShape, pChildAnchor, &GetDoc() ) );
219                         pCurrXclObj = nullptr;     // no metafile or whatsoever
220                     }
221                     else    // metafile and OLE object
222                         pCurrXclObj = new XclObjOle( mrObjMgr, *pObj );
223                 }
224                 else    // just a metafile
225                     pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );
226             }
227             else
228                 pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );
229         }
230         else if( nObjType == OBJ_UNO )
231         {
232             //added for exporting OCX control
233             Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
234             Any aAny;
235             try
236             {
237                 aAny = xPropSet->getPropertyValue("ControlTypeinMSO");
238                 aAny >>= nMsCtlType;
239             }
240             catch(const Exception&)
241             {
242                 SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ControlTypeinMSO!");
243             }
244             if( nMsCtlType == 2 )  //OCX Form Control
245                 pCurrXclObj = CreateOCXCtrlObj( rxShape, pChildAnchor ).release();
246             else  //TBX Form Control
247                 pCurrXclObj = CreateTBXCtrlObj( rxShape, pChildAnchor ).release();
248             if( !pCurrXclObj )
249                 pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );   // just a metafile
250         }
251         else if( !ScDrawLayer::IsNoteCaption( pObj ) )
252         {
253             // ignore permanent note shapes
254             // #i12190# do not ignore callouts (do not filter by object type ID)
255             pCurrXclObj = ShapeInteractionHelper::CreateShapeObj( mrObjMgr, rxShape, &GetDoc() );
256             ShapeInteractionHelper::PopulateShapeInteractionInfo( mrObjMgr, rxShape, *pCurrAppData );
257         }
258     }
259     if ( pCurrXclObj )
260     {
261         if ( !mrObjMgr.AddObj( std::unique_ptr<XclObj>(pCurrXclObj) ) )
262         {   // maximum count reached, object got deleted
263             pCurrXclObj = nullptr;
264         }
265         else
266         {
267             pCurrAppData->SetClientData( pTheClientData.get() );
268             if ( nAdditionalText == 0 )
269             {
270                 if ( pObj )
271                 {
272                     if ( !bInGroup )
273                     {
274                         /*  Create a dummy anchor carrying the flags. Real
275                             coordinates are calculated later in virtual call of
276                             WriteData(EscherEx&,const Rectangle&). */
277                         XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
278                         pAnchor->SetFlags( *pObj );
279                         pCurrAppData->SetClientAnchor( pAnchor );
280                     }
281                     const SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( pObj  );
282                     if( pTextObj && !lcl_IsFontwork( pTextObj ) && (pObj->GetObjIdentifier() != OBJ_CAPTION) )
283                     {
284                         const OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject();
285                         if( pParaObj )
286                             pCurrAppData->SetClientTextbox(
287                                 new XclEscherClientTextbox( GetRoot(), *pTextObj, pCurrXclObj ) );
288                     }
289                 }
290                 else
291                 {
292                     if ( !bInGroup )
293                         pCurrAppData->SetClientAnchor( mrObjMgr.CreateDffAnchor() );
294                 }
295             }
296             else if ( nAdditionalText == 3 )
297             {
298                 if ( pAdditionalText )
299                 {
300                     pAdditionalText->SetXclObj( pCurrXclObj );
301                     pCurrAppData->SetClientTextbox( pAdditionalText );
302                 }
303             }
304         }
305     }
306     if(pObj)
307     {
308         //add  for exporting OCX control
309         //for OCX control import from MS office file,we need keep the id value as MS office file.
310         //GetOldRoot().pObjRecs->Add( pCurrXclObj ) statement has generated the id value as obj id rule;
311         //but we trick it here.
312         sal_uInt16 nObjType = pObj->GetObjIdentifier();
313         if( nObjType == OBJ_UNO && pCurrXclObj )
314         {
315             Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
316             Any aAny;
317             try
318             {
319                 aAny = xPropSet->getPropertyValue("ObjIDinMSO");
320             }
321             catch(const Exception&)
322             {
323                 SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ObjIDinMSO!");
324             }
325             sal_uInt16 nObjIDinMSO = 0xFFFF;
326             aAny >>= nObjIDinMSO;
327             if( nObjIDinMSO != 0xFFFF && nMsCtlType == 2)  //OCX
328             {
329                 pCurrXclObj->SetId(nObjIDinMSO);
330             }
331         }
332     }
333     if ( !pCurrXclObj )
334         pCurrAppData->SetDontWriteShape( true );
335     return pCurrAppData.get();
336 }
337 
EndShape(sal_uInt16 nShapeType,sal_uInt32 nShapeID)338 void XclEscherEx::EndShape( sal_uInt16 nShapeType, sal_uInt32 nShapeID )
339 {
340     // own escher data created? -> never delete such objects
341     bool bOwnEscher = pCurrXclObj && pCurrXclObj->IsOwnEscher();
342 
343     // post process the current object - not for objects with own escher data
344     if( pCurrXclObj && !bOwnEscher )
345     {
346         // escher data of last shape not written? -> delete it from object list
347         if( nShapeID == 0 )
348         {
349             std::unique_ptr<XclObj> pLastObj = mrObjMgr.RemoveLastObj();
350             OSL_ENSURE( pLastObj.get() == pCurrXclObj, "XclEscherEx::EndShape - wrong object" );
351             pCurrXclObj = nullptr;
352         }
353 
354         if( pCurrXclObj )
355         {
356             // set shape type
357             if ( pCurrAppData->IsStackedGroup() )
358                 pCurrXclObj->SetEscherShapeTypeGroup();
359             else
360             {
361                 pCurrXclObj->SetEscherShapeType( nShapeType );
362                 UpdateDffFragmentEnd();
363             }
364         }
365     }
366 
367     // get next object from stack
368     DeleteCurrAppData();
369     if (aStack.empty())
370     {
371         pCurrXclObj = nullptr;
372         pCurrAppData = nullptr;
373     }
374     else
375     {
376         pCurrXclObj = aStack.top().first;
377         pCurrAppData = std::move(aStack.top().second);
378         aStack.pop();
379     }
380     if( nAdditionalText == 3 )
381         nAdditionalText = 0;
382 }
383 
EnterAdditionalTextGroup()384 EscherExHostAppData* XclEscherEx::EnterAdditionalTextGroup()
385 {
386     nAdditionalText = 1;
387     pAdditionalText = static_cast<XclEscherClientTextbox*>( pCurrAppData->GetClientTextbox() );
388     pCurrAppData->SetClientTextbox( nullptr );
389     return pCurrAppData.get();
390 }
391 
EndDocument()392 void XclEscherEx::EndDocument()
393 {
394     if( mbIsRootDff )
395         Flush( static_cast< XclEscherExGlobal& >( *mxGlobal ).GetPictureStream() );
396 
397     // seek back DFF stream to prepare saving the MSODRAWING[GROUP] records
398     mpOutStrm->Seek( 0 );
399 }
400 
CreateOCXCtrlObj(Reference<XShape> const & xShape,const tools::Rectangle * pChildAnchor)401 std::unique_ptr<XclExpOcxControlObj> XclEscherEx::CreateOCXCtrlObj( Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor )
402 {
403     ::std::unique_ptr< XclExpOcxControlObj > xOcxCtrl;
404 
405     Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
406     if( xCtrlModel.is() )
407     {
408         // output stream
409         if( !mxCtlsStrm.is() )
410             mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
411         if( mxCtlsStrm.is() )
412         {
413             OUString aClassName;
414             sal_uInt32 nStrmStart = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() );
415 
416             // writes from xCtrlModel into mxCtlsStrm, raw class name returned in aClassName
417             Reference< XOutputStream > xOut( new utl::OSeekableOutputStreamWrapper( *mxCtlsStrm ) );
418             Reference< css::frame::XModel > xModel( GetDocShell() ? GetDocShell()->GetModel() : nullptr );
419             if( xModel.is() && xOut.is() && oox::ole::MSConvertOCXControls::WriteOCXExcelKludgeStream( xModel, xOut, xCtrlModel, xShape->getSize(), aClassName ) )
420             {
421                 sal_uInt32 nStrmSize = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() - nStrmStart );
422                 // adjust the class name to "Forms.***.1"
423                 aClassName = "Forms." + aClassName +  ".1";
424                 xOcxCtrl.reset( new XclExpOcxControlObj( mrObjMgr, xShape, pChildAnchor, aClassName, nStrmStart, nStrmSize ) );
425             }
426         }
427     }
428     return xOcxCtrl;
429 }
430 
CreateTBXCtrlObj(Reference<XShape> const & xShape,const tools::Rectangle * pChildAnchor)431 std::unique_ptr<XclExpTbxControlObj> XclEscherEx::CreateTBXCtrlObj( Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor )
432 {
433     ::std::unique_ptr< XclExpTbxControlObj > xTbxCtrl( new XclExpTbxControlObj( mrObjMgr, xShape, pChildAnchor ) );
434     if( xTbxCtrl->GetObjType() == EXC_OBJTYPE_UNKNOWN )
435         xTbxCtrl.reset();
436 
437     if (xTbxCtrl)
438     {
439         // find attached macro
440         Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
441         ConvertTbxMacro( *xTbxCtrl, xCtrlModel );
442     }
443     return xTbxCtrl;
444 }
445 
ConvertTbxMacro(XclExpTbxControlObj & rTbxCtrlObj,Reference<XControlModel> const & xCtrlModel)446 void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj& rTbxCtrlObj, Reference< XControlModel > const & xCtrlModel )
447 {
448     SdrPage* pSdrPage = GetSdrPage( GetCurrScTab() );
449     if( xCtrlModel.is() && GetDocShell() && pSdrPage ) try
450     {
451         Reference< XFormsSupplier > xFormsSupplier( pSdrPage->getUnoPage(), UNO_QUERY_THROW );
452         Reference< XIndexAccess > xFormsIA( xFormsSupplier->getForms(), UNO_QUERY_THROW );
453 
454         // 1) try to find the index of the processed control in the form
455 
456         Reference< XIndexAccess > xFormIA;  // needed in step 2) below
457         sal_Int32 nFoundIdx = -1;
458 
459         // search all existing forms in the draw page
460         for( sal_Int32 nFormIdx = 0, nFormCount = xFormsIA->getCount();
461                 (nFoundIdx < 0) && (nFormIdx < nFormCount); ++nFormIdx )
462         {
463             // get the XIndexAccess interface of the form with index nFormIdx
464             if( xFormIA.set( xFormsIA->getByIndex( nFormIdx ), UNO_QUERY ) )
465             {
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 )
469                 {
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;
474                 }
475             }
476         }
477 
478         // 2) try to find an attached macro
479 
480         if( xFormIA.is() && (nFoundIdx >= 0) )
481         {
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 )
486             {
487                 // try to set the event data at the Excel control object, returns true on success
488                 if (rTbxCtrlObj.SetMacroLink( rEvent ))
489                     break;
490             }
491         }
492     }
493     catch( Exception& )
494     {
495     }
496 }
497 
DeleteCurrAppData()498 void XclEscherEx::DeleteCurrAppData()
499 {
500     if ( pCurrAppData )
501     {
502         delete pCurrAppData->GetClientAnchor();
503 //      delete pCurrAppData->GetClientData();
504         delete pCurrAppData->GetClientTextbox();
505         delete pCurrAppData->GetInteractionInfo();
506         pCurrAppData.reset();
507     }
508 }
509 
510 // --- class XclEscherClientData -------------------------------------
511 
WriteData(EscherEx & rEx) const512 void XclEscherClientData::WriteData( EscherEx& rEx ) const
513 {   // actual data is in the following OBJ record
514     rEx.AddAtom( 0, ESCHER_ClientData );
515 }
516 
517 // --- class XclEscherClientTextbox -------------------------------------
518 
XclEscherClientTextbox(const XclExpRoot & rRoot,const SdrTextObj & rObj,XclObj * pObj)519 XclEscherClientTextbox::XclEscherClientTextbox( const XclExpRoot& rRoot,
520             const SdrTextObj& rObj, XclObj* pObj )
521         :
522         XclExpRoot( rRoot ),
523         rTextObj( rObj ),
524         pXclObj( pObj )
525 {
526 }
527 
WriteData(EscherEx &) const528 void XclEscherClientTextbox::WriteData( EscherEx& /*rEx*/ ) const
529 {
530     pXclObj->SetText( GetRoot(), rTextObj );
531 }
532 
533 XclExpShapeObj*
CreateShapeObj(XclExpObjectManager & rObjMgr,const Reference<XShape> & xShape,ScDocument * pDoc)534 ShapeInteractionHelper::CreateShapeObj( XclExpObjectManager& rObjMgr, const Reference< XShape >& xShape, ScDocument* pDoc )
535 {
536     return new XclExpShapeObj( rObjMgr, xShape, pDoc );
537 }
538 
539 void
PopulateShapeInteractionInfo(const XclExpObjectManager & rObjMgr,const Reference<XShape> & xShape,EscherExHostAppData & rHostAppData)540 ShapeInteractionHelper::PopulateShapeInteractionInfo( const XclExpObjectManager& rObjMgr, const Reference< XShape >& xShape, EscherExHostAppData& rHostAppData )
541 {
542    try
543    {
544       SvMemoryStream* pMemStrm = nullptr;
545       OUString sHyperLink;
546       OUString sMacro;
547       if ( ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( ::GetSdrObjectFromXShape( xShape ) ) )
548       {
549          sHyperLink = pInfo->GetHlink();
550          sMacro = pInfo->GetMacro();
551       }
552       if (  !sHyperLink.isEmpty() )
553       {
554          pMemStrm = new SvMemoryStream();
555          XclExpStream tmpStream( *pMemStrm, rObjMgr.GetRoot() );
556          ScAddress dummyAddress;
557          SvxURLField aUrlField;
558          aUrlField.SetURL( sHyperLink );
559          XclExpHyperlink hExpHlink( rObjMgr.GetRoot(), aUrlField, dummyAddress );
560          hExpHlink.WriteEmbeddedData( tmpStream );
561       }
562       if ( !sHyperLink.isEmpty() || !sMacro.isEmpty() )
563           rHostAppData.SetInteractionInfo( new InteractionInfo( pMemStrm ) );
564    }
565    catch( Exception& )
566    {
567    }
568 }
569 
570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
571