diff options
Diffstat (limited to 'sc/source/filter/excel/xeescher.cxx')
-rw-r--r-- | sc/source/filter/excel/xeescher.cxx | 2046 |
1 files changed, 2046 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xeescher.cxx b/sc/source/filter/excel/xeescher.cxx new file mode 100644 index 000000000..d166b172a --- /dev/null +++ b/sc/source/filter/excel/xeescher.cxx @@ -0,0 +1,2046 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <xeescher.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> + +#include <set> +#include <vcl/BitmapReadAccess.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdocapt.hxx> +#include <editeng/outlobj.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <svtools/embedhlp.hxx> + +#include <unonames.hxx> +#include <convuno.hxx> +#include <postit.hxx> + +#include <fapihelper.hxx> +#include <xcl97esc.hxx> +#include <xechart.hxx> +#include <xeformula.hxx> +#include <xehelper.hxx> +#include <xelink.hxx> +#include <xename.hxx> +#include <xestyle.hxx> +#include <xllink.hxx> +#include <xltools.hxx> +#include <userdat.hxx> +#include <drwlayer.hxx> +#include <svl/itemset.hxx> +#include <svx/sdtaitm.hxx> +#include <document.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> + +#include <comphelper/sequence.hxx> +#include <oox/token/tokens.hxx> +#include <oox/token/relationship.hxx> +#include <oox/export/drawingml.hxx> +#include <oox/export/chartexport.hxx> +#include <oox/export/utils.hxx> +#include <oox/token/namespaces.hxx> +#include <oox/export/vmlexport.hxx> +#include <memory> + +using namespace com::sun::star; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::XServiceInfo; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XShapes; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::awt::XControlModel; +using ::com::sun::star::form::binding::XBindableValue; +using ::com::sun::star::form::binding::XListEntrySink; +using ::com::sun::star::script::ScriptEventDescriptor; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::oox::drawingml::DrawingML; +using ::oox::drawingml::ChartExport; +using namespace oox; + +namespace +{ + +const char *ToHorizAlign( SdrTextHorzAdjust eAdjust ) +{ + switch( eAdjust ) + { + case SDRTEXTHORZADJUST_CENTER: + return "center"; + case SDRTEXTHORZADJUST_RIGHT: + return "right"; + case SDRTEXTHORZADJUST_BLOCK: + return "justify"; + case SDRTEXTHORZADJUST_LEFT: + default: + return "left"; + } +} + +const char *ToVertAlign( SdrTextVertAdjust eAdjust ) +{ + switch( eAdjust ) + { + case SDRTEXTVERTADJUST_CENTER: + return "center"; + case SDRTEXTVERTADJUST_BOTTOM: + return "bottom"; + case SDRTEXTVERTADJUST_BLOCK: + return "justify"; + case SDRTEXTVERTADJUST_TOP: + default: + return "top"; + } +} + +void lcl_WriteAnchorVertex( sax_fastparser::FSHelperPtr const & rComments, const tools::Rectangle &aRect ) +{ + rComments->startElement(FSNS(XML_xdr, XML_col)); + rComments->writeEscaped( OUString::number( aRect.Left() ) ); + rComments->endElement( FSNS( XML_xdr, XML_col ) ); + rComments->startElement(FSNS(XML_xdr, XML_colOff)); + rComments->writeEscaped( OUString::number( aRect.Top() ) ); + rComments->endElement( FSNS( XML_xdr, XML_colOff ) ); + rComments->startElement(FSNS(XML_xdr, XML_row)); + rComments->writeEscaped( OUString::number( aRect.Right() ) ); + rComments->endElement( FSNS( XML_xdr, XML_row ) ); + rComments->startElement(FSNS(XML_xdr, XML_rowOff)); + rComments->writeEscaped( OUString::number( aRect.Bottom() ) ); + rComments->endElement( FSNS( XML_xdr, XML_rowOff ) ); +} + +tools::Long lcl_hmm2output(tools::Long value, bool bInEMU) +{ + return o3tl::convert(value, o3tl::Length::mm100, bInEMU ? o3tl::Length::emu : o3tl::Length::px); +} + +void lcl_GetFromTo( const XclExpRoot& rRoot, const tools::Rectangle &aRect, sal_Int32 nTab, tools::Rectangle &aFrom, tools::Rectangle &aTo, bool bInEMU = false ) +{ + sal_Int32 nCol = 0, nRow = 0; + sal_Int32 nColOff = 0, nRowOff= 0; + + const bool bRTL = rRoot.GetDoc().IsNegativePage( nTab ); + if (!bRTL) + { + while(true) + { + tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab ); + if( r.Left() <= aRect.Left() ) + { + nCol++; + nColOff = aRect.Left() - r.Left(); + } + if( r.Top() <= aRect.Top() ) + { + nRow++; + nRowOff = aRect.Top() - r.Top(); + } + if( r.Left() > aRect.Left() && r.Top() > aRect.Top() ) + { + aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ), + nRow-1, lcl_hmm2output( nRowOff, bInEMU ) ); + break; + } + } + } + else + { + while(true) + { + tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab ); + if( r.Left() >= aRect.Left() ) + { + nCol++; + nColOff = r.Left() - aRect.Left(); + } + if( r.Top() <= aRect.Top() ) + { + nRow++; + nRowOff = aRect.Top() - r.Top(); + } + if( r.Left() < aRect.Left() && r.Top() > aRect.Top() ) + { + aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ), + nRow-1, lcl_hmm2output( nRowOff, bInEMU ) ); + break; + } + } + } + if (!bRTL) + { + while(true) + { + tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab ); + if( r.Right() < aRect.Right() ) + nCol++; + if( r.Bottom() < aRect.Bottom() ) + nRow++; + if( r.Right() >= aRect.Right() && r.Bottom() >= aRect.Bottom() ) + { + aTo = tools::Rectangle( nCol, lcl_hmm2output( aRect.Right() - r.Left(), bInEMU ), + nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU )); + break; + } + } + } + else + { + while(true) + { + tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab ); + if( r.Right() >= aRect.Right() ) + nCol++; + if( r.Bottom() < aRect.Bottom() ) + nRow++; + if( r.Right() < aRect.Right() && r.Bottom() >= aRect.Bottom() ) + { + aTo = tools::Rectangle( nCol, lcl_hmm2output( r.Left() - aRect.Right(), bInEMU ), + nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU )); + break; + } + } + } +} + +} // namespace + +// Escher client anchor ======================================================= + +XclExpDffAnchorBase::XclExpDffAnchorBase( const XclExpRoot& rRoot, sal_uInt16 nFlags ) : + XclExpRoot( rRoot ), + mnFlags( nFlags ) +{ +} + +void XclExpDffAnchorBase::SetFlags( const SdrObject& rSdrObj ) +{ + ImplSetFlags( rSdrObj ); +} + +void XclExpDffAnchorBase::SetSdrObject( const SdrObject& rSdrObj ) +{ + ImplSetFlags( rSdrObj ); + ImplCalcAnchorRect( rSdrObj.GetCurrentBoundRect(), MapUnit::Map100thMM ); +} + +void XclExpDffAnchorBase::WriteDffData( EscherEx& rEscherEx ) const +{ + rEscherEx.AddAtom( 18, ESCHER_ClientAnchor ); + rEscherEx.GetStream().WriteUInt16( mnFlags ); + WriteXclObjAnchor( rEscherEx.GetStream(), maAnchor ); +} + +void XclExpDffAnchorBase::WriteData( EscherEx& rEscherEx, const tools::Rectangle& rRect ) +{ + // the passed rectangle is in twips + ImplCalcAnchorRect( rRect, MapUnit::MapTwip ); + WriteDffData( rEscherEx ); +} + +void XclExpDffAnchorBase::ImplSetFlags( const SdrObject& ) +{ + OSL_FAIL( "XclExpDffAnchorBase::ImplSetFlags - not implemented" ); +} + +void XclExpDffAnchorBase::ImplCalcAnchorRect( const tools::Rectangle&, MapUnit ) +{ + OSL_FAIL( "XclExpDffAnchorBase::ImplCalcAnchorRect - not implemented" ); +} + +XclExpDffSheetAnchor::XclExpDffSheetAnchor( const XclExpRoot& rRoot ) : + XclExpDffAnchorBase( rRoot ), + mnScTab( rRoot.GetCurrScTab() ) +{ +} + +void XclExpDffSheetAnchor::ImplSetFlags( const SdrObject& rSdrObj ) +{ + // set flags for cell/page anchoring + if ( ScDrawLayer::GetAnchorType( rSdrObj ) == SCA_CELL ) + mnFlags = 0; + else + mnFlags = EXC_ESC_ANCHOR_LOCKED; +} + +void XclExpDffSheetAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit ) +{ + maAnchor.SetRect( GetRoot(), mnScTab, rRect, eMapUnit ); +} + +XclExpDffEmbeddedAnchor::XclExpDffEmbeddedAnchor( const XclExpRoot& rRoot, + const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) : + XclExpDffAnchorBase( rRoot ), + maPageSize( rPageSize ), + mnScaleX( nScaleX ), + mnScaleY( nScaleY ) +{ +} + +void XclExpDffEmbeddedAnchor::ImplSetFlags( const SdrObject& /*rSdrObj*/ ) +{ + // TODO (unsupported feature): fixed size +} + +void XclExpDffEmbeddedAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit ) +{ + maAnchor.SetRect( maPageSize, mnScaleX, mnScaleY, rRect, eMapUnit ); +} + +XclExpDffNoteAnchor::XclExpDffNoteAnchor( const XclExpRoot& rRoot, const tools::Rectangle& rRect ) : + XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_SIZELOCKED ) +{ + maAnchor.SetRect( rRoot, rRoot.GetCurrScTab(), rRect, MapUnit::Map100thMM ); +} + +XclExpDffDropDownAnchor::XclExpDffDropDownAnchor( const XclExpRoot& rRoot, const ScAddress& rScPos ) : + XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_POSLOCKED ) +{ + GetAddressConverter().ConvertAddress( maAnchor.maFirst, rScPos, true ); + maAnchor.maLast.mnCol = maAnchor.maFirst.mnCol + 1; + maAnchor.maLast.mnRow = maAnchor.maFirst.mnRow + 1; + maAnchor.mnLX = maAnchor.mnTY = maAnchor.mnRX = maAnchor.mnBY = 0; +} + +// MSODRAWING* records ======================================================== + +XclExpMsoDrawingBase::XclExpMsoDrawingBase( XclEscherEx& rEscherEx, sal_uInt16 nRecId ) : + XclExpRecord( nRecId ), + mrEscherEx( rEscherEx ), + mnFragmentKey( rEscherEx.InitNextDffFragment() ) +{ +} + +void XclExpMsoDrawingBase::WriteBody( XclExpStream& rStrm ) +{ + OSL_ENSURE( mrEscherEx.GetStreamPos() == mrEscherEx.GetDffFragmentPos( mnFragmentKey ), + "XclExpMsoDrawingBase::WriteBody - DFF stream position mismatch" ); + rStrm.CopyFromStream( mrEscherEx.GetStream(), mrEscherEx.GetDffFragmentSize( mnFragmentKey ) ); +} + +XclExpMsoDrawingGroup::XclExpMsoDrawingGroup( XclEscherEx& rEscherEx ) : + XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWINGGROUP ) +{ + SvStream& rDffStrm = mrEscherEx.GetStream(); + + // write the DGGCONTAINER with some default settings + mrEscherEx.OpenContainer( ESCHER_DggContainer ); + + // TODO: stuff the OPT atom with our own document defaults? + static const sal_uInt8 spnDffOpt[] = { + 0xBF, 0x00, 0x08, 0x00, 0x08, 0x00, 0x81, 0x01, + 0x09, 0x00, 0x00, 0x08, 0xC0, 0x01, 0x40, 0x00, + 0x00, 0x08 + }; + mrEscherEx.AddAtom( sizeof( spnDffOpt ), ESCHER_OPT, 3, 3 ); + rDffStrm.WriteBytes(spnDffOpt, sizeof(spnDffOpt)); + + // SPLITMENUCOLORS contains colors in toolbar + static const sal_uInt8 spnDffSplitMenuColors[] = { + 0x0D, 0x00, 0x00, 0x08, 0x0C, 0x00, 0x00, 0x08, + 0x17, 0x00, 0x00, 0x08, 0xF7, 0x00, 0x00, 0x10 + }; + mrEscherEx.AddAtom( sizeof( spnDffSplitMenuColors ), ESCHER_SplitMenuColors, 0, 4 ); + rDffStrm.WriteBytes(spnDffSplitMenuColors, sizeof(spnDffSplitMenuColors)); + + // close the DGGCONTAINER + mrEscherEx.CloseContainer(); + mrEscherEx.UpdateDffFragmentEnd(); +} + +XclExpMsoDrawing::XclExpMsoDrawing( XclEscherEx& rEscherEx ) : + XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWING ) +{ +} + +XclExpImgData::XclExpImgData( const Graphic& rGraphic, sal_uInt16 nRecId ) : + maGraphic( rGraphic ), + mnRecId( nRecId ) +{ +} + +void XclExpImgData::Save( XclExpStream& rStrm ) +{ + Bitmap aBmp = maGraphic.GetBitmapEx().GetBitmap(); + if (aBmp.getPixelFormat() != vcl::PixelFormat::N24_BPP) + aBmp.Convert( BmpConversion::N24Bit ); + + Bitmap::ScopedReadAccess pAccess(aBmp); + if( !pAccess ) + return; + + sal_Int32 nWidth = ::std::min< sal_Int32 >( pAccess->Width(), 0xFFFF ); + sal_Int32 nHeight = ::std::min< sal_Int32 >( pAccess->Height(), 0xFFFF ); + if( (nWidth <= 0) || (nHeight <= 0) ) + return; + + sal_uInt8 nPadding = static_cast< sal_uInt8 >( nWidth & 0x03 ); + sal_uInt32 nTmpSize = static_cast< sal_uInt32 >( (nWidth * 3 + nPadding) * nHeight + 12 ); + + rStrm.StartRecord( mnRecId, nTmpSize + 4 ); + + rStrm << EXC_IMGDATA_BMP // BMP format + << EXC_IMGDATA_WIN // Windows + << nTmpSize // size after _this_ field + << sal_uInt32( 12 ) // BITMAPCOREHEADER size + << static_cast< sal_uInt16 >( nWidth ) // width + << static_cast< sal_uInt16 >( nHeight ) // height + << sal_uInt16( 1 ) // planes + << sal_uInt16( 24 ); // bits per pixel + + for( sal_Int32 nY = nHeight - 1; nY >= 0; --nY ) + { + Scanline pScanline = pAccess->GetScanline( nY ); + for( sal_Int32 nX = 0; nX < nWidth; ++nX ) + { + const BitmapColor& rBmpColor = pAccess->GetPixelFromData( pScanline, nX ); + rStrm << rBmpColor.GetBlue() << rBmpColor.GetGreen() << rBmpColor.GetRed(); + } + rStrm.WriteZeroBytes( nPadding ); + } + + rStrm.EndRecord(); +} + +void XclExpImgData::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetCurrentStream(); + + DrawingML aDML(pWorksheet, &rStrm, drawingml::DOCUMENT_XLSX); + OUString rId = aDML.WriteImage( maGraphic ); + pWorksheet->singleElement(XML_picture, FSNS(XML_r, XML_id), rId); +} + +XclExpControlHelper::XclExpControlHelper( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mnEntryCount( 0 ) +{ +} + +XclExpControlHelper::~XclExpControlHelper() +{ +} + +void XclExpControlHelper::ConvertSheetLinks( Reference< XShape > const & xShape ) +{ + mxCellLink.reset(); + mxCellLinkAddress.SetInvalid(); + mxSrcRange.reset(); + mnEntryCount = 0; + + // get control model + Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape ); + if( !xCtrlModel.is() ) + return; + + // *** cell link *** ------------------------------------------------------ + + Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY ); + if( xBindable.is() ) + { + Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY ); + if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) ) + { + ScfPropertySet aBindProp( xServInfo ); + CellAddress aApiAddress; + if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) ) + { + ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress ); + if( GetTabInfo().IsExportTab( mxCellLinkAddress.Tab() ) ) + mxCellLink = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, mxCellLinkAddress ); + } + } + } + + // *** source range *** --------------------------------------------------- + + Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY ); + if( !xEntrySink.is() ) + return; + + Reference< XServiceInfo > xServInfo( xEntrySink->getListEntrySource(), UNO_QUERY ); + if( !(xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_LISTSOURCE )) ) + return; + + ScfPropertySet aSinkProp( xServInfo ); + CellRangeAddress aApiRange; + if( aSinkProp.GetProperty( aApiRange, SC_UNONAME_CELLRANGE ) ) + { + ScRange aSrcRange; + ScUnoConversion::FillScRange( aSrcRange, aApiRange ); + if( (aSrcRange.aStart.Tab() == aSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( aSrcRange.aStart.Tab() ) ) + mxSrcRange = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, aSrcRange ); + mnEntryCount = static_cast< sal_uInt16 >( aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1 ); + } +} + +void XclExpControlHelper::WriteFormula( XclExpStream& rStrm, const XclTokenArray& rTokArr ) +{ + sal_uInt16 nFmlaSize = rTokArr.GetSize(); + rStrm << nFmlaSize << sal_uInt32( 0 ); + rTokArr.WriteArray( rStrm ); + if( nFmlaSize & 1 ) // pad to 16-bit + rStrm << sal_uInt8( 0 ); +} + +void XclExpControlHelper::WriteFormulaSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId, const XclTokenArray& rTokArr ) +{ + rStrm.StartRecord( nSubRecId, (rTokArr.GetSize() + 5) & ~1 ); + WriteFormula( rStrm, rTokArr ); + rStrm.EndRecord(); +} + +//delete for exporting OCX +//#if EXC_EXP_OCX_CTRL + +XclExpOcxControlObj::XclExpOcxControlObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape, + const tools::Rectangle* pChildAnchor, const OUString& rClassName, sal_uInt32 nStrmStart, sal_uInt32 nStrmSize ) : + XclObj( rObjMgr, EXC_OBJTYPE_PICTURE, true ), + XclExpControlHelper( rObjMgr.GetRoot() ), + maClassName( rClassName ), + mnStrmStart( nStrmStart ), + mnStrmSize( nStrmSize ) +{ + ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) ); + + // OBJ record flags + SetLocked( true ); + SetPrintable( aCtrlProp.GetBoolProperty( "Printable" ) ); + SetAutoFill( false ); + SetAutoLine( false ); + + // fill DFF property set + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, + ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | ShapeFlag::OLEShape ); + tools::Rectangle aDummyRect; + EscherPropertyContainer aPropOpt( mrEscherEx.GetGraphicProvider(), mrEscherEx.QueryPictureStream(), aDummyRect ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x08000040 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field + + // #i51348# name of the control, may overwrite shape name + OUString aCtrlName; + if( aCtrlProp.GetProperty( aCtrlName, "Name" ) && !aCtrlName.isEmpty() ) + aPropOpt.AddOpt( ESCHER_Prop_wzName, aCtrlName ); + + // meta file + //TODO - needs check + Reference< XPropertySet > xShapePS( xShape, UNO_QUERY ); + if( xShapePS.is() && aPropOpt.CreateGraphicProperties( xShapePS, "MetaFile", false ) ) + { + sal_uInt32 nBlipId; + if( aPropOpt.GetOpt( ESCHER_Prop_pib, nBlipId ) ) + aPropOpt.AddOpt( ESCHER_Prop_pictureId, nBlipId ); + } + + // write DFF property set to stream + aPropOpt.Commit( mrEscherEx.GetStream() ); + + // anchor + ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor ); + + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record + mrEscherEx.CloseContainer(); // ESCHER_SpContainer + mrEscherEx.UpdateDffFragmentEnd(); + + // spreadsheet links + ConvertSheetLinks( xShape ); +} + +void XclExpOcxControlObj::WriteSubRecs( XclExpStream& rStrm ) +{ + // OBJCF - clipboard format + rStrm.StartRecord( EXC_ID_OBJCF, 2 ); + rStrm << sal_uInt16( 2 ); + rStrm.EndRecord(); + + // OBJFLAGS + rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 ); + rStrm << sal_uInt16( 0x0031 ); + rStrm.EndRecord(); + + // OBJPICTFMLA + XclExpString aClass( maClassName ); + sal_uInt16 nClassNameSize = static_cast< sal_uInt16 >( aClass.GetSize() ); + sal_uInt16 nClassNamePad = nClassNameSize & 1; + sal_uInt16 nFirstPartSize = 12 + nClassNameSize + nClassNamePad; + + const XclTokenArray* pCellLink = GetCellLinkTokArr(); + sal_uInt16 nCellLinkSize = pCellLink ? ((pCellLink->GetSize() + 7) & 0xFFFE) : 0; + + const XclTokenArray* pSrcRange = GetSourceRangeTokArr(); + sal_uInt16 nSrcRangeSize = pSrcRange ? ((pSrcRange->GetSize() + 7) & 0xFFFE) : 0; + + sal_uInt16 nPictFmlaSize = nFirstPartSize + nCellLinkSize + nSrcRangeSize + 18; + rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nPictFmlaSize ); + + rStrm << nFirstPartSize // size of first part + << sal_uInt16( 5 ) // formula size + << sal_uInt32( 0 ) // unknown ID + << sal_uInt8( 0x02 ) << sal_uInt32( 0 ) // tTbl token with unknown ID + << sal_uInt8( 3 ) // pad to word + << aClass; // "Forms.***.1" + rStrm.WriteZeroBytes( nClassNamePad ); // pad to word + rStrm << mnStrmStart // start in 'Ctls' stream + << mnStrmSize // size in 'Ctls' stream + << sal_uInt32( 0 ); // class ID size + // cell link + rStrm << nCellLinkSize; + if( pCellLink ) + WriteFormula( rStrm, *pCellLink ); + // list source range + rStrm << nSrcRangeSize; + if( pSrcRange ) + WriteFormula( rStrm, *pSrcRange ); + + rStrm.EndRecord(); +} + +//#else + +XclExpTbxControlObj::XclExpTbxControlObj( XclExpObjectManager& rRoot, Reference< XShape > const & xShape , const tools::Rectangle* pChildAnchor ) : + XclObj( rRoot, EXC_OBJTYPE_UNKNOWN, true ), + XclMacroHelper( rRoot ), + mxShape( xShape ), + meEventType( EXC_TBX_EVENT_ACTION ), + mnHeight( 0 ), + mnState( 0 ), + mnLineCount( 0 ), + mnSelEntry( 0 ), + mnScrollValue( 0 ), + mnScrollMin( 0 ), + mnScrollMax( 100 ), + mnScrollStep( 1 ), + mnScrollPage( 10 ), + mbFlatButton( false ), + mbFlatBorder( false ), + mbMultiSel( false ), + mbScrollHor( false ), + mbPrint( false ), + mbVisible( false ), + mnShapeId( 0 ), + mrRoot(rRoot) +{ + namespace FormCompType = css::form::FormComponentType; + namespace AwtVisualEffect = css::awt::VisualEffect; + namespace AwtScrollOrient = css::awt::ScrollBarOrientation; + + ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) ); + if( !xShape.is() || !aCtrlProp.Is() ) + return; + + mnHeight = xShape->getSize().Height; + if( mnHeight <= 0 ) + return; + + // control type + sal_Int16 nClassId = 0; + if( aCtrlProp.GetProperty( nClassId, "ClassId" ) ) + { + switch( nClassId ) + { + case FormCompType::COMMANDBUTTON: mnObjType = EXC_OBJTYPE_BUTTON; meEventType = EXC_TBX_EVENT_ACTION; break; + case FormCompType::RADIOBUTTON: mnObjType = EXC_OBJTYPE_OPTIONBUTTON; meEventType = EXC_TBX_EVENT_ACTION; break; + case FormCompType::CHECKBOX: mnObjType = EXC_OBJTYPE_CHECKBOX; meEventType = EXC_TBX_EVENT_ACTION; break; + case FormCompType::LISTBOX: mnObjType = EXC_OBJTYPE_LISTBOX; meEventType = EXC_TBX_EVENT_CHANGE; break; + case FormCompType::COMBOBOX: mnObjType = EXC_OBJTYPE_DROPDOWN; meEventType = EXC_TBX_EVENT_CHANGE; break; + case FormCompType::GROUPBOX: mnObjType = EXC_OBJTYPE_GROUPBOX; meEventType = EXC_TBX_EVENT_MOUSE; break; + case FormCompType::FIXEDTEXT: mnObjType = EXC_OBJTYPE_LABEL; meEventType = EXC_TBX_EVENT_MOUSE; break; + case FormCompType::SCROLLBAR: mnObjType = EXC_OBJTYPE_SCROLLBAR; meEventType = EXC_TBX_EVENT_VALUE; break; + case FormCompType::SPINBUTTON: mnObjType = EXC_OBJTYPE_SPIN; meEventType = EXC_TBX_EVENT_VALUE; break; + } + } + if( mnObjType == EXC_OBJTYPE_UNKNOWN ) + return; + + // OBJ record flags + SetLocked( true ); + mbPrint = aCtrlProp.GetBoolProperty( "Printable" ); + SetPrintable( mbPrint ); + SetAutoFill( false ); + SetAutoLine( false ); + + // fill DFF property set + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + mbVisible = aCtrlProp.GetBoolProperty( "EnableVisible" ); + aPropOpt.AddOpt( ESCHER_Prop_fPrint, mbVisible ? 0x00080000 : 0x00080002 ); // visible flag + + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01000100 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // Text ID + aPropOpt.AddOpt( ESCHER_Prop_WrapText, 0x00000001 ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x001A0008 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00100000 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field + + // #i51348# name of the control, may overwrite shape name + if( aCtrlProp.GetProperty( msCtrlName, "Name" ) && !msCtrlName.isEmpty() ) + aPropOpt.AddOpt( ESCHER_Prop_wzName, msCtrlName ); + + //Export description as alt text + if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) ) + { + OUString aAltTxt; + OUString aDescrText = pSdrObj->GetDescription(); + if(!aDescrText.isEmpty()) + aAltTxt = aDescrText.copy( 0, std::min<sal_Int32>(MSPROP_DESCRIPTION_MAX_LEN, aDescrText.getLength()) ); + aPropOpt.AddOpt( ESCHER_Prop_wzDescription, aAltTxt ); + } + + // write DFF property set to stream + aPropOpt.Commit( mrEscherEx.GetStream() ); + + // anchor + ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor ); + + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record + mrEscherEx.UpdateDffFragmentEnd(); + + // control label + if( aCtrlProp.GetProperty( msLabel, "Label" ) ) + { + /* Be sure to construct the MSODRAWING record containing the + ClientTextbox atom after the base OBJ's MSODRAWING record data is + completed. */ + pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) ); + mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record + mrEscherEx.UpdateDffFragmentEnd(); + + sal_uInt16 nXclFont = EXC_FONT_APP; + if( !msLabel.isEmpty() ) + { + XclFontData aFontData; + GetFontPropSetHelper().ReadFontProperties( aFontData, aCtrlProp, EXC_FONTPROPSET_CONTROL ); + if( (!aFontData.maName.isEmpty() ) && (aFontData.mnHeight > 0) ) + nXclFont = GetFontBuffer().Insert( aFontData, EXC_COLOR_CTRLTEXT ); + } + + pTxo.reset( new XclTxo( msLabel, nXclFont ) ); + pTxo->SetHorAlign( (mnObjType == EXC_OBJTYPE_BUTTON) ? EXC_OBJ_HOR_CENTER : EXC_OBJ_HOR_LEFT ); + pTxo->SetVerAlign( EXC_OBJ_VER_CENTER ); + } + + mrEscherEx.CloseContainer(); // ESCHER_SpContainer + + // other properties + aCtrlProp.GetProperty( mnLineCount, "LineCount" ); + + // border style + sal_Int16 nApiButton = AwtVisualEffect::LOOK3D; + sal_Int16 nApiBorder = AwtVisualEffect::LOOK3D; + switch( nClassId ) + { + case FormCompType::LISTBOX: + case FormCompType::COMBOBOX: + aCtrlProp.GetProperty( nApiBorder, "Border" ); + break; + case FormCompType::CHECKBOX: + case FormCompType::RADIOBUTTON: + aCtrlProp.GetProperty( nApiButton, "VisualEffect" ); + nApiBorder = AwtVisualEffect::NONE; + break; + // Push button cannot be set to flat in Excel + case FormCompType::COMMANDBUTTON: + nApiBorder = AwtVisualEffect::LOOK3D; + break; + // Label does not support a border in Excel + case FormCompType::FIXEDTEXT: + nApiBorder = AwtVisualEffect::NONE; + break; + /* Scroll bar and spin button have a "Border" property, but it is + really used for a border, and not for own 3D/flat look (#i34712#). */ + case FormCompType::SCROLLBAR: + case FormCompType::SPINBUTTON: + nApiButton = AwtVisualEffect::LOOK3D; + nApiBorder = AwtVisualEffect::NONE; + break; + // Group box does not support flat style (#i34712#) + case FormCompType::GROUPBOX: + nApiBorder = AwtVisualEffect::LOOK3D; + break; + } + mbFlatButton = nApiButton != AwtVisualEffect::LOOK3D; + mbFlatBorder = nApiBorder != AwtVisualEffect::LOOK3D; + + // control state + sal_Int16 nApiState = 0; + if( aCtrlProp.GetProperty( nApiState, "State" ) ) + { + switch( nApiState ) + { + case 0: mnState = EXC_OBJ_CHECKBOX_UNCHECKED; break; + case 1: mnState = EXC_OBJ_CHECKBOX_CHECKED; break; + case 2: mnState = EXC_OBJ_CHECKBOX_TRISTATE; break; + } + } + + // special control contents + switch( nClassId ) + { + case FormCompType::LISTBOX: + { + mbMultiSel = aCtrlProp.GetBoolProperty( "MultiSelection" ); + Sequence< sal_Int16 > aSelection; + if( aCtrlProp.GetProperty( aSelection, "SelectedItems" ) ) + { + if( aSelection.hasElements() ) + { + mnSelEntry = aSelection[ 0 ] + 1; + comphelper::sequenceToContainer(maMultiSel, aSelection); + } + } + + // convert listbox with dropdown button to Excel dropdown + if( aCtrlProp.GetBoolProperty( "Dropdown" ) ) + mnObjType = EXC_OBJTYPE_DROPDOWN; + } + break; + + case FormCompType::COMBOBOX: + { + Sequence< OUString > aStringList; + OUString aDefText; + if( aCtrlProp.GetProperty( aStringList, "StringItemList" ) && + aCtrlProp.GetProperty( aDefText, "Text" ) && + aStringList.hasElements() && !aDefText.isEmpty() ) + { + auto nIndex = comphelper::findValue(aStringList, aDefText); + if( nIndex != -1 ) + mnSelEntry = static_cast< sal_Int16 >( nIndex + 1 ); // 1-based + if( mnSelEntry > 0 ) + maMultiSel.resize( 1, mnSelEntry - 1 ); + } + + // convert combobox without dropdown button to Excel listbox + if( !aCtrlProp.GetBoolProperty( "Dropdown" ) ) + mnObjType = EXC_OBJTYPE_LISTBOX; + } + break; + + case FormCompType::SCROLLBAR: + { + sal_Int32 nApiValue = 0; + if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMin" ) ) + mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMax" ) ) + mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, "ScrollValue" ) ) + mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax ); + if( aCtrlProp.GetProperty( nApiValue, "LineIncrement" ) ) + mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, "BlockIncrement" ) ) + mnScrollPage = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) ) + mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL; + } + break; + + case FormCompType::SPINBUTTON: + { + sal_Int32 nApiValue = 0; + if( aCtrlProp.GetProperty( nApiValue, "SpinValueMin" ) ) + mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, "SpinValueMax" ) ) + mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, "SpinValue" ) ) + mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax ); + if( aCtrlProp.GetProperty( nApiValue, "SpinIncrement" ) ) + mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) ) + mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL; + } + break; + } + + { + Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape ); + if( xCtrlModel.is() ) + { + Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY ); + if( xBindable.is() ) + { + Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY ); + if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) ) + { + ScfPropertySet aBindProp( xServInfo ); + CellAddress aApiAddress; + if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) ) + { + ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress ); + if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) ) + lcl_GetFromTo( rRoot, pSdrObj->GetLogicRect(), mxCellLinkAddress.Tab(), maAreaFrom, maAreaTo, true ); + } + } + } + } + } + + // spreadsheet links + ConvertSheetLinks( xShape ); +} + +bool XclExpTbxControlObj::SetMacroLink( const ScriptEventDescriptor& rEvent ) +{ + return XclMacroHelper::SetMacroLink( rEvent, meEventType ); +} + +void XclExpTbxControlObj::WriteSubRecs( XclExpStream& rStrm ) +{ + switch( mnObjType ) + { + // *** Push buttons, labels *** + + case EXC_OBJTYPE_BUTTON: + case EXC_OBJTYPE_LABEL: + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + break; + + // *** Check boxes, option buttons *** + + case EXC_OBJTYPE_CHECKBOX: + case EXC_OBJTYPE_OPTIONBUTTON: + { + // ftCbls - box properties + sal_uInt16 nStyle = 0; + ::set_flag( nStyle, EXC_OBJ_CHECKBOX_FLAT, mbFlatButton ); + + rStrm.StartRecord( EXC_ID_OBJCBLS, 12 ); + rStrm << mnState; + rStrm.WriteZeroBytes( 8 ); + rStrm << nStyle; + rStrm.EndRecord(); + + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + // ftCblsFmla subrecord - cell link + WriteCellLinkSubRec( rStrm, EXC_ID_OBJCBLSFMLA ); + + // ftCblsData subrecord - box properties, again + rStrm.StartRecord( EXC_ID_OBJCBLS, 8 ); + rStrm << mnState; + rStrm.WriteZeroBytes( 4 ); + rStrm << nStyle; + rStrm.EndRecord(); + } + break; + + // *** List boxes, combo boxes *** + + case EXC_OBJTYPE_LISTBOX: + case EXC_OBJTYPE_DROPDOWN: + { + sal_uInt16 nEntryCount = GetSourceEntryCount(); + + // ftSbs subrecord - Scroll bars + sal_Int32 nLineHeight = XclTools::GetHmmFromTwips( 200 ); // always 10pt + if( mnObjType == EXC_OBJTYPE_LISTBOX ) + mnLineCount = static_cast< sal_uInt16 >( mnHeight / nLineHeight ); + mnScrollValue = 0; + mnScrollMin = 0; + sal_uInt16 nInvisLines = (nEntryCount >= mnLineCount) ? (nEntryCount - mnLineCount) : 0; + mnScrollMax = limit_cast< sal_uInt16 >( nInvisLines, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + mnScrollStep = 1; + mnScrollPage = limit_cast< sal_uInt16 >( mnLineCount, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX ); + mbScrollHor = false; + WriteSbs( rStrm ); + + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + // ftSbsFmla subrecord - cell link + WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA ); + + // ftLbsData - source data range and box properties + sal_uInt16 nStyle = 0; + ::insert_value( nStyle, mbMultiSel ? EXC_OBJ_LISTBOX_MULTI : EXC_OBJ_LISTBOX_SINGLE, 4, 2 ); + ::set_flag( nStyle, EXC_OBJ_LISTBOX_FLAT, mbFlatBorder ); + + rStrm.StartRecord( EXC_ID_OBJLBSDATA, 0 ); + + if( const XclTokenArray* pSrcRange = GetSourceRangeTokArr() ) + { + rStrm << static_cast< sal_uInt16 >( (pSrcRange->GetSize() + 7) & 0xFFFE ); + WriteFormula( rStrm, *pSrcRange ); + } + else + rStrm << sal_uInt16( 0 ); + + rStrm << nEntryCount << mnSelEntry << nStyle << sal_uInt16( 0 ); + if( mnObjType == EXC_OBJTYPE_LISTBOX ) + { + if( nEntryCount ) + { + ScfUInt8Vec aSelEx( nEntryCount, 0 ); + for( const auto& rItem : maMultiSel ) + if( rItem < nEntryCount ) + aSelEx[ rItem ] = 1; + rStrm.Write( aSelEx.data(), aSelEx.size() ); + } + } + else if( mnObjType == EXC_OBJTYPE_DROPDOWN ) + { + rStrm << sal_uInt16( 0 ) << mnLineCount << sal_uInt16( 0 ) << sal_uInt16( 0 ); + } + + rStrm.EndRecord(); + } + break; + + // *** Spin buttons, scrollbars *** + + case EXC_OBJTYPE_SPIN: + case EXC_OBJTYPE_SCROLLBAR: + { + // ftSbs subrecord - scroll bars + WriteSbs( rStrm ); + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + // ftSbsFmla subrecord - cell link + WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA ); + } + break; + + // *** Group boxes *** + + case EXC_OBJTYPE_GROUPBOX: + { + // ftMacro - macro link + WriteMacroSubRec( rStrm ); + + // ftGboData subrecord - group box properties + sal_uInt16 nStyle = 0; + ::set_flag( nStyle, EXC_OBJ_GROUPBOX_FLAT, mbFlatBorder ); + + rStrm.StartRecord( EXC_ID_OBJGBODATA, 6 ); + rStrm << sal_uInt32( 0 ) + << nStyle; + rStrm.EndRecord(); + } + break; + } +} + +void XclExpTbxControlObj::WriteCellLinkSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId ) +{ + if( const XclTokenArray* pCellLink = GetCellLinkTokArr() ) + WriteFormulaSubRec( rStrm, nSubRecId, *pCellLink ); +} + +void XclExpTbxControlObj::WriteSbs( XclExpStream& rStrm ) +{ + sal_uInt16 nOrient = 0; + ::set_flag( nOrient, EXC_OBJ_SCROLLBAR_HOR, mbScrollHor ); + sal_uInt16 nStyle = EXC_OBJ_SCROLLBAR_DEFFLAGS; + ::set_flag( nStyle, EXC_OBJ_SCROLLBAR_FLAT, mbFlatButton ); + + rStrm.StartRecord( EXC_ID_OBJSBS, 20 ); + rStrm << sal_uInt32( 0 ) // reserved + << mnScrollValue // thumb position + << mnScrollMin // thumb min pos + << mnScrollMax // thumb max pos + << mnScrollStep // line increment + << mnScrollPage // page increment + << nOrient // 0 = vertical, 1 = horizontal + << sal_uInt16( 15 ) // thumb width + << nStyle; // flags/style + rStrm.EndRecord(); +} + +void XclExpTbxControlObj::setShapeId(sal_Int32 aShapeId) +{ + mnShapeId = aShapeId; +} + +namespace +{ +/// Handles the VML export of form controls (e.g. checkboxes). +class VmlFormControlExporter : public oox::vml::VMLExport +{ + sal_uInt16 m_nObjType; + tools::Rectangle m_aAreaFrom; + tools::Rectangle m_aAreaTo; + OUString m_aLabel; + OUString m_aMacroName; + +public: + VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p, sal_uInt16 nObjType, + const tools::Rectangle& rAreaFrom, const tools::Rectangle& rAreaTo, + const OUString& rLabel, const OUString& rMacroName); + +protected: + using VMLExport::StartShape; + sal_Int32 StartShape() override; + using VMLExport::EndShape; + void EndShape(sal_Int32 nShapeElement) override; +}; + +VmlFormControlExporter::VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p, + sal_uInt16 nObjType, + const tools::Rectangle& rAreaFrom, + const tools::Rectangle& rAreaTo, + const OUString& rLabel, const OUString& rMacroName) + : VMLExport(p) + , m_nObjType(nObjType) + , m_aAreaFrom(rAreaFrom) + , m_aAreaTo(rAreaTo) + , m_aLabel(rLabel) + , m_aMacroName(rMacroName) +{ +} + +sal_Int32 VmlFormControlExporter::StartShape() +{ + // Host control. + AddShapeAttribute(XML_type, "#_x0000_t201"); + return VMLExport::StartShape(); +} + +void VmlFormControlExporter::EndShape(sal_Int32 nShapeElement) +{ + sax_fastparser::FSHelperPtr pVmlDrawing = GetFS(); + + pVmlDrawing->startElement(FSNS(XML_v, XML_textbox)); + pVmlDrawing->startElement(XML_div); + pVmlDrawing->write(m_aLabel); + pVmlDrawing->endElement(XML_div); + pVmlDrawing->endElement(FSNS(XML_v, XML_textbox)); + + OString aObjectType; + switch (m_nObjType) + { + case EXC_OBJTYPE_CHECKBOX: + aObjectType = "Checkbox"; + break; + case EXC_OBJTYPE_BUTTON: + aObjectType = "Button"; + break; + } + pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, aObjectType); + OString aAnchor + = OString::number(m_aAreaFrom.Left()) + ", " + OString::number(m_aAreaFrom.Top()) + ", " + + OString::number(m_aAreaFrom.Right()) + ", " + OString::number(m_aAreaFrom.Bottom()) + ", " + + OString::number(m_aAreaTo.Left()) + ", " + OString::number(m_aAreaTo.Top()) + ", " + + OString::number(m_aAreaTo.Right()) + ", " + OString::number(m_aAreaTo.Bottom()); + XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_Anchor), aAnchor); + + if (!m_aMacroName.isEmpty()) + { + XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_FmlaMacro), m_aMacroName); + } + + // XclExpOcxControlObj::WriteSubRecs() has the same fixed values. + if (m_nObjType == EXC_OBJTYPE_BUTTON) + { + XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextHAlign), "Center"); + } + XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextVAlign), "Center"); + + pVmlDrawing->endElement(FSNS(XML_x, XML_ClientData)); + VMLExport::EndShape(nShapeElement); +} + +} + +/// Save into xl/drawings/vmlDrawing1.vml. +void XclExpTbxControlObj::SaveVml(XclExpXmlStream& rStrm) +{ + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape); + tools::Rectangle aAreaFrom; + tools::Rectangle aAreaTo; + // Unlike XclExpTbxControlObj::SaveXml(), this is not calculated in EMUs. + lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo); + VmlFormControlExporter aFormControlExporter(rStrm.GetCurrentStream(), GetObjType(), aAreaFrom, + aAreaTo, msLabel, GetMacroName()); + aFormControlExporter.AddSdrObject(*pObj, /*bIsFollowingTextFlow=*/false, /*eHOri=*/-1, + /*eVOri=*/-1, /*eHRel=*/-1, /*eVRel=*/-1, + /*pWrapAttrList=*/nullptr, /*bOOxmlExport=*/true); +} + +// save into xl\drawings\drawing1.xml +void XclExpTbxControlObj::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& pDrawing = rStrm.GetCurrentStream(); + + pDrawing->startElement(FSNS(XML_mc, XML_AlternateContent), + FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce))); + pDrawing->startElement(FSNS(XML_mc, XML_Choice), + FSNS(XML_xmlns, XML_a14), rStrm.getNamespaceURL(OOX_NS(a14)), + XML_Requires, "a14"); + + pDrawing->startElement(FSNS(XML_xdr, XML_twoCellAnchor), XML_editAs, "oneCell"); + { + pDrawing->startElement(FSNS(XML_xdr, XML_from)); + lcl_WriteAnchorVertex(pDrawing, maAreaFrom); + pDrawing->endElement(FSNS(XML_xdr, XML_from)); + pDrawing->startElement(FSNS(XML_xdr, XML_to)); + lcl_WriteAnchorVertex(pDrawing, maAreaTo); + pDrawing->endElement(FSNS(XML_xdr, XML_to)); + + pDrawing->startElement(FSNS(XML_xdr, XML_sp)); + { + // xdr:nvSpPr + pDrawing->startElement(FSNS(XML_xdr, XML_nvSpPr)); + { + pDrawing->singleElement(FSNS(XML_xdr, XML_cNvPr), + XML_id, OString::number(mnShapeId).getStr(), + XML_name, msCtrlName, // control name + XML_descr, msLabel, // description as alt text + XML_hidden, mbVisible ? "0" : "1"); + pDrawing->singleElement(FSNS(XML_xdr, XML_cNvSpPr)); + } + pDrawing->endElement(FSNS(XML_xdr, XML_nvSpPr)); + + // xdr:spPr + pDrawing->startElement(FSNS(XML_xdr, XML_spPr)); + { + // a:xfrm + pDrawing->startElement(FSNS(XML_a, XML_xfrm)); + { + pDrawing->singleElement(FSNS(XML_a, XML_off), + XML_x, "0", + XML_y, "0"); + pDrawing->singleElement(FSNS(XML_a, XML_ext), + XML_cx, "0", + XML_cy, "0"); + } + pDrawing->endElement(FSNS(XML_a, XML_xfrm)); + + // a:prstGeom + pDrawing->startElement(FSNS(XML_a, XML_prstGeom), XML_prst, "rect"); + { + pDrawing->singleElement(FSNS(XML_a, XML_avLst)); + } + pDrawing->endElement(FSNS(XML_a, XML_prstGeom)); + } + pDrawing->endElement(FSNS(XML_xdr, XML_spPr)); + + // xdr:txBody + { + pDrawing->startElement(FSNS(XML_xdr, XML_txBody)); + +#define DEFLRINS 254 +#define DEFTBINS 127 + sal_Int32 nLeft, nRight, nTop, nBottom; + nLeft = nRight = DEFLRINS; + nTop = nBottom = DEFTBINS; + + // top inset looks a bit different compared to ppt export + // check if something related doesn't work as expected + Reference< XPropertySet > rXPropSet(mxShape, UNO_QUERY); + + try + { + css::uno::Any mAny; + + mAny = rXPropSet->getPropertyValue("TextLeftDistance"); + if (mAny.hasValue()) + mAny >>= nLeft; + + mAny = rXPropSet->getPropertyValue("TextRightDistance"); + if (mAny.hasValue()) + mAny >>= nRight; + + mAny = rXPropSet->getPropertyValue("TextUpperDistance"); + if (mAny.hasValue()) + mAny >>= nTop; + + mAny = rXPropSet->getPropertyValue("TextLowerDistance"); + if (mAny.hasValue()) + mAny >>= nBottom; + } + catch (...) + { + } + + // Specifies the inset of the bounding rectangle. + // Insets are used just as internal margins for text boxes within shapes. + // If this attribute is omitted, then a value of 45720 or 0.05 inches is implied. + pDrawing->startElementNS(XML_a, XML_bodyPr, + XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), nLeft != DEFLRINS), + XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), nRight != DEFLRINS), + XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), nTop != DEFTBINS), + XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), nBottom != DEFTBINS), + XML_anchor, "ctr"); + + { + bool bTextAutoGrowHeight = false; + + try + { + css::uno::Any mAny; + + mAny = rXPropSet->getPropertyValue("TextAutoGrowHeight"); + if (mAny.hasValue()) + mAny >>= bTextAutoGrowHeight; + } + catch (...) + { + } + + pDrawing->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit)); + } + + pDrawing->endElementNS(XML_a, XML_bodyPr); + + { + pDrawing->startElementNS(XML_a, XML_p); + pDrawing->startElementNS(XML_a, XML_r); + pDrawing->startElementNS(XML_a, XML_t); + pDrawing->write(msLabel); + pDrawing->endElementNS(XML_a, XML_t); + pDrawing->endElementNS(XML_a, XML_r); + pDrawing->endElementNS(XML_a, XML_p); + } + + pDrawing->endElement(FSNS(XML_xdr, XML_txBody)); + } + } + pDrawing->endElement(FSNS(XML_xdr, XML_sp)); + pDrawing->singleElement(FSNS(XML_xdr, XML_clientData)); + } + pDrawing->endElement(FSNS(XML_xdr, XML_twoCellAnchor)); + pDrawing->endElement( FSNS( XML_mc, XML_Choice ) ); + pDrawing->endElement( FSNS( XML_mc, XML_AlternateContent ) ); +} + +// output into ctrlProp1.xml +OUString XclExpTbxControlObj::SaveControlPropertiesXml(XclExpXmlStream& rStrm) const +{ + OUString sIdFormControlPr; + + switch (mnObjType) + { + case EXC_OBJTYPE_CHECKBOX: + { + const sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId(); + sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName( "xl/", "ctrlProps/ctrlProps", nDrawing ), + XclXmlUtils::GetStreamName( "../", "ctrlProps/ctrlProps", nDrawing ), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.ms-excel.controlproperties+xml", + oox::getRelationship(Relationship::CTRLPROP), + &sIdFormControlPr ); + + rStrm.PushStream( pFormControl ); + // checkbox + // <formControlPr + // xmlns="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" + // objectType="CheckBox" checked="Checked" lockText="1" noThreeD="1"/> + // + pFormControl->write("<formControlPr xmlns=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\" objectType=\"CheckBox\""); + if (mnState == EXC_OBJ_CHECKBOX_CHECKED) + pFormControl->write(" checked=\"Checked\""); + + pFormControl->write(" autoLine=\"false\""); + + if (mbPrint) + pFormControl->write(" print=\"true\""); + else + pFormControl->write(" print=\"false\""); + + if (mxCellLinkAddress.IsValid()) + { + OUString aCellLink = mxCellLinkAddress.Format(ScRefFlags::ADDR_ABS, + &GetDoc(), + ScAddress::Details(::formula::FormulaGrammar::CONV_XL_A1)); + + // "Sheet1!$C$5" + pFormControl->write(" fmlaLink=\""); + if (aCellLink.indexOf('!') < 0) + { + pFormControl->write(GetTabInfo().GetScTabName(mxCellLinkAddress.Tab())); + pFormControl->write("!"); + } + pFormControl->write(aCellLink); + pFormControl->write("\""); + } + + pFormControl->write(" lockText=\"1\" noThreeD=\"1\"/>"); + rStrm.PopStream(); + + break; + } + case EXC_OBJTYPE_BUTTON: + { + sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId(); + sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName("xl/", "ctrlProps/ctrlProps", nDrawing), + XclXmlUtils::GetStreamName("../", "ctrlProps/ctrlProps", nDrawing), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.ms-excel.controlproperties+xml", + oox::getRelationship(Relationship::CTRLPROP), &sIdFormControlPr); + + pFormControl->singleElement(XML_formControlPr, XML_xmlns, + rStrm.getNamespaceURL(OOX_NS(xls14Lst)), XML_objectType, + "Button", XML_lockText, "1"); + break; + } + } + + return sIdFormControlPr; +} + +// output into sheet1.xml +void XclExpTbxControlObj::SaveSheetXml(XclExpXmlStream& rStrm, const OUString& aIdFormControlPr) const +{ + switch (mnObjType) + { + case EXC_OBJTYPE_CHECKBOX: + { + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + + rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent), + FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce))); + rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14"); + + rWorksheet->startElement( + XML_control, + XML_shapeId, OString::number(mnShapeId).getStr(), + FSNS(XML_r, XML_id), aIdFormControlPr, + XML_name, msLabel); // text to display with checkbox button + + rWorksheet->write("<controlPr defaultSize=\"0\" locked=\"1\" autoFill=\"0\" autoLine=\"0\" autoPict=\"0\""); + + if (mbPrint) + rWorksheet->write(" print=\"true\""); + else + rWorksheet->write(" print=\"false\""); + + if (!msCtrlName.isEmpty()) + { + rWorksheet->write(" altText=\""); + rWorksheet->write(msCtrlName); // alt text + rWorksheet->write("\""); + } + + rWorksheet->write(">"); + + rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells, "false"); + rWorksheet->startElement(XML_from); + lcl_WriteAnchorVertex(rWorksheet, maAreaFrom); + rWorksheet->endElement(XML_from); + rWorksheet->startElement(XML_to); + lcl_WriteAnchorVertex(rWorksheet, maAreaTo); + rWorksheet->endElement(XML_to); + rWorksheet->endElement( XML_anchor ); + + rWorksheet->write("</controlPr>"); + + rWorksheet->endElement(XML_control); + rWorksheet->endElement( FSNS( XML_mc, XML_Choice ) ); + rWorksheet->endElement( FSNS( XML_mc, XML_AlternateContent ) ); + + break; + } + case EXC_OBJTYPE_BUTTON: + { + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + + rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent), FSNS(XML_xmlns, XML_mc), + rStrm.getNamespaceURL(OOX_NS(mce))); + rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14"); + + rWorksheet->startElement(XML_control, XML_shapeId, OString::number(mnShapeId).getStr(), + FSNS(XML_r, XML_id), aIdFormControlPr, XML_name, msCtrlName); + + OString aMacroName = GetMacroName().toUtf8(); + // Omit the macro attribute if it would be empty. + const char* pMacroName = aMacroName.isEmpty() ? nullptr : aMacroName.getStr(); + rWorksheet->startElement(XML_controlPr, XML_defaultSize, "0", XML_print, + mbPrint ? "true" : "false", XML_autoFill, "0", XML_autoPict, + "0", XML_macro, pMacroName); + + rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells, + "false"); + + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape); + tools::Rectangle aAreaFrom; + tools::Rectangle aAreaTo; + lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo, + /*bInEMU=*/true); + + rWorksheet->startElement(XML_from); + lcl_WriteAnchorVertex(rWorksheet, aAreaFrom); + rWorksheet->endElement(XML_from); + rWorksheet->startElement(XML_to); + lcl_WriteAnchorVertex(rWorksheet, aAreaTo); + rWorksheet->endElement(XML_to); + rWorksheet->endElement(XML_anchor); + + rWorksheet->endElement(XML_controlPr); + + rWorksheet->endElement(XML_control); + rWorksheet->endElement(FSNS(XML_mc, XML_Choice)); + rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent)); + break; + } + } +} + +//#endif + +XclExpChartObj::XclExpChartObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor, ScDocument* pDoc ) : + XclObj( rObjMgr, EXC_OBJTYPE_CHART ), + XclExpRoot( rObjMgr.GetRoot() ), mxShape( xShape ), + mpDoc(pDoc) +{ + // create the MSODRAWING record contents for the chart object + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); + aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x0800004E ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x0800004D ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 ); + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x0800004D ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080008 ); + aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00020000 ); + aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080000 ); + aPropOpt.Commit( mrEscherEx.GetStream() ); + + // anchor + SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ); + ImplWriteAnchor( pSdrObj, pChildAnchor ); + + // client data (the following OBJ record) + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); + mrEscherEx.CloseContainer(); // ESCHER_SpContainer + mrEscherEx.UpdateDffFragmentEnd(); + + // load the chart OLE object + if( SdrOle2Obj* pSdrOleObj = dynamic_cast< SdrOle2Obj* >( pSdrObj ) ) + svt::EmbeddedObjectRef::TryRunningState( pSdrOleObj->GetObjRef() ); + + // create the chart substream object + ScfPropertySet aShapeProp( xShape ); + css::awt::Rectangle aBoundRect; + aShapeProp.GetProperty( aBoundRect, "BoundRect" ); + tools::Rectangle aChartRect( Point( aBoundRect.X, aBoundRect.Y ), Size( aBoundRect.Width, aBoundRect.Height ) ); + mxChart = std::make_shared<XclExpChart>(GetRoot(), GetChartDoc(), aChartRect); +} + +XclExpChartObj::~XclExpChartObj() +{ +} + +void XclExpChartObj::Save( XclExpStream& rStrm ) +{ + // content of OBJ record + XclObj::Save( rStrm ); + // chart substream + mxChart->Save( rStrm ); +} + +void XclExpChartObj::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream(); + + // FIXME: two cell? it seems the two cell anchor is incorrect. + pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor + XML_editAs, "oneCell" ); + Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY ); + if (xPropSet.is()) + { + XclObjAny::WriteFromTo( rStrm, mxShape, GetTab() ); + ChartExport aChartExport(XML_xdr, pDrawing, GetChartDoc(), &rStrm, drawingml::DOCUMENT_XLSX); + auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc); + aChartExport.SetURLTranslator(pURLTransformer); + static sal_Int32 nChartCount = 0; + nChartCount++; + sal_Int32 nID = rStrm.GetUniqueId(); + aChartExport.WriteChartObj( mxShape, nID, nChartCount ); + // TODO: get the correcto chart number + } + + pDrawing->singleElement( FSNS( XML_xdr, XML_clientData) + // OOXTODO: XML_fLocksWithSheet + // OOXTODO: XML_fPrintsWithSheet + ); + pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) ); +} + +css::uno::Reference<css::chart::XChartDocument> XclExpChartObj::GetChartDoc() const +{ + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape); + if (!pObj || pObj->GetObjIdentifier() != SdrObjKind::OLE2) + return {}; + // May load here - makes sure that we are working with actually loaded OLE object + return css::uno::Reference<css::chart::XChartDocument>( + static_cast<SdrOle2Obj*>(pObj)->getXModel(), css::uno::UNO_QUERY); +} + +XclExpNote::XclExpNote(const XclExpRoot& rRoot, const ScAddress& rScPos, + const ScPostIt* pScNote, std::u16string_view rAddText) + : XclExpRecord(EXC_ID_NOTE) + , mrRoot(rRoot) + , maScPos(rScPos) + , mnObjId(EXC_OBJ_INVALID_ID) + , mbVisible(pScNote && pScNote->IsCaptionShown()) + , meTHA(SDRTEXTHORZADJUST_LEFT) + , meTVA(SDRTEXTVERTADJUST_TOP) + , mbAutoScale(false) + , mbLocked(false) + , mbAutoFill(false) + , mbColHidden(false) + , mbRowHidden(false) +{ + // get the main note text + OUString aNoteText; + if( pScNote ) + { + aNoteText = pScNote->GetText(); + const EditTextObject *pEditObj = pScNote->GetEditTextObject(); + if( pEditObj ) + mpNoteContents = XclExpStringHelper::CreateString( rRoot, *pEditObj ); + } + // append additional text + aNoteText = ScGlobal::addToken( aNoteText, rAddText, '\n', 2 ); + + // initialize record dependent on BIFF type + switch( rRoot.GetBiff() ) + { + case EXC_BIFF5: + maNoteText = OUStringToOString(aNoteText, rRoot.GetTextEncoding()); + break; + + case EXC_BIFF8: + { + // TODO: additional text + if( pScNote ) + { + if( SdrCaptionObj* pCaption = pScNote->GetOrCreateCaption( maScPos ) ) + { + lcl_GetFromTo( rRoot, pCaption->GetLogicRect(), maScPos.Tab(), maCommentFrom, maCommentTo ); + if( const OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() ) + mnObjId = rRoot.GetObjectManager().AddObj( std::make_unique<XclObjComment>( rRoot.GetObjectManager(), pCaption->GetLogicRect(), pOPO->GetTextObject(), pCaption, mbVisible, maScPos, maCommentFrom, maCommentTo ) ); + + SfxItemSet aItemSet = pCaption->GetMergedItemSet(); + meTVA = pCaption->GetTextVerticalAdjust(); + meTHA = pCaption->GetTextHorizontalAdjust(); + mbAutoScale = pCaption->GetFitToSize() != drawing::TextFitToSizeType_NONE; + mbLocked = pCaption->IsMoveProtect() || pCaption->IsResizeProtect(); + + // AutoFill style would change if Postit.cxx object creation values are changed + OUString aCol(aItemSet.Get(XATTR_FILLCOLOR).GetValue()); + mbAutoFill = aCol.isEmpty() && (aItemSet.Get(XATTR_FILLSTYLE).GetValue() == drawing::FillStyle_SOLID); + mbRowHidden = (rRoot.GetDoc().RowHidden(maScPos.Row(),maScPos.Tab())); + mbColHidden = (rRoot.GetDoc().ColHidden(maScPos.Col(),maScPos.Tab())); + } + // stAuthor (variable): An XLUnicodeString that specifies the name of the comment + // author. String length MUST be greater than or equal to 1 and less than or equal + // to 54. + if( pScNote->GetAuthor().isEmpty() ) + maAuthor = XclExpString( " " ); + else + maAuthor = XclExpString( pScNote->GetAuthor(), XclStrFlags::NONE, 54 ); + } + + SetRecSize( 9 + maAuthor.GetSize() ); + } + break; + + default: DBG_ERROR_BIFF(); + } +} + +void XclExpNote::Save( XclExpStream& rStrm ) +{ + switch( rStrm.GetRoot().GetBiff() ) + { + case EXC_BIFF5: + { + // write the NOTE record directly, there may be the need to create more than one + const char* pcBuffer = maNoteText.getStr(); + sal_uInt16 nCharsLeft = static_cast< sal_uInt16 >( maNoteText.getLength() ); + + while( nCharsLeft ) + { + sal_uInt16 nWriteChars = ::std::min( nCharsLeft, EXC_NOTE5_MAXLEN ); + + rStrm.StartRecord( EXC_ID_NOTE, 6 + nWriteChars ); + if( pcBuffer == maNoteText.getStr() ) + { + // first record: row, col, length of complete text + rStrm << static_cast< sal_uInt16 >( maScPos.Row() ) + << static_cast< sal_uInt16 >( maScPos.Col() ) + << nCharsLeft; // still contains full length + } + else + { + // next records: -1, 0, length of current text segment + rStrm << sal_uInt16( 0xFFFF ) + << sal_uInt16( 0 ) + << nWriteChars; + } + rStrm.Write( pcBuffer, nWriteChars ); + rStrm.EndRecord(); + + pcBuffer += nWriteChars; + nCharsLeft = nCharsLeft - nWriteChars; + } + } + break; + + case EXC_BIFF8: + if( mnObjId != EXC_OBJ_INVALID_ID ) + XclExpRecord::Save( rStrm ); + break; + + default: DBG_ERROR_BIFF(); + } +} + +void XclExpNote::WriteBody( XclExpStream& rStrm ) +{ + // BIFF5/BIFF7 is written separately + OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ); + + sal_uInt16 nFlags = 0; + ::set_flag( nFlags, EXC_NOTE_VISIBLE, mbVisible ); + + rStrm << static_cast< sal_uInt16 >( maScPos.Row() ) + << static_cast< sal_uInt16 >( maScPos.Col() ) + << nFlags + << mnObjId + << maAuthor + << sal_uInt8( 0 ); +} + +void XclExpNote::WriteXml( sal_Int32 nAuthorId, XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr rComments = rStrm.GetCurrentStream(); + + rComments->startElement( XML_comment, + XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), maScPos), + XML_authorId, OString::number(nAuthorId) + // OOXTODO: XML_guid + ); + rComments->startElement(XML_text); + // OOXTODO: phoneticPr, rPh, r + if( mpNoteContents ) + mpNoteContents->WriteXml( rStrm ); + rComments->endElement( XML_text ); + +/* + Export of commentPr is disabled, since the current (Oct 2010) + version of MSO 2010 doesn't yet support commentPr + */ +#if 1//def XLSX_OOXML_FUTURE + if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 ) + { + rComments->startElement(FSNS(XML_mc, XML_AlternateContent)); + rComments->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "v2"); + rComments->startElement( XML_commentPr, + XML_autoFill, ToPsz( mbAutoFill ), + XML_autoScale, ToPsz( mbAutoScale ), + XML_colHidden, ToPsz( mbColHidden ), + XML_locked, ToPsz( mbLocked ), + XML_rowHidden, ToPsz( mbRowHidden ), + XML_textHAlign, ToHorizAlign( meTHA ), + XML_textVAlign, ToVertAlign( meTVA ) ); + rComments->startElement(XML_anchor, XML_moveWithCells, "false", XML_sizeWithCells, "false"); + rComments->startElement(FSNS(XML_xdr, XML_from)); + lcl_WriteAnchorVertex( rComments, maCommentFrom ); + rComments->endElement( FSNS( XML_xdr, XML_from ) ); + rComments->startElement(FSNS(XML_xdr, XML_to)); + lcl_WriteAnchorVertex( rComments, maCommentTo ); + rComments->endElement( FSNS( XML_xdr, XML_to ) ); + rComments->endElement( XML_anchor ); + rComments->endElement( XML_commentPr ); + + rComments->endElement( FSNS( XML_mc, XML_Choice ) ); + rComments->startElement(FSNS(XML_mc, XML_Fallback)); + // Any fallback code ? + rComments->endElement( FSNS( XML_mc, XML_Fallback ) ); + rComments->endElement( FSNS( XML_mc, XML_AlternateContent ) ); + } +#endif + rComments->endElement( XML_comment ); +} + +XclMacroHelper::XclMacroHelper( const XclExpRoot& rRoot ) : + XclExpControlHelper( rRoot ) +{ +} + +XclMacroHelper::~XclMacroHelper() +{ +} + +void XclMacroHelper::WriteMacroSubRec( XclExpStream& rStrm ) +{ + if( mxMacroLink ) + WriteFormulaSubRec( rStrm, EXC_ID_OBJMACRO, *mxMacroLink ); +} + +const OUString& XclMacroHelper::GetMacroName() const { return maMacroName; } + +bool +XclMacroHelper::SetMacroLink( const ScriptEventDescriptor& rEvent, const XclTbxEventType& nEventType ) +{ + maMacroName = XclControlHelper::ExtractFromMacroDescriptor(rEvent, nEventType); + if (!maMacroName.isEmpty()) + { + return SetMacroLink(maMacroName); + } + return false; +} + +bool +XclMacroHelper::SetMacroLink( const OUString& rMacroName ) +{ + // OOXML documents do not store any defined name for VBA macros (while BIFF documents do). + bool bOOXML = GetOutput() == EXC_OUTPUT_XML_2007; + if (!rMacroName.isEmpty() && !bOOXML) + { + sal_uInt16 nExtSheet = GetLocalLinkManager().FindExtSheet( EXC_EXTSH_OWNDOC ); + sal_uInt16 nNameIdx + = GetNameManager().InsertMacroCall(rMacroName, /*bVBasic=*/true, /*bFunc=*/false); + mxMacroLink = GetFormulaCompiler().CreateNameXFormula( nExtSheet, nNameIdx ); + return true; + } + return false; +} + +XclExpShapeObj::XclExpShapeObj( XclExpObjectManager& rRoot, css::uno::Reference< css::drawing::XShape > const & xShape, ScDocument* pDoc ) : + XclObjAny( rRoot, xShape, pDoc ), + XclMacroHelper( rRoot ) +{ + if (SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape)) + { + ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( pSdrObj ); + if ( pInfo && !pInfo->GetMacro().isEmpty() ) +// FIXME ooo330-m2: XclControlHelper::GetXclMacroName was removed in upstream sources; they started to call XclTools::GetXclMacroName instead; is this enough? it has only one parameter +// SetMacroLink( XclControlHelper::GetXclMacroName( pInfo->GetMacro(), rRoot.GetDocShell() ) ); + SetMacroLink( XclTools::GetXclMacroName( pInfo->GetMacro() ) ); + } +} + +XclExpShapeObj::~XclExpShapeObj() +{ +} + +void XclExpShapeObj::WriteSubRecs( XclExpStream& rStrm ) +{ + XclObjAny::WriteSubRecs( rStrm ); + WriteMacroSubRec( rStrm ); +} + +XclExpComments::XclExpComments( SCTAB nTab, XclExpRecordList< XclExpNote >& rNotes ) + : mnTab( nTab ), mrNotes( rNotes ) +{ +} + +void XclExpComments::SaveXml( XclExpXmlStream& rStrm ) +{ + if( mrNotes.IsEmpty() ) + return; + + sax_fastparser::FSHelperPtr rComments = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName( "xl/", "comments", mnTab + 1 ), + XclXmlUtils::GetStreamName( "../", "comments", mnTab + 1 ), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", + oox::getRelationship(Relationship::COMMENTS)); + rStrm.PushStream( rComments ); + + if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 ) + rComments->startElement( XML_comments, + XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)), + FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)), + FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)), + FSNS(XML_xmlns, XML_v2), rStrm.getNamespaceURL(OOX_NS(mceTest)), + FSNS( XML_mc, XML_Ignorable ), "v2" ); + else + rComments->startElement( XML_comments, + XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)), + FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)) ); + + rComments->startElement(XML_authors); + + typedef std::set<OUString> Authors; + Authors aAuthors; + + size_t nNotes = mrNotes.GetSize(); + for( size_t i = 0; i < nNotes; ++i ) + { + aAuthors.insert( XclXmlUtils::ToOUString( mrNotes.GetRecord( i )->GetAuthor() ) ); + } + + for( const auto& rAuthor : aAuthors ) + { + rComments->startElement(XML_author); + rComments->writeEscaped( rAuthor ); + rComments->endElement( XML_author ); + } + + rComments->endElement( XML_authors ); + rComments->startElement(XML_commentList); + + Authors::const_iterator aAuthorsBegin = aAuthors.begin(); + for( size_t i = 0; i < nNotes; ++i ) + { + XclExpRecordList< XclExpNote >::RecordRefType xNote = mrNotes.GetRecord( i ); + Authors::const_iterator aAuthor = aAuthors.find( + XclXmlUtils::ToOUString( xNote->GetAuthor() ) ); + sal_Int32 nAuthorId = distance( aAuthorsBegin, aAuthor ); + xNote->WriteXml( nAuthorId, rStrm ); + } + + rComments->endElement( XML_commentList ); + rComments->endElement( XML_comments ); + + rStrm.PopStream(); +} + +// object manager ============================================================= + +XclExpObjectManager::XclExpObjectManager( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ) +{ + InitStream( true ); + mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mxDffStrm ); +} + +XclExpObjectManager::XclExpObjectManager( const XclExpObjectManager& rParent ) : + XclExpRoot( rParent.GetRoot() ) +{ + InitStream( false ); + mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mxDffStrm, rParent.mxEscherEx.get() ); +} + +XclExpObjectManager::~XclExpObjectManager() +{ +} + +XclExpDffAnchorBase* XclExpObjectManager::CreateDffAnchor() const +{ + return new XclExpDffSheetAnchor( GetRoot() ); +} + +rtl::Reference< XclExpRecordBase > XclExpObjectManager::CreateDrawingGroup() +{ + return new XclExpMsoDrawingGroup( *mxEscherEx ); +} + +void XclExpObjectManager::StartSheet() +{ + mxObjList = new XclExpObjList( GetRoot(), *mxEscherEx ); +} + +rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const SdrPage* pSdrPage ) +{ + if( pSdrPage ) + mxEscherEx->AddSdrPage( *pSdrPage, GetOutput() != EXC_OUTPUT_BINARY ); + // the first dummy object may still be open + OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" ); + while( mxEscherEx->GetGroupLevel() ) + mxEscherEx->LeaveGroup(); + mxObjList->EndSheet(); + return mxObjList; +} + +rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const Reference< XShapes >& rxShapes ) +{ + if( rxShapes.is() ) + mxEscherEx->AddUnoShapes( rxShapes, GetOutput() != EXC_OUTPUT_BINARY ); + // the first dummy object may still be open + OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" ); + while( mxEscherEx->GetGroupLevel() ) + mxEscherEx->LeaveGroup(); + mxObjList->EndSheet(); + return mxObjList; +} + +void XclExpObjectManager::EndDocument() +{ + mxEscherEx->EndDocument(); +} + +XclExpMsoDrawing* XclExpObjectManager::GetMsodrawingPerSheet() +{ + return mxObjList->GetMsodrawingPerSheet(); +} + +bool XclExpObjectManager::HasObj() const +{ + return !mxObjList->empty(); +} + +sal_uInt16 XclExpObjectManager::AddObj( std::unique_ptr<XclObj> pObjRec ) +{ + return mxObjList->Add( std::move(pObjRec) ); +} + +std::unique_ptr<XclObj> XclExpObjectManager::RemoveLastObj() +{ + return mxObjList->pop_back(); +} + +void XclExpObjectManager::InitStream( bool bTempFile ) +{ + if( bTempFile ) + { + mxTempFile = std::make_shared<::utl::TempFile>(); + if( mxTempFile->IsValid() ) + { + mxTempFile->EnableKillingFile(); + mxDffStrm = ::utl::UcbStreamHelper::CreateStream( mxTempFile->GetURL(), StreamMode::STD_READWRITE ); + } + } + + if( !mxDffStrm ) + mxDffStrm = std::make_unique<SvMemoryStream>(); + + mxDffStrm->SetEndian( SvStreamEndian::LITTLE ); +} + +XclExpEmbeddedObjectManager::XclExpEmbeddedObjectManager( + const XclExpObjectManager& rParent, const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) : + XclExpObjectManager( rParent ), + maPageSize( rPageSize ), + mnScaleX( nScaleX ), + mnScaleY( nScaleY ) +{ +} + +XclExpDffAnchorBase* XclExpEmbeddedObjectManager::CreateDffAnchor() const +{ + return new XclExpDffEmbeddedAnchor( GetRoot(), maPageSize, mnScaleX, mnScaleY ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |