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_sc.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