xref: /trunk/main/sc/source/filter/xcl97/xcl97esc.cxx (revision b77af630)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_scfilt.hxx"
26 
27 #include <com/sun/star/awt/XControlModel.hpp>
28 #include <com/sun/star/embed/XClassifiedObject.hpp>
29 #include <com/sun/star/form/XFormsSupplier.hpp>
30 #include <com/sun/star/script/ScriptEventDescriptor.hpp>
31 #include <com/sun/star/script/XEventAttacherManager.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/form/XForm.hpp>
34 
35 #include <svx/svdpage.hxx>
36 #include <editeng/outlobj.hxx>
37 #include <svx/svdotext.hxx>
38 #include <svx/svdobj.hxx>
39 #include <svx/svdoole2.hxx>
40 #include <svx/unoapi.hxx>
41 #include <svx/fmglob.hxx>
42 #include <filter/msfilter/msocximex.hxx>
43 #include <vcl/outdev.hxx>
44 #include <unotools/tempfile.hxx>
45 #include <unotools/ucbstreamhelper.hxx>
46 #include <tools/debug.hxx>
47 #include <svx/sdasitm.hxx>
48 #include <sfx2/docfile.hxx>
49 
50 #include <sot/exchange.hxx>
51 #include "xeescher.hxx"
52 
53 #include "global.hxx"
54 #include "document.hxx"
55 #include "drwlayer.hxx"
56 #include "xcl97rec.hxx"
57 #include "xehelper.hxx"
58 #include "xechart.hxx"
59 #include "xcl97esc.hxx"
60 
61 using ::rtl::OUString;
62 using ::com::sun::star::uno::Any;
63 using ::com::sun::star::uno::Exception;
64 using ::com::sun::star::uno::Reference;
65 using ::com::sun::star::uno::Sequence;
66 using ::com::sun::star::uno::UNO_QUERY;
67 using ::com::sun::star::uno::UNO_QUERY_THROW;
68 using ::com::sun::star::container::XIndexAccess;
69 using ::com::sun::star::embed::XClassifiedObject;
70 using ::com::sun::star::drawing::XShape;
71 using ::com::sun::star::awt::XControlModel;
72 using ::com::sun::star::beans::XPropertySet;
73 using ::com::sun::star::uno::Any;
74 using ::com::sun::star::form::XForm;
75 using ::com::sun::star::form::XFormComponent;
76 using ::com::sun::star::form::XFormsSupplier;
77 using ::com::sun::star::script::ScriptEventDescriptor;
78 using ::com::sun::star::script::XEventAttacherManager;
79 
80 // ============================================================================
81 
XclEscherExGlobal(const XclExpRoot & rRoot)82 XclEscherExGlobal::XclEscherExGlobal( const XclExpRoot& rRoot ) :
83     XclExpRoot( rRoot )
84 {
85     SetBaseURI( GetMedium().GetBaseURL( true ) );
86 }
87 
ImplQueryPictureStream()88 SvStream* XclEscherExGlobal::ImplQueryPictureStream()
89 {
90     mxPicTempFile.reset( new ::utl::TempFile );
91     if( mxPicTempFile->IsValid() )
92     {
93         mxPicTempFile->EnableKillingFile();
94         mxPicStrm.reset( ::utl::UcbStreamHelper::CreateStream( mxPicTempFile->GetURL(), STREAM_STD_READWRITE ) );
95         mxPicStrm->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
96     }
97     return mxPicStrm.get();
98 }
99 
100 // ============================================================================
101 
XclEscherEx(const XclExpRoot & rRoot,XclExpObjectManager & rObjMgr,SvStream & rStrm,const XclEscherEx * pParent)102 XclEscherEx::XclEscherEx( const XclExpRoot& rRoot, XclExpObjectManager& rObjMgr, SvStream& rStrm, const XclEscherEx* pParent ) :
103     EscherEx( pParent ? pParent->mxGlobal : EscherExGlobalRef( new XclEscherExGlobal( rRoot ) ), rStrm ),
104     XclExpRoot( rRoot ),
105     mrObjMgr( rObjMgr ),
106     pCurrXclObj( NULL ),
107     pCurrAppData( NULL ),
108     pTheClientData( new XclEscherClientData ),
109     pAdditionalText( NULL ),
110     nAdditionalText( 0 ),
111     mnNextKey( 0 ),
112     mbIsRootDff( pParent == 0 )
113 {
114     InsertPersistOffset( mnNextKey, 0 );
115 }
116 
117 
~XclEscherEx()118 XclEscherEx::~XclEscherEx()
119 {
120 	DBG_ASSERT( !aStack.Count(), "~XclEscherEx: stack not empty" );
121 	DeleteCurrAppData();
122 	delete pTheClientData;
123 }
124 
125 
InitNextDffFragment()126 sal_uInt32 XclEscherEx::InitNextDffFragment()
127 {
128     /*  Current value of mnNextKey will be used by caller to refer to the
129         starting point of the DFF fragment. The key exists already in the
130         PersistTable (has been inserted by c'tor of previous call of
131         InitNextDffFragment(), has been updated by UpdateDffFragmentEnd(). */
132     sal_uInt32 nPersistKey = mnNextKey;
133 
134     /*  Prepare the next key that is used by caller as end point of the DFF
135         fragment. Will be updated by caller when writing to the DFF stream,
136         using the UpdateDffFragmentEnd() function. This is needed to find DFF
137         data written by the SVX base class implementation without interaction,
138         e.g. the solver container that will be written after the last shape. */
139     ++mnNextKey;
140     InsertPersistOffset( mnNextKey, mpOutStrm->Tell() );
141 
142     return nPersistKey;
143 }
144 
UpdateDffFragmentEnd()145 void XclEscherEx::UpdateDffFragmentEnd()
146 {
147     // update existing fragment key with new stream position
148     ReplacePersistOffset( mnNextKey, mpOutStrm->Tell() );
149 }
150 
GetDffFragmentPos(sal_uInt32 nFragmentKey)151 sal_uInt32 XclEscherEx::GetDffFragmentPos( sal_uInt32 nFragmentKey )
152 {
153     /*  TODO: this function is non-const because PersistTable::PtGetOffsetByID()
154         is non-const due to tools/List usage. */
155     return GetPersistOffset( nFragmentKey );
156 }
157 
GetDffFragmentSize(sal_uInt32 nFragmentKey)158 sal_uInt32 XclEscherEx::GetDffFragmentSize( sal_uInt32 nFragmentKey )
159 {
160     /*  TODO: this function is non-const because PersistTable::PtGetOffsetByID()
161         is non-const due to tools/List usage. */
162     return GetDffFragmentPos( nFragmentKey + 1 ) - GetDffFragmentPos( nFragmentKey );
163 }
164 
HasPendingDffData()165 bool XclEscherEx::HasPendingDffData()
166 {
167     /*  TODO: this function is non-const because PersistTable::PtGetOffsetByID()
168         is non-const due to tools/List usage. */
169     return GetDffFragmentPos( mnNextKey ) < GetStreamPos();
170 }
171 
CreateDffAnchor(const SdrObject & rSdrObj) const172 XclExpDffAnchorBase* XclEscherEx::CreateDffAnchor( const SdrObject& rSdrObj ) const
173 {
174     // the object manager creates the correct anchor type according to context
175     XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
176     // pass the drawing object, that will calculate the anchor position
177     pAnchor->SetSdrObject( rSdrObj );
178     return pAnchor;
179 }
180 
181 namespace {
182 
lcl_IsFontwork(const SdrObject * pObj)183 bool lcl_IsFontwork( const SdrObject* pObj )
184 {
185     bool bIsFontwork = false;
186     if( pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE )
187 	{
188         const OUString aTextPath = CREATE_OUSTRING( "TextPath" );
189 		SdrCustomShapeGeometryItem& rGeometryItem = (SdrCustomShapeGeometryItem&)
190 			pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
191         if( Any* pAny = rGeometryItem.GetPropertyValueByName( aTextPath, aTextPath ) )
192 			*pAny >>= bIsFontwork;
193 	}
194 	return bIsFontwork;
195 }
196 
197 } // namespace
198 
StartShape(const Reference<XShape> & rxShape,const Rectangle * pChildAnchor)199 EscherExHostAppData* XclEscherEx::StartShape( const Reference< XShape >& rxShape, const Rectangle* pChildAnchor )
200 {
201 	if ( nAdditionalText )
202 		nAdditionalText++;
203 	sal_Bool bInGroup = ( pCurrXclObj != NULL );
204 	if ( bInGroup )
205 	{	// stacked recursive group object
206 		if ( !pCurrAppData->IsStackedGroup() )
207         {   //! UpdateDffFragmentEnd only once
208 			pCurrAppData->SetStackedGroup( sal_True );
209             UpdateDffFragmentEnd();
210 		}
211 	}
212 	aStack.Push( pCurrXclObj );
213 	aStack.Push( pCurrAppData );
214 	pCurrAppData = new XclEscherHostAppData;
215     SdrObject* pObj = GetSdrObjectFromXShape( rxShape );
216 	//added for exporting OCX control
217 	sal_Int16 nMsCtlType = 0;
218 	if ( !pObj )
219         pCurrXclObj = new XclObjAny( mrObjMgr );  // just what is it?!?
220 	else
221 	{
222         pCurrXclObj = NULL;
223         sal_uInt16 nObjType = pObj->GetObjIdentifier();
224 
225         if( nObjType == OBJ_OLE2 )
226         {
227             // no OLE objects in embedded drawings (chart shapes)
228             if( mbIsRootDff )
229             {
230                 //! not-const because GetObjRef may load the OLE object
231                 Reference < XClassifiedObject > xObj( ((SdrOle2Obj*)pObj)->GetObjRef(), UNO_QUERY );
232                 if ( xObj.is() )
233                 {
234                     SvGlobalName aObjClsId( xObj->getClassID() );
235                     if ( SotExchange::IsChart( aObjClsId ) )
236                     {   // yes, it's a chart diagram
237                         mrObjMgr.AddObj( new XclExpChartObj( mrObjMgr, rxShape, pChildAnchor ) );
238                         pCurrXclObj = NULL;     // no metafile or whatsoever
239                     }
240                     else    // metafile and OLE object
241                         pCurrXclObj = new XclObjOle( mrObjMgr, *pObj );
242                 }
243                 else    // just a metafile
244                     pCurrXclObj = new XclObjAny( mrObjMgr );
245             }
246             else
247                 pCurrXclObj = new XclObjAny( mrObjMgr );
248         }
249         else if( nObjType == OBJ_UNO )
250         {
251             //added for exporting OCX control
252             Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
253             Any aAny;
254             try{
255                 aAny = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("ControlTypeinMSO"));
256             }catch(...)
257             {
258                 OSL_TRACE("XclEscherEx::StartShape, this control can't get the property ControlTypeinMSO!");
259             }
260             aAny >>= nMsCtlType;
261 
262             if( nMsCtlType == 2 )  //OCX Form Control
263                 pCurrXclObj = CreateOCXCtrlObj( rxShape, pChildAnchor );
264             else  //TBX Form Control
265                 pCurrXclObj = CreateTBXCtrlObj( rxShape, pChildAnchor );
266             if( !pCurrXclObj )
267                 pCurrXclObj = new XclObjAny( mrObjMgr );   // just a metafile
268         }
269         else if( !ScDrawLayer::IsNoteCaption( pObj ) )
270         {
271             // #107540# ignore permanent note shapes
272             // #i12190# do not ignore callouts (do not filter by object type ID)
273             pCurrXclObj = new XclObjAny( mrObjMgr );   // just a metafile
274         }
275 	}
276 	if ( pCurrXclObj )
277 	{
278         if ( !mrObjMgr.AddObj( pCurrXclObj ) )
279 		{	// maximum count reached, object got deleted
280 			pCurrXclObj = NULL;
281 		}
282 		else
283 		{
284 			pCurrAppData->SetClientData( pTheClientData );
285 			if ( nAdditionalText == 0 )
286 			{
287 				if ( pObj )
288 				{
289 					if ( !bInGroup )
290                     {
291                         /*  Create a dummy anchor carrying the flags. Real
292                             coordinates are calculated later in virtual call of
293                             WriteData(EscherEx&,const Rectangle&). */
294                         XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
295                         pAnchor->SetFlags( *pObj );
296                         pCurrAppData->SetClientAnchor( pAnchor );
297                     }
298 					const SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, pObj );
299                     if( pTextObj && !lcl_IsFontwork( pTextObj ) && (pObj->GetObjIdentifier() != OBJ_CAPTION) )
300 					{
301 						const OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject();
302 						if( pParaObj )
303 							pCurrAppData->SetClientTextbox(
304                                 new XclEscherClientTextbox( GetRoot(), *pTextObj, pCurrXclObj ) );
305 					}
306 				}
307 				else
308 				{
309 					if ( !bInGroup )
310                         pCurrAppData->SetClientAnchor( mrObjMgr.CreateDffAnchor() );
311 				}
312 			}
313 			else if ( nAdditionalText == 3 )
314 			{
315 				if ( pAdditionalText )
316 				{
317 					pAdditionalText->SetXclObj( pCurrXclObj );
318 					pCurrAppData->SetClientTextbox( pAdditionalText );
319 				}
320 			}
321 		}
322 	}
323 	//add  for exporting OCX control
324 	//for OCX control import from MS office file,we need keep the id value as MS office file.
325 	//GetOldRoot().pObjRecs->Add( pCurrXclObj ) statement has generated the id value as aoo obj id rule;
326 	//but we trick it here.
327 	sal_uInt16 nObjType = pObj->GetObjIdentifier();
328 	if( nObjType == OBJ_UNO && pCurrXclObj )
329 	{
330 		Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
331 		Any aAny;
332 		try{
333 			aAny = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("ObjIDinMSO"));
334 		}catch(...)
335 		{
336 			OSL_TRACE("XclEscherEx::StartShape, this control can't get the property ObjIDinMSO!");
337 		}
338 		sal_uInt16 nObjIDinMSO = 0xFFFF;
339 		aAny >>= nObjIDinMSO;
340 		if( nObjIDinMSO != 0xFFFF && nMsCtlType == 2)  //OCX
341 		{
342 			pCurrXclObj->SetId(nObjIDinMSO);
343 		}
344 	}
345 	if ( !pCurrXclObj )
346 		pCurrAppData->SetDontWriteShape( sal_True );
347 	return pCurrAppData;
348 }
349 
350 
EndShape(sal_uInt16 nShapeType,sal_uInt32 nShapeID)351 void XclEscherEx::EndShape( sal_uInt16 nShapeType, sal_uInt32 nShapeID )
352 {
353     // own escher data created? -> never delete such objects
354     bool bOwnEscher = pCurrXclObj && pCurrXclObj->IsOwnEscher();
355 
356     // post process the current object - not for objects with own escher data
357     if( pCurrXclObj && !bOwnEscher )
358     {
359         // escher data of last shape not written? -> delete it from object list
360         if( nShapeID == 0 )
361         {
362             XclObj* pLastObj = mrObjMgr.RemoveLastObj();
363             DBG_ASSERT( pLastObj == pCurrXclObj, "XclEscherEx::EndShape - wrong object" );
364             DELETEZ( pLastObj );
365             pCurrXclObj = 0;
366         }
367 
368         if( pCurrXclObj )
369         {
370             // set shape type
371             if ( pCurrAppData->IsStackedGroup() )
372                 pCurrXclObj->SetEscherShapeTypeGroup();
373             else
374             {
375                 pCurrXclObj->SetEscherShapeType( nShapeType );
376                 UpdateDffFragmentEnd();
377             }
378         }
379     }
380 
381     // get next object from stack
382     DeleteCurrAppData();
383     pCurrAppData = static_cast< XclEscherHostAppData* >( aStack.Pop() );
384     pCurrXclObj = static_cast< XclObj* >( aStack.Pop() );
385     if( nAdditionalText == 3 )
386         nAdditionalText = 0;
387 }
388 
389 
EnterAdditionalTextGroup()390 EscherExHostAppData* XclEscherEx::EnterAdditionalTextGroup()
391 {
392 	nAdditionalText = 1;
393 	pAdditionalText = (XclEscherClientTextbox*) pCurrAppData->GetClientTextbox();
394 	pCurrAppData->SetClientTextbox( NULL );
395 	return pCurrAppData;
396 }
397 
398 
EndDocument()399 void XclEscherEx::EndDocument()
400 {
401     if( mbIsRootDff )
402         Flush( static_cast< XclEscherExGlobal& >( *mxGlobal ).GetPictureStream() );
403 
404     // seek back DFF stream to prepare saving the MSODRAWING[GROUP] records
405     mpOutStrm->Seek( 0 );
406 }
407 
408 //delete for exporting OCX
409 //#if EXC_EXP_OCX_CTRL
410 
CreateOCXCtrlObj(Reference<XShape> xShape,const Rectangle * pChildAnchor)411 XclExpOcxControlObj* XclEscherEx::CreateOCXCtrlObj( Reference< XShape > xShape, const Rectangle* pChildAnchor )
412 {
413     ::std::auto_ptr< XclExpOcxControlObj > xOcxCtrl;
414 
415     Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
416     if( xCtrlModel.is() )
417     {
418         // output stream
419         if( !mxCtlsStrm.Is() )
420             mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
421         if( mxCtlsStrm.Is() )
422         {
423             String aClassName;
424             sal_uInt32 nStrmStart = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() );
425 
426             // writes from xCtrlModel into mxCtlsStrm, raw class name returned in aClassName
427             if( SvxMSConvertOCXControls::WriteOCXExcelKludgeStream( mxCtlsStrm, xCtrlModel, xShape->getSize(), aClassName ) )
428             {
429                 sal_uInt32 nStrmSize = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() - nStrmStart );
430                 // adjust the class name to "Forms.***.1"
431                 aClassName.InsertAscii( "Forms.", 0 ).AppendAscii( ".1" );
432                 xOcxCtrl.reset( new XclExpOcxControlObj( mrObjMgr, xShape, pChildAnchor, aClassName, nStrmStart, nStrmSize ) );
433             }
434         }
435     }
436     return xOcxCtrl.release();
437 }
438 
439 //#else
440 
CreateTBXCtrlObj(Reference<XShape> xShape,const Rectangle * pChildAnchor)441 XclExpTbxControlObj* XclEscherEx::CreateTBXCtrlObj( Reference< XShape > xShape, const Rectangle* pChildAnchor )
442 {
443     ::std::auto_ptr< XclExpTbxControlObj > xTbxCtrl( new XclExpTbxControlObj( mrObjMgr, xShape, pChildAnchor ) );
444     if( xTbxCtrl->GetObjType() == EXC_OBJTYPE_UNKNOWN )
445         xTbxCtrl.reset();
446 
447     if( xTbxCtrl.get() )
448     {
449         // find attached macro
450         Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
451         ConvertTbxMacro( *xTbxCtrl, xCtrlModel );
452     }
453     return xTbxCtrl.release();
454 }
455 
ConvertTbxMacro(XclExpTbxControlObj & rTbxCtrlObj,Reference<XControlModel> xCtrlModel)456 void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj& rTbxCtrlObj, Reference< XControlModel > xCtrlModel )
457 {
458     SdrPage* pSdrPage = GetSdrPage( GetCurrScTab() );
459     if( xCtrlModel.is() && GetDocShell() && pSdrPage ) try
460     {
461         Reference< XFormsSupplier > xFormsSupplier( pSdrPage->getUnoPage(), UNO_QUERY_THROW );
462         Reference< XIndexAccess > xFormsIA( xFormsSupplier->getForms(), UNO_QUERY_THROW );
463 
464         // 1) try to find the index of the processed control in the form
465 
466         Reference< XIndexAccess > xFormIA;  // needed in step 2) below
467         sal_Int32 nFoundIdx = -1;
468 
469         // search all existing forms in the draw page
470         for( sal_Int32 nFormIdx = 0, nFormCount = xFormsIA->getCount();
471                 (nFoundIdx < 0) && (nFormIdx < nFormCount); ++nFormIdx )
472         {
473             // get the XIndexAccess interface of the form with index nFormIdx
474             if( xFormIA.set( xFormsIA->getByIndex( nFormIdx ), UNO_QUERY ) )
475             {
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 )
479                 {
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;
484                 }
485             }
486         }
487 
488         // 2) try to find an attached macro
489 
490         if( xFormIA.is() && (nFoundIdx >= 0) )
491         {
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 ) );
495             bool bFound = false;
496             for( sal_Int32 nEventIdx = 0, nEventCount = aEventSeq.getLength();
497                     !bFound && (nEventIdx < nEventCount); ++nEventIdx )
498             {
499                 // try to set the event data at the Excel control object, returns true on success
500                 bFound = rTbxCtrlObj.SetMacroLink( aEventSeq[ nEventIdx ] );
501             }
502         }
503     }
504     catch( Exception& )
505     {
506     }
507 }
508 
509 //#endif
510 
DeleteCurrAppData()511 void XclEscherEx::DeleteCurrAppData()
512 {
513     if ( pCurrAppData )
514     {
515         delete pCurrAppData->GetClientAnchor();
516 //      delete pCurrAppData->GetClientData();
517         delete pCurrAppData->GetClientTextbox();
518         delete pCurrAppData;
519     }
520 }
521 
522 // ============================================================================
523 
524 // --- class XclEscherClientData -------------------------------------
525 
WriteData(EscherEx & rEx) const526 void XclEscherClientData::WriteData( EscherEx& rEx ) const
527 {	// actual data is in the following OBJ record
528 	rEx.AddAtom( 0, ESCHER_ClientData );
529 }
530 
531 
532 // --- class XclEscherClientTextbox -------------------------------------
533 
XclEscherClientTextbox(const XclExpRoot & rRoot,const SdrTextObj & rObj,XclObj * pObj)534 XclEscherClientTextbox::XclEscherClientTextbox( const XclExpRoot& rRoot,
535 			const SdrTextObj& rObj, XclObj* pObj )
536 		:
537         XclExpRoot( rRoot ),
538 		rTextObj( rObj ),
539 		pXclObj( pObj )
540 {
541 }
542 
543 
WriteData(EscherEx &) const544 void XclEscherClientTextbox::WriteData( EscherEx& /*rEx*/ ) const
545 {
546     pXclObj->SetText( GetRoot(), rTextObj );
547 }
548 
549 
550