diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /oox/source/ole | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | oox/source/ole/axbinaryreader.cxx | 291 | ||||
-rw-r--r-- | oox/source/ole/axbinarywriter.cxx | 201 | ||||
-rw-r--r-- | oox/source/ole/axcontrol.cxx | 2786 | ||||
-rw-r--r-- | oox/source/ole/axcontrolfragment.cxx | 167 | ||||
-rw-r--r-- | oox/source/ole/axfontdata.cxx | 117 | ||||
-rw-r--r-- | oox/source/ole/olehelper.cxx | 571 | ||||
-rw-r--r-- | oox/source/ole/oleobjecthelper.cxx | 176 | ||||
-rw-r--r-- | oox/source/ole/olestorage.cxx | 376 | ||||
-rw-r--r-- | oox/source/ole/vbacontrol.cxx | 874 | ||||
-rw-r--r-- | oox/source/ole/vbaexport.cxx | 1193 | ||||
-rw-r--r-- | oox/source/ole/vbahelper.cxx | 59 | ||||
-rw-r--r-- | oox/source/ole/vbainputstream.cxx | 208 | ||||
-rw-r--r-- | oox/source/ole/vbamodule.cxx | 347 | ||||
-rw-r--r-- | oox/source/ole/vbaproject.cxx | 564 |
14 files changed, 7930 insertions, 0 deletions
diff --git a/oox/source/ole/axbinaryreader.cxx b/oox/source/ole/axbinaryreader.cxx new file mode 100644 index 000000000..6467e51bc --- /dev/null +++ b/oox/source/ole/axbinaryreader.cxx @@ -0,0 +1,291 @@ +/* -*- 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 <oox/ole/axbinaryreader.hxx> + +#include <oox/ole/axfontdata.hxx> +#include <oox/ole/olehelper.hxx> + +#include <osl/diagnose.h> + +namespace oox::ole { + +namespace { + +const sal_uInt32 AX_STRING_SIZEMASK = 0x7FFFFFFF; +const sal_uInt32 AX_STRING_COMPRESSED = 0x80000000; + +} // namespace + +AxAlignedInputStream::AxAlignedInputStream( BinaryInputStream& rInStrm ) : + BinaryStreamBase( false ), + mpInStrm( &rInStrm ), + mnStrmPos( 0 ), + mnStrmSize( rInStrm.getRemaining() ) +{ + mbEof = mbEof || rInStrm.isEof(); +} + +sal_Int64 AxAlignedInputStream::size() const +{ + return mpInStrm ? mnStrmSize : -1; +} + +sal_Int64 AxAlignedInputStream::tell() const +{ + return mpInStrm ? mnStrmPos : -1; +} + +void AxAlignedInputStream::seek( sal_Int64 nPos ) +{ + mbEof = mbEof || (nPos < mnStrmPos); + if( !mbEof ) + skip( static_cast< sal_Int32 >( nPos - mnStrmPos ) ); +} + +void AxAlignedInputStream::close() +{ + mpInStrm = nullptr; + mbEof = true; +} + +sal_Int32 AxAlignedInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize ) +{ + sal_Int32 nReadSize = 0; + if( !mbEof ) + { + nReadSize = mpInStrm->readData( orData, nBytes, nAtomSize ); + mnStrmPos += nReadSize; + mbEof = mpInStrm->isEof(); + } + return nReadSize; +} + +sal_Int32 AxAlignedInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t nAtomSize ) +{ + sal_Int32 nReadSize = 0; + if( !mbEof ) + { + nReadSize = mpInStrm->readMemory( opMem, nBytes, nAtomSize ); + mnStrmPos += nReadSize; + mbEof = mpInStrm->isEof(); + } + return nReadSize; +} + +void AxAlignedInputStream::skip( sal_Int32 nBytes, size_t nAtomSize ) +{ + if( !mbEof ) + { + mpInStrm->skip( nBytes, nAtomSize ); + mnStrmPos += nBytes; + mbEof = mpInStrm->isEof(); + } +} + +void AxAlignedInputStream::align( size_t nSize ) +{ + skip( static_cast< sal_Int32 >( (nSize - (mnStrmPos % nSize)) % nSize ) ); +} + +namespace { + +bool lclReadString( AxAlignedInputStream& rInStrm, OUString& rValue, sal_uInt32 nSize, bool bArrayString ) +{ + bool bCompressed = getFlag( nSize, AX_STRING_COMPRESSED ); + sal_uInt32 nBufSize = nSize & AX_STRING_SIZEMASK; + // Unicode: simple strings store byte count, array strings store char count + sal_Int32 nChars = static_cast< sal_Int32 >( nBufSize / ((bCompressed || bArrayString) ? 1 : 2) ); + bool bValidChars = nChars <= 65536; + OSL_ENSURE( bValidChars, "lclReadString - string too long" ); + sal_Int64 nEndPos = rInStrm.tell() + nChars * (bCompressed ? 1 : 2); + nChars = ::std::min< sal_Int32 >( nChars, 65536 ); + rValue = rInStrm.readCompressedUnicodeArray( nChars, bCompressed ); + rInStrm.seek( nEndPos ); + return bValidChars; +} + +} // namespace + +AxBinaryPropertyReader::ComplexProperty::~ComplexProperty() +{ +} + +bool AxBinaryPropertyReader::PairProperty::readProperty( AxAlignedInputStream& rInStrm ) +{ + mrPairData.first = rInStrm.readInt32(); + mrPairData.second = rInStrm.readInt32(); + return true; +} + +bool AxBinaryPropertyReader::StringProperty::readProperty( AxAlignedInputStream& rInStrm ) +{ + return lclReadString( rInStrm, mrValue, mnSize, false ); +} + +bool AxBinaryPropertyReader::ArrayStringProperty::readProperty( AxAlignedInputStream& rInStrm ) +{ + sal_Int64 nEndPos = rInStrm.tell() + mnSize; + while( rInStrm.tell() < nEndPos ) + { + OUString aString; + if( !lclReadString( rInStrm, aString, rInStrm.readuInt32(), true ) ) + return false; + mrArray.push_back( aString ); + // every array string is aligned on 4 byte boundaries + rInStrm.align( 4 ); + } + return true; +} + +bool AxBinaryPropertyReader::GuidProperty::readProperty( AxAlignedInputStream& rInStrm ) +{ + mrGuid = OleHelper::importGuid( rInStrm ); + return true; +} + +bool AxBinaryPropertyReader::FontProperty::readProperty( AxAlignedInputStream& rInStrm ) +{ + return mrFontData.importGuidAndFont( rInStrm ); +} + +bool AxBinaryPropertyReader::PictureProperty::readProperty( AxAlignedInputStream& rInStrm ) +{ + return OleHelper::importStdPic( mrPicData, rInStrm ); +} + +AxBinaryPropertyReader::AxBinaryPropertyReader( BinaryInputStream& rInStrm, bool b64BitPropFlags ) : + maInStrm( rInStrm ), + mbValid( true ) +{ + // version and size of property block + maInStrm.skip( 2 ); + sal_uInt16 nBlockSize = maInStrm.readuInt16(); + mnPropsEnd = maInStrm.tell() + nBlockSize; + // flagfield containing existing properties + if( b64BitPropFlags ) + mnPropFlags = maInStrm.readInt64(); + else + mnPropFlags = maInStrm.readuInt32(); + mnNextProp = 1; +} + +void AxBinaryPropertyReader::readBoolProperty( bool& orbValue, bool bReverse ) +{ + // there is no data, the boolean value is equivalent to the property flag itself + orbValue = startNextProperty() != bReverse; +} + +void AxBinaryPropertyReader::readPairProperty( AxPairData& orPairData ) +{ + if( startNextProperty() ) + maLargeProps.push_back( ComplexPropVector::value_type( std::make_shared<PairProperty>( orPairData ) ) ); +} + +void AxBinaryPropertyReader::readStringProperty( OUString& orValue ) +{ + if( startNextProperty() ) + { + sal_uInt32 nSize = maInStrm.readAligned< sal_uInt32 >(); + maLargeProps.push_back( ComplexPropVector::value_type( std::make_shared<StringProperty>( orValue, nSize ) ) ); + } +} + +void AxBinaryPropertyReader::readArrayStringProperty( std::vector<OUString>& orValue ) +{ + if( startNextProperty() ) + { + sal_uInt32 nSize = maInStrm.readAligned< sal_uInt32 >(); + maLargeProps.push_back( ComplexPropVector::value_type( std::make_shared<ArrayStringProperty>( orValue, nSize ) ) ); + } +} + +void AxBinaryPropertyReader::readGuidProperty( OUString& orGuid ) +{ + if( startNextProperty() ) + maLargeProps.push_back( ComplexPropVector::value_type( std::make_shared<GuidProperty>( orGuid ) ) ); +} + +void AxBinaryPropertyReader::readFontProperty( AxFontData& orFontData ) +{ + if( startNextProperty() ) + { + sal_Int16 nData = maInStrm.readAligned< sal_Int16 >(); + if( ensureValid( nData == -1 ) ) + maStreamProps.push_back( ComplexPropVector::value_type( std::make_shared<FontProperty>( orFontData ) ) ); + } +} + +void AxBinaryPropertyReader::readPictureProperty( StreamDataSequence& orPicData ) +{ + if( startNextProperty() ) + { + sal_Int16 nData = maInStrm.readAligned< sal_Int16 >(); + if( ensureValid( nData == -1 ) ) + maStreamProps.push_back( ComplexPropVector::value_type( std::make_shared<PictureProperty>( orPicData ) ) ); + } +} + +bool AxBinaryPropertyReader::finalizeImport() +{ + // read large properties + maInStrm.align( 4 ); + if( ensureValid( mnPropFlags == 0 ) ) + { + for (auto const& largeProp : maLargeProps) + { + if (!ensureValid()) + break; + ensureValid( largeProp->readProperty( maInStrm ) ); + maInStrm.align( 4 ); + } + } + maInStrm.seek( mnPropsEnd ); + + // read stream properties (no stream alignment between properties!) + if( ensureValid() ) + { + for (auto const& streamProp : maStreamProps) + { + if (!ensureValid()) + break; + ensureValid( streamProp->readProperty( maInStrm ) ); + } + } + + return mbValid; +} + +bool AxBinaryPropertyReader::ensureValid( bool bCondition ) +{ + mbValid = mbValid && bCondition && !maInStrm.isEof(); + return mbValid; +} + +bool AxBinaryPropertyReader::startNextProperty() +{ + bool bHasProp = getFlag( mnPropFlags, mnNextProp ); + setFlag( mnPropFlags, mnNextProp, false ); + mnNextProp <<= 1; + return ensureValid() && bHasProp; +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/axbinarywriter.cxx b/oox/source/ole/axbinarywriter.cxx new file mode 100644 index 000000000..b9c4b66be --- /dev/null +++ b/oox/source/ole/axbinarywriter.cxx @@ -0,0 +1,201 @@ +/* -*- 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/. + */ +#include <oox/ole/axbinarywriter.hxx> + +namespace oox::ole { + +namespace { + +const sal_uInt32 AX_STRING_COMPRESSED = 0x80000000; + +} // namespace + +AxAlignedOutputStream::AxAlignedOutputStream( BinaryOutputStream& rOutStrm ) : + BinaryStreamBase( false ), + mpOutStrm( &rOutStrm ), + mnStrmPos( 0 ), + mnStrmSize( rOutStrm.getRemaining() ), + mnWrappedBeginPos( rOutStrm.tell() ) +{ + mbEof = mbEof || rOutStrm.isEof(); +} + +sal_Int64 AxAlignedOutputStream::size() const +{ + return mpOutStrm ? mnStrmSize : -1; +} + +sal_Int64 AxAlignedOutputStream::tell() const +{ + return mpOutStrm ? mnStrmPos : -1; +} + +void AxAlignedOutputStream::seek( sal_Int64 nPos ) +{ + mbEof = (nPos < 0); + if( !mbEof ) + { + mpOutStrm->seek( static_cast< sal_Int32 >( mnWrappedBeginPos + nPos ) ); + mnStrmPos = mpOutStrm->tell() - mnWrappedBeginPos; + } +} + +void AxAlignedOutputStream::close() +{ + mpOutStrm = nullptr; + mbEof = true; +} + +void AxAlignedOutputStream::writeData( const StreamDataSequence& orData, size_t nAtomSize ) +{ + mpOutStrm->writeData( orData, nAtomSize ); + mnStrmPos = mpOutStrm->tell() - mnWrappedBeginPos; +} + +void AxAlignedOutputStream::writeMemory( const void* opMem, sal_Int32 nBytes, size_t nAtomSize ) +{ + mpOutStrm->writeMemory( opMem, nBytes, nAtomSize ); + mnStrmPos = mpOutStrm->tell() - mnWrappedBeginPos; +} + +void AxAlignedOutputStream::pad( sal_Int32 nBytes ) +{ + //PRESUMABLY we need to pad with 0's here as appropriate + css::uno::Sequence< sal_Int8 > aData( nBytes ); + // ok we could be padding with rubbish here, but really that shouldn't matter + // set to 0(s), easier to not get fooled by 0's when looking at + // binary content... + memset( static_cast<void*>( aData.getArray() ), 0, nBytes ); + mpOutStrm->writeData( aData ); + mnStrmPos = mpOutStrm->tell() - mnWrappedBeginPos; +} + +void AxAlignedOutputStream::align( size_t nSize ) +{ + pad( static_cast< sal_Int32 >( (nSize - (mnStrmPos % nSize)) % nSize ) ); +} + +namespace { + +void lclWriteString( AxAlignedOutputStream& rOutStrm, OUString const & rValue, sal_uInt32 nSize ) +{ + bool bCompressed = getFlag( nSize, AX_STRING_COMPRESSED ); + rOutStrm.writeCompressedUnicodeArray( rValue, bCompressed ); +} + +} // namespace + +AxBinaryPropertyWriter::ComplexProperty::~ComplexProperty() +{ +} + +bool AxBinaryPropertyWriter::PairProperty::writeProperty( AxAlignedOutputStream& rOutStrm ) +{ + rOutStrm.WriteInt32(mrPairData.first).WriteInt32(mrPairData.second); + return true; +} + +bool AxBinaryPropertyWriter::StringProperty::writeProperty( AxAlignedOutputStream& rOutStrm ) +{ + lclWriteString( rOutStrm, mrValue, mnSize ); + return true; +} + +AxBinaryPropertyWriter::AxBinaryPropertyWriter( BinaryOutputStream& rOutStrm, bool b64BitPropFlags ) : + maOutStrm( rOutStrm ), + mnPropFlags( 0x0 ), + mbValid( true ), + mb64BitPropFlags( b64BitPropFlags ) +{ + sal_uInt16 nId( 0x0200 ); + maOutStrm.WriteUInt16(nId); + mnBlockSize = 0; // will be filled in the finalize method + + maOutStrm.WriteUInt16(nId); + mnPropFlagsStart = maOutStrm.tell(); + + if( mb64BitPropFlags ) + maOutStrm.WriteInt64( mnPropFlags ); + else + maOutStrm.WriteUInt32( mnPropFlags ); + mnNextProp = 1; +} + +void AxBinaryPropertyWriter::writeBoolProperty( bool orbValue ) +{ + // orbValue == bReverse false then we want to set the bit, e.g. don't skip + startNextProperty( !orbValue ); +} + +void AxBinaryPropertyWriter::writePairProperty( AxPairData& orPairData ) +{ + startNextProperty(); + maLargeProps.push_back( ComplexPropVector::value_type( std::make_shared<PairProperty>( orPairData ) ) ); +} + +void AxBinaryPropertyWriter::writeStringProperty( OUString& orValue ) +{ + sal_uInt32 nSize = orValue.getLength() * 2; + setFlag( nSize, AX_STRING_COMPRESSED, false ); + maOutStrm.writeAligned< sal_uInt32 >( nSize ); + maLargeProps.push_back( ComplexPropVector::value_type( std::make_shared<StringProperty>( orValue, nSize ) ) ); + startNextProperty(); +} + +void AxBinaryPropertyWriter::finalizeExport() +{ + // write large properties + maOutStrm.align( 4 ); + for (auto const& largeProp : maLargeProps) + { + if (!ensureValid()) + break; + largeProp->writeProperty( maOutStrm ); + maOutStrm.align( 4 ); + } + + mnBlockSize = maOutStrm.tell() - mnPropFlagsStart; + + // write stream properties (no stream alignment between properties!) + for (auto const& streamProp : maStreamProps) + { + if (!ensureValid()) + break; + streamProp->writeProperty( maOutStrm ); + } + + sal_Int64 nPos = maOutStrm.tell(); + maOutStrm.seek( mnPropFlagsStart - sizeof( mnBlockSize ) ); + + maOutStrm.WriteInt16( mnBlockSize ); + + if( mb64BitPropFlags ) + maOutStrm.WriteInt64( mnPropFlags ); + else + maOutStrm.WriteUInt32( mnPropFlags ); + + maOutStrm.seek( nPos ); +} + +bool AxBinaryPropertyWriter::ensureValid() +{ + mbValid = mbValid && !maOutStrm.isEof(); + return mbValid; +} + +void AxBinaryPropertyWriter::startNextProperty( bool bSkip ) +{ + // if we are skipping then we clear the flag + setFlag( mnPropFlags, mnNextProp, !bSkip ); + mnNextProp <<= 1; +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/axcontrol.cxx b/oox/source/ole/axcontrol.cxx new file mode 100644 index 000000000..a3573b395 --- /dev/null +++ b/oox/source/ole/axcontrol.cxx @@ -0,0 +1,2786 @@ +/* -*- 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 <oox/ole/axcontrol.hxx> + +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/ImagePosition.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/TextAlign.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/form/XFormComponent.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sheet/XCellRangeAddressable.hpp> +#include <com/sun/star/sheet/XCellRangeReferrer.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <rtl/tencinfo.h> +#include <osl/diagnose.h> +#include <vcl/font.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <oox/helper/attributelist.hxx> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/helper/graphichelper.hxx> +#include <oox/helper/propertymap.hxx> +#include <oox/ole/axbinarywriter.hxx> +#include <oox/token/properties.hxx> +#include <oox/token/tokens.hxx> +#include <tools/diagnose_ex.h> +#include <o3tl/string_view.hxx> + +namespace oox::ole { + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::form::binding; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::uno; + +namespace { + +const sal_uInt32 COMCTL_ID_SIZE = 0x12344321; + +const sal_uInt32 COMCTL_ID_COMMONDATA = 0xABCDEF01; +const sal_uInt32 COMCTL_COMMON_FLATBORDER = 0x00000001; +const sal_uInt32 COMCTL_COMMON_ENABLED = 0x00000002; +const sal_uInt32 COMCTL_COMMON_3DBORDER = 0x00000004; + +const sal_uInt32 COMCTL_ID_COMPLEXDATA = 0xBDECDE1F; +const sal_uInt32 COMCTL_COMPLEX_FONT = 0x00000001; +const sal_uInt32 COMCTL_COMPLEX_MOUSEICON = 0x00000002; + +const sal_uInt32 COMCTL_ID_SCROLLBAR_60 = 0x99470A83; +const sal_uInt32 COMCTL_SCROLLBAR_HOR = 0x00000010; + +const sal_uInt32 COMCTL_ID_PROGRESSBAR_50 = 0xE6E17E84; +const sal_uInt32 COMCTL_ID_PROGRESSBAR_60 = 0x97AB8A01; + +const sal_uInt32 AX_CMDBUTTON_DEFFLAGS = 0x0000001B; +const sal_uInt32 AX_LABEL_DEFFLAGS = 0x0080001B; +const sal_uInt32 AX_IMAGE_DEFFLAGS = 0x0000001B; +const sal_uInt32 AX_MORPHDATA_DEFFLAGS = 0x2C80081B; +const sal_uInt32 AX_SPINBUTTON_DEFFLAGS = 0x0000001B; +const sal_uInt32 AX_SCROLLBAR_DEFFLAGS = 0x0000001B; + +const sal_uInt16 AX_POS_TOPLEFT = 0; +const sal_uInt16 AX_POS_TOP = 1; +const sal_uInt16 AX_POS_TOPRIGHT = 2; +const sal_uInt16 AX_POS_LEFT = 3; +const sal_uInt16 AX_POS_CENTER = 4; +const sal_uInt16 AX_POS_RIGHT = 5; +const sal_uInt16 AX_POS_BOTTOMLEFT = 6; +const sal_uInt16 AX_POS_BOTTOM = 7; +const sal_uInt16 AX_POS_BOTTOMRIGHT = 8; + +#define AX_PICPOS_IMPL( label, image ) ((AX_POS_##label << 16) | AX_POS_##image) +const sal_uInt32 AX_PICPOS_LEFTTOP = AX_PICPOS_IMPL( TOPRIGHT, TOPLEFT ); +const sal_uInt32 AX_PICPOS_LEFTCENTER = AX_PICPOS_IMPL( RIGHT, LEFT ); +const sal_uInt32 AX_PICPOS_LEFTBOTTOM = AX_PICPOS_IMPL( BOTTOMRIGHT, BOTTOMLEFT ); +const sal_uInt32 AX_PICPOS_RIGHTTOP = AX_PICPOS_IMPL( TOPLEFT, TOPRIGHT ); +const sal_uInt32 AX_PICPOS_RIGHTCENTER = AX_PICPOS_IMPL( LEFT, RIGHT ); +const sal_uInt32 AX_PICPOS_RIGHTBOTTOM = AX_PICPOS_IMPL( BOTTOMLEFT, BOTTOMRIGHT ); +const sal_uInt32 AX_PICPOS_ABOVELEFT = AX_PICPOS_IMPL( BOTTOMLEFT, TOPLEFT ); +const sal_uInt32 AX_PICPOS_ABOVECENTER = AX_PICPOS_IMPL( BOTTOM, TOP ); +const sal_uInt32 AX_PICPOS_ABOVERIGHT = AX_PICPOS_IMPL( BOTTOMRIGHT, TOPRIGHT ); +const sal_uInt32 AX_PICPOS_BELOWLEFT = AX_PICPOS_IMPL( TOPLEFT, BOTTOMLEFT ); +const sal_uInt32 AX_PICPOS_BELOWCENTER = AX_PICPOS_IMPL( TOP, BOTTOM ); +const sal_uInt32 AX_PICPOS_BELOWRIGHT = AX_PICPOS_IMPL( TOPRIGHT, BOTTOMRIGHT ); +const sal_uInt32 AX_PICPOS_CENTER = AX_PICPOS_IMPL( CENTER, CENTER ); +#undef AX_PICPOS_IMPL + +const sal_Int32 AX_MATCHENTRY_FIRSTLETTER = 0; +const sal_Int32 AX_MATCHENTRY_COMPLETE = 1; +const sal_Int32 AX_MATCHENTRY_NONE = 2; + +const sal_Int32 AX_ORIENTATION_AUTO = -1; +const sal_Int32 AX_ORIENTATION_VERTICAL = 0; +const sal_Int32 AX_ORIENTATION_HORIZONTAL = 1; + +const sal_Int32 AX_PROPTHUMB_ON = -1; + +const sal_uInt32 AX_TABSTRIP_TABS = 0; +const sal_uInt32 AX_TABSTRIP_NONE = 2; + +const sal_uInt32 AX_CONTAINER_ENABLED = 0x00000004; +const sal_uInt32 AX_CONTAINER_NOCLASSTABLE = 0x00008000; + +const sal_uInt32 AX_CONTAINER_DEFFLAGS = 0x00000004; + +const sal_Int32 AX_CONTAINER_DEFWIDTH = 4000; +const sal_Int32 AX_CONTAINER_DEFHEIGHT = 3000; + +const sal_Int32 AX_CONTAINER_CYCLEALL = 0; + +const sal_Int32 AX_CONTAINER_SCR_NONE = 0x00; + +const sal_Int16 API_BORDER_NONE = 0; +const sal_Int16 API_BORDER_SUNKEN = 1; +const sal_Int16 API_BORDER_FLAT = 2; + +const sal_Int16 API_STATE_UNCHECKED = 0; +const sal_Int16 API_STATE_CHECKED = 1; +const sal_Int16 API_STATE_DONTKNOW = 2; + +/** Tries to extract a range address from a defined name. */ +bool lclExtractRangeFromName( CellRangeAddress& orRangeAddr, const Reference< XModel >& rxDocModel, const OUString& rAddressString ) +{ + try + { + PropertySet aPropSet( rxDocModel ); + Reference< XNameAccess > xRangesNA( aPropSet.getAnyProperty( PROP_NamedRanges ), UNO_QUERY_THROW ); + Reference< XCellRangeReferrer > xReferrer( xRangesNA->getByName( rAddressString ), UNO_QUERY_THROW ); + Reference< XCellRangeAddressable > xAddressable( xReferrer->getReferredCells(), UNO_QUERY_THROW ); + orRangeAddr = xAddressable->getRangeAddress(); + return true; + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox", ""); + } + return false; +} + +bool lclExtractAddressFromName( CellAddress& orAddress, const Reference< XModel >& rxDocModel, const OUString& rAddressString ) +{ + CellRangeAddress aRangeAddr; + if( lclExtractRangeFromName( aRangeAddr, rxDocModel, rAddressString ) && + (aRangeAddr.StartColumn == aRangeAddr.EndColumn) && + (aRangeAddr.StartRow == aRangeAddr.EndRow) ) + { + orAddress.Sheet = aRangeAddr.Sheet; + orAddress.Column = aRangeAddr.StartColumn; + orAddress.Row = aRangeAddr.StartRow; + return true; + } + return false; +} + +void lclPrepareConverter( PropertySet& rConverter, const Reference< XModel >& rxDocModel, + const OUString& rAddressString, sal_Int32 nRefSheet, bool bRange ) +{ + if( !rConverter.is() ) try + { + Reference< XMultiServiceFactory > xModelFactory( rxDocModel, UNO_QUERY_THROW ); + OUString aServiceName = bRange ? + OUString( "com.sun.star.table.CellRangeAddressConversion" ) : + OUString( "com.sun.star.table.CellAddressConversion" ); + rConverter.set( xModelFactory->createInstance( aServiceName ) ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox", ""); + } + rConverter.setProperty( PROP_XLA1Representation, rAddressString ); + rConverter.setProperty( PROP_ReferenceSheet, nRefSheet ); +} + +} // namespace + +ControlConverter::ControlConverter( const Reference< XModel >& rxDocModel, + const GraphicHelper& rGraphicHelper, bool bDefaultColorBgr ) : + mxDocModel( rxDocModel ), + mrGraphicHelper( rGraphicHelper ), + mbDefaultColorBgr( bDefaultColorBgr ) +{ + OSL_ENSURE( mxDocModel.is(), "ControlConverter::ControlConverter - missing document model" ); +} + +ControlConverter::~ControlConverter() +{ +} + +// Generic conversion --------------------------------------------------------- + +void ControlConverter::convertPosition( PropertyMap& rPropMap, const AxPairData& rPos ) const +{ + // position is given in 1/100 mm, UNO needs AppFont units + awt::Point aAppFontPos = mrGraphicHelper.convertHmmToAppFont( awt::Point( rPos.first, rPos.second ) ); + rPropMap.setProperty( PROP_PositionX, aAppFontPos.X ); + rPropMap.setProperty( PROP_PositionY, aAppFontPos.Y ); +} + +void ControlConverter::convertSize( PropertyMap& rPropMap, const AxPairData& rSize ) const +{ + // size is given in 1/100 mm, UNO needs AppFont units + awt::Size aAppFontSize = mrGraphicHelper.convertHmmToAppFont( awt::Size( rSize.first, rSize.second ) ); + rPropMap.setProperty( PROP_Width, aAppFontSize.Width ); + rPropMap.setProperty( PROP_Height, aAppFontSize.Height ); +} + +void ControlConverter::convertColor( PropertyMap& rPropMap, sal_Int32 nPropId, sal_uInt32 nOleColor ) const +{ + rPropMap.setProperty( nPropId, OleHelper::decodeOleColor( mrGraphicHelper, nOleColor, mbDefaultColorBgr ) ); +} + +void ControlConverter::convertToMSColor( PropertySet const & rPropSet, sal_Int32 nPropId, sal_uInt32& nOleColor, sal_uInt32 nDefault ) +{ + sal_uInt32 nRGB = 0; + if (rPropSet.getProperty( nRGB, nPropId )) + nOleColor = OleHelper::encodeOleColor( nRGB ); + else + nOleColor = nDefault; +} +void ControlConverter::convertPicture( PropertyMap& rPropMap, const StreamDataSequence& rPicData ) const +{ + if( rPicData.hasElements() ) + { + uno::Reference<graphic::XGraphic> xGraphic = mrGraphicHelper.importGraphic(rPicData); + if (xGraphic.is()) + rPropMap.setProperty(PROP_Graphic, xGraphic); + } +} + +void ControlConverter::convertOrientation( PropertyMap& rPropMap, bool bHorizontal ) +{ + sal_Int32 nScrollOrient = bHorizontal ? ScrollBarOrientation::HORIZONTAL : ScrollBarOrientation::VERTICAL; + rPropMap.setProperty( PROP_Orientation, nScrollOrient ); +} + +void ControlConverter::convertToMSOrientation( PropertySet const & rPropSet, bool& bHorizontal ) +{ + sal_Int32 nScrollOrient = ScrollBarOrientation::HORIZONTAL; + if ( rPropSet.getProperty( nScrollOrient, PROP_Orientation ) ) + bHorizontal = ( nScrollOrient == ScrollBarOrientation::HORIZONTAL ); +} + +void ControlConverter::convertVerticalAlign( PropertyMap& rPropMap, sal_Int32 nVerticalAlign ) +{ + VerticalAlignment eAlign = VerticalAlignment_TOP; + switch( nVerticalAlign ) + { + case XML_Top: eAlign = VerticalAlignment_TOP; break; + case XML_Center: eAlign = VerticalAlignment_MIDDLE; break; + case XML_Bottom: eAlign = VerticalAlignment_BOTTOM; break; + } + rPropMap.setProperty( PROP_VerticalAlign, eAlign ); +} + +void ControlConverter::convertScrollabilitySettings( PropertyMap& rPropMap, + const AxPairData& rScrollPos, const AxPairData& rScrollArea, + sal_Int32 nScrollBars ) const +{ + awt::Size tmpSize = mrGraphicHelper.convertHmmToAppFont( awt::Size( rScrollArea.first, rScrollArea.second ) ); + awt::Point tmpPos = mrGraphicHelper.convertHmmToAppFont( awt::Point( rScrollPos.first, rScrollPos.second ) ); + rPropMap.setProperty( PROP_ScrollHeight, tmpSize.Height ); + rPropMap.setProperty( PROP_ScrollWidth, tmpSize.Width ); + rPropMap.setProperty( PROP_ScrollTop, tmpPos.Y ); + rPropMap.setProperty( PROP_ScrollLeft, tmpPos.X ); + rPropMap.setProperty( PROP_HScroll, ( nScrollBars & 0x1 ) == 0x1 ); + rPropMap.setProperty( PROP_VScroll, ( nScrollBars & 0x2 ) == 0x2 ); +} + +void ControlConverter::convertScrollBar( PropertyMap& rPropMap, + sal_Int32 nMin, sal_Int32 nMax, sal_Int32 nPosition, + sal_Int32 nSmallChange, sal_Int32 nLargeChange, bool bAwtModel ) +{ + rPropMap.setProperty( PROP_ScrollValueMin, ::std::min( nMin, nMax ) ); + rPropMap.setProperty( PROP_ScrollValueMax, ::std::max( nMin, nMax ) ); + rPropMap.setProperty( PROP_LineIncrement, nSmallChange ); + rPropMap.setProperty( PROP_BlockIncrement, nLargeChange ); + rPropMap.setProperty( bAwtModel ? PROP_ScrollValue : PROP_DefaultScrollValue, nPosition ); +} + +void ControlConverter::bindToSources( const Reference< XControlModel >& rxCtrlModel, + const OUString& rCtrlSource, const OUString& rRowSource, sal_Int32 nRefSheet ) const +{ + // value binding + if( !rCtrlSource.isEmpty() ) try + { + // first check if the XBindableValue interface is supported + Reference< XBindableValue > xBindable( rxCtrlModel, UNO_QUERY_THROW ); + + // convert address string to cell address struct + CellAddress aAddress; + if( !lclExtractAddressFromName( aAddress, mxDocModel, rCtrlSource ) ) + { + lclPrepareConverter( maAddressConverter, mxDocModel, rCtrlSource, nRefSheet, false ); + if( !maAddressConverter.getProperty( aAddress, PROP_Address ) ) + throw RuntimeException(); + } + + // create argument sequence + Sequence< Any > aArgs{ Any(NamedValue("BoundCell", Any(aAddress))) }; + + // create the CellValueBinding instance and set at the control model + Reference< XMultiServiceFactory > xModelFactory( mxDocModel, UNO_QUERY_THROW ); + Reference< XValueBinding > xBinding( xModelFactory->createInstanceWithArguments( "com.sun.star.table.CellValueBinding", aArgs ), UNO_QUERY_THROW ); + xBindable->setValueBinding( xBinding ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox", ""); + } + + // list entry source + if( rRowSource.isEmpty() ) + return; + + try + { + // first check if the XListEntrySink interface is supported + Reference< XListEntrySink > xEntrySink( rxCtrlModel, UNO_QUERY_THROW ); + + // convert address string to cell range address struct + CellRangeAddress aRangeAddr; + if( !lclExtractRangeFromName( aRangeAddr, mxDocModel, rRowSource ) ) + { + lclPrepareConverter( maRangeConverter, mxDocModel, rRowSource, nRefSheet, true ); + if( !maRangeConverter.getProperty( aRangeAddr, PROP_Address ) ) + throw RuntimeException(); + } + + // create argument sequence + Sequence< Any > aArgs{ Any(NamedValue("CellRange", Any(aRangeAddr))) }; + + // create the EntrySource instance and set at the control model + Reference< XMultiServiceFactory > xModelFactory( mxDocModel, UNO_QUERY_THROW ); + Reference< XListEntrySource > xEntrySource( xModelFactory->createInstanceWithArguments("com.sun.star.table.CellRangeListSource", aArgs ), UNO_QUERY_THROW ); + xEntrySink->setListEntrySource( xEntrySource ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox", ""); + } +} + +// ActiveX (Forms 2.0) specific conversion ------------------------------------ + +void ControlConverter::convertAxBackground( PropertyMap& rPropMap, + sal_uInt32 nBackColor, sal_uInt32 nFlags, ApiTransparencyMode eTranspMode ) const +{ + bool bOpaque = getFlag( nFlags, AX_FLAGS_OPAQUE ); + switch( eTranspMode ) + { + case ApiTransparencyMode::NotSupported: + // fake transparency by using system window background if needed + convertColor( rPropMap, PROP_BackgroundColor, bOpaque ? nBackColor : AX_SYSCOLOR_WINDOWBACK ); + break; + case ApiTransparencyMode::Void: + // keep transparency by leaving the (void) default property value + if( bOpaque ) + convertColor( rPropMap, PROP_BackgroundColor, nBackColor ); + break; + } +} + +void ControlConverter::convertAxBorder( PropertyMap& rPropMap, + sal_uInt32 nBorderColor, sal_Int32 nBorderStyle, sal_Int32 nSpecialEffect ) const +{ + sal_Int16 nBorder = (nBorderStyle == AX_BORDERSTYLE_SINGLE) ? API_BORDER_FLAT : + ((nSpecialEffect == AX_SPECIALEFFECT_FLAT) ? API_BORDER_NONE : API_BORDER_SUNKEN); + rPropMap.setProperty( PROP_Border, nBorder ); + convertColor( rPropMap, PROP_BorderColor, nBorderColor ); +} + +void ControlConverter::convertToAxBorder( PropertySet const & rPropSet, + sal_uInt32& nBorderColor, sal_Int32& nBorderStyle, sal_Int32& nSpecialEffect ) +{ + sal_Int16 nBorder = API_BORDER_NONE; + rPropSet.getProperty( nBorder, PROP_Border ); + nBorderStyle = AX_BORDERSTYLE_NONE; + nSpecialEffect = AX_SPECIALEFFECT_FLAT; + switch ( nBorder ) + { + case API_BORDER_FLAT: + nBorderStyle = AX_BORDERSTYLE_SINGLE; + break; + case API_BORDER_SUNKEN: + nSpecialEffect = AX_SPECIALEFFECT_SUNKEN; + break; + case API_BORDER_NONE: + default: + break; + } + convertToMSColor( rPropSet, PROP_BorderColor, nBorderColor ); +} + +void ControlConverter::convertAxVisualEffect( PropertyMap& rPropMap, sal_Int32 nSpecialEffect ) +{ + sal_Int16 nVisualEffect = (nSpecialEffect == AX_SPECIALEFFECT_FLAT) ? VisualEffect::FLAT : VisualEffect::LOOK3D; + rPropMap.setProperty( PROP_VisualEffect, nVisualEffect ); +} + +void ControlConverter::convertToAxVisualEffect( PropertySet const & rPropSet, sal_Int32& nSpecialEffect ) +{ + sal_Int16 nVisualEffect = AX_SPECIALEFFECT_FLAT; + rPropSet.getProperty( nVisualEffect, PROP_VisualEffect ); + // is this appropriate AX_SPECIALEFFECT_XXXX value ? + if (nVisualEffect == VisualEffect::LOOK3D ) + nSpecialEffect = AX_SPECIALEFFECT_RAISED; +} + +void ControlConverter::convertAxPicture( PropertyMap& rPropMap, const StreamDataSequence& rPicData, sal_uInt32 nPicPos ) const +{ + // the picture + convertPicture( rPropMap, rPicData ); + + // picture position + sal_Int16 nImagePos = ImagePosition::LeftCenter; + switch( nPicPos ) + { + case AX_PICPOS_LEFTTOP: nImagePos = ImagePosition::LeftTop; break; + case AX_PICPOS_LEFTCENTER: nImagePos = ImagePosition::LeftCenter; break; + case AX_PICPOS_LEFTBOTTOM: nImagePos = ImagePosition::LeftBottom; break; + case AX_PICPOS_RIGHTTOP: nImagePos = ImagePosition::RightTop; break; + case AX_PICPOS_RIGHTCENTER: nImagePos = ImagePosition::RightCenter; break; + case AX_PICPOS_RIGHTBOTTOM: nImagePos = ImagePosition::RightBottom; break; + case AX_PICPOS_ABOVELEFT: nImagePos = ImagePosition::AboveLeft; break; + case AX_PICPOS_ABOVECENTER: nImagePos = ImagePosition::AboveCenter; break; + case AX_PICPOS_ABOVERIGHT: nImagePos = ImagePosition::AboveRight; break; + case AX_PICPOS_BELOWLEFT: nImagePos = ImagePosition::BelowLeft; break; + case AX_PICPOS_BELOWCENTER: nImagePos = ImagePosition::BelowCenter; break; + case AX_PICPOS_BELOWRIGHT: nImagePos = ImagePosition::BelowRight; break; + case AX_PICPOS_CENTER: nImagePos = ImagePosition::Centered; break; + default: OSL_FAIL( "ControlConverter::convertAxPicture - unknown picture position" ); + } + rPropMap.setProperty( PROP_ImagePosition, nImagePos ); +} + +void ControlConverter::convertAxPicture( PropertyMap& rPropMap, const StreamDataSequence& rPicData, + sal_Int32 nPicSizeMode ) const +{ + // the picture + convertPicture( rPropMap, rPicData ); + + // picture scale mode + sal_Int16 nScaleMode = ImageScaleMode::NONE; + switch( nPicSizeMode ) + { + case AX_PICSIZE_CLIP: nScaleMode = ImageScaleMode::NONE; break; + case AX_PICSIZE_STRETCH: nScaleMode = ImageScaleMode::ANISOTROPIC; break; + case AX_PICSIZE_ZOOM: nScaleMode = ImageScaleMode::ISOTROPIC; break; + default: OSL_FAIL( "ControlConverter::convertAxPicture - unknown picture size mode" ); + } + rPropMap.setProperty( PROP_ScaleMode, nScaleMode ); +} + +void ControlConverter::convertAxState( PropertyMap& rPropMap, + const OUString& rValue, sal_Int32 nMultiSelect, ApiDefaultStateMode eDefStateMode, bool bAwtModel ) +{ + bool bBooleanState = eDefStateMode == API_DEFAULTSTATE_BOOLEAN; + bool bSupportsTriState = eDefStateMode == API_DEFAULTSTATE_TRISTATE; + + // state + sal_Int16 nState = bSupportsTriState ? API_STATE_DONTKNOW : API_STATE_UNCHECKED; + if( rValue.getLength() == 1 ) switch( rValue[ 0 ] ) + { + case '0': nState = API_STATE_UNCHECKED; break; + case '1': nState = API_STATE_CHECKED; break; + // any other string (also empty) means 'dontknow' + } + sal_Int32 nPropId = bAwtModel ? PROP_State : PROP_DefaultState; + if( bBooleanState ) + rPropMap.setProperty( nPropId, nState != API_STATE_UNCHECKED ); + else + rPropMap.setProperty( nPropId, nState ); + + // tristate + if( bSupportsTriState ) + rPropMap.setProperty( PROP_TriState, nMultiSelect == AX_SELECTION_MULTI ); +} + +void ControlConverter::convertToAxState( PropertySet const & rPropSet, + OUString& rValue, sal_Int32& nMultiSelect, ApiDefaultStateMode eDefStateMode ) +{ + bool bSupportsTriState = eDefStateMode == API_DEFAULTSTATE_TRISTATE; + + sal_Int16 nState = API_STATE_DONTKNOW; + + // need to use State for current state ( I think this is regardless of whether + // control is awt or not ) + rPropSet.getProperty( nState, PROP_State ); + + rValue.clear(); // empty e.g. 'don't know' + if ( nState == API_STATE_UNCHECKED ) + rValue = "0"; + else if ( nState == API_STATE_CHECKED ) + rValue = "1"; + + // tristate + if( bSupportsTriState ) + { + bool bTriStateEnabled = false; + bool bPropertyExists = rPropSet.getProperty( bTriStateEnabled, PROP_TriState ); + if( bPropertyExists && bTriStateEnabled ) + nMultiSelect = AX_SELECTION_MULTI; + } +} + +void ControlConverter::convertAxOrientation( PropertyMap& rPropMap, + const AxPairData& rSize, sal_Int32 nOrientation ) +{ + bool bHorizontal = true; + switch( nOrientation ) + { + case AX_ORIENTATION_AUTO: bHorizontal = rSize.first > rSize.second; break; + case AX_ORIENTATION_VERTICAL: bHorizontal = false; break; + case AX_ORIENTATION_HORIZONTAL: bHorizontal = true; break; + default: OSL_FAIL( "ControlConverter::convertAxOrientation - unknown orientation" ); + } + convertOrientation( rPropMap, bHorizontal ); +} + +void ControlConverter::convertToAxOrientation( PropertySet const & rPropSet, + sal_Int32& nOrientation ) +{ + bool bHorizontal = true; + convertToMSOrientation( rPropSet, bHorizontal ); + + if ( bHorizontal ) + nOrientation = AX_ORIENTATION_HORIZONTAL; + else + nOrientation = AX_ORIENTATION_VERTICAL; +} + +ControlModelBase::ControlModelBase() : + maSize( 0, 0 ), + mbAwtModel( false ) +{ +} + +ControlModelBase::~ControlModelBase() +{ +} + +OUString ControlModelBase::getServiceName() const +{ + ApiControlType eCtrlType = getControlType(); + if( mbAwtModel ) switch( eCtrlType ) + { + case API_CONTROL_BUTTON: return "com.sun.star.awt.UnoControlButtonModel"; + case API_CONTROL_FIXEDTEXT: return "com.sun.star.awt.UnoControlFixedTextModel"; + case API_CONTROL_IMAGE: return "com.sun.star.awt.UnoControlImageControlModel"; + case API_CONTROL_CHECKBOX: return "com.sun.star.awt.UnoControlCheckBoxModel"; + case API_CONTROL_RADIOBUTTON: return "com.sun.star.form.component.RadioButton"; + case API_CONTROL_EDIT: return "com.sun.star.awt.UnoControlEditModel"; + case API_CONTROL_NUMERIC: return "com.sun.star.awt.UnoControlNumericFieldModel"; + case API_CONTROL_LISTBOX: return "com.sun.star.form.component.ListBox"; + case API_CONTROL_COMBOBOX: return "com.sun.star.form.component.ComboBox"; + case API_CONTROL_SPINBUTTON: return "com.sun.star.form.component.SpinButton"; + case API_CONTROL_SCROLLBAR: return "com.sun.star.form.component.ScrollBar"; + case API_CONTROL_PROGRESSBAR: return "com.sun.star.awt.UnoControlProgressBarModel"; + case API_CONTROL_GROUPBOX: return "com.sun.star.form.component.GroupBox"; + case API_CONTROL_FRAME: return "com.sun.star.awt.UnoFrameModel"; + case API_CONTROL_PAGE: return "com.sun.star.awt.UnoPageModel"; + case API_CONTROL_MULTIPAGE: return "com.sun.star.awt.UnoMultiPageModel"; + case API_CONTROL_DIALOG: return "com.sun.star.awt.UnoControlDialogModel"; + default: OSL_FAIL( "ControlModelBase::getServiceName - no AWT model service supported" ); + } + else switch( eCtrlType ) + { + case API_CONTROL_BUTTON: return "com.sun.star.form.component.CommandButton"; + case API_CONTROL_FIXEDTEXT: return "com.sun.star.form.component.FixedText"; + case API_CONTROL_IMAGE: return "com.sun.star.form.component.DatabaseImageControl"; + case API_CONTROL_CHECKBOX: return "com.sun.star.form.component.CheckBox"; + case API_CONTROL_RADIOBUTTON: return "com.sun.star.form.component.RadioButton"; + case API_CONTROL_EDIT: return "com.sun.star.form.component.TextField"; + case API_CONTROL_NUMERIC: return "com.sun.star.form.component.NumericField"; + case API_CONTROL_LISTBOX: return "com.sun.star.form.component.ListBox"; + case API_CONTROL_COMBOBOX: return "com.sun.star.form.component.ComboBox"; + case API_CONTROL_SPINBUTTON: return "com.sun.star.form.component.SpinButton"; + case API_CONTROL_SCROLLBAR: return "com.sun.star.form.component.ScrollBar"; + case API_CONTROL_GROUPBOX: return "com.sun.star.form.component.GroupBox"; + default: OSL_FAIL( "ControlModelBase::getServiceName - no form component service supported" ); + } + return OUString(); +} + +void ControlModelBase::importProperty( sal_Int32 /*nPropId*/, const OUString& /*rValue*/ ) +{ +} + +void ControlModelBase::importPictureData( sal_Int32 /*nPropId*/, BinaryInputStream& /*rInStrm*/ ) +{ +} + +void ControlModelBase::convertProperties( PropertyMap& /*rPropMap*/, const ControlConverter& /*rConv*/ ) const +{ +} + +void ControlModelBase::convertFromProperties( PropertySet& /*rPropMap*/, const ControlConverter& /*rConv*/ ) +{ +} + +void ControlModelBase::convertSize( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rConv.convertSize( rPropMap, maSize ); +} + +ComCtlModelBase::ComCtlModelBase( sal_uInt32 nDataPartId5, sal_uInt32 nDataPartId6, + sal_uInt16 nVersion ) : + maFontData( "Tahoma", 82500 ), + mnFlags( 0 ), + mnVersion( nVersion ), + mnDataPartId5( nDataPartId5 ), + mnDataPartId6( nDataPartId6 ), + mbCommonPart( true ), + mbComplexPart( true ) +{ +} + +bool ComCtlModelBase::importBinaryModel( BinaryInputStream& rInStrm ) +{ + // read initial size part and header of the control data part + if( importSizePart( rInStrm ) && readPartHeader( rInStrm, getDataPartId(), mnVersion ) ) + { + // if flags part exists, the first int32 of the data part contains its size + sal_uInt32 nCommonPartSize = 0; + if (mbCommonPart) + nCommonPartSize = rInStrm.readuInt32(); + // implementations must read the exact amount of data, stream must point to its end afterwards + importControlData( rInStrm ); + // read following parts + if( !rInStrm.isEof() && + (!mbCommonPart || importCommonPart( rInStrm, nCommonPartSize )) && + (!mbComplexPart || importComplexPart( rInStrm )) ) + { + return !rInStrm.isEof(); + } + } + return false; +} + +void ComCtlModelBase::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + if( mbCommonPart ) + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, COMCTL_COMMON_ENABLED ) ); + ControlModelBase::convertProperties( rPropMap, rConv ); +} + +sal_uInt32 ComCtlModelBase::getDataPartId() const +{ + switch( mnVersion ) + { + case COMCTL_VERSION_50: return mnDataPartId5; + case COMCTL_VERSION_60: return mnDataPartId6; + } + OSL_FAIL( "ComCtlObjectBase::getDataPartId - unexpected version" ); + return SAL_MAX_UINT32; +} + +bool ComCtlModelBase::readPartHeader( BinaryInputStream& rInStrm, sal_uInt32 nExpPartId, sal_uInt16 nExpMajor, sal_uInt16 nExpMinor ) +{ + // no idea if all this is correct... + sal_uInt32 nPartId = rInStrm.readuInt32(); + sal_uInt16 nMinor = rInStrm.readuInt16(); + sal_uInt16 nMajor = rInStrm.readuInt16(); + bool bPartId = nPartId == nExpPartId; + OSL_ENSURE( bPartId, "ComCtlObjectBase::readPartHeader - unexpected part identifier" ); + bool bVersion = ((nExpMajor == SAL_MAX_UINT16) || (nExpMajor == nMajor)) && ((nExpMinor == SAL_MAX_UINT16) || (nExpMinor == nMinor)); + OSL_ENSURE( bVersion, "ComCtlObjectBase::readPartHeader - unexpected part version" ); + return !rInStrm.isEof() && bPartId && bVersion; +} + +bool ComCtlModelBase::importSizePart( BinaryInputStream& rInStrm ) +{ + if( readPartHeader( rInStrm, COMCTL_ID_SIZE, 0, 8 ) ) + { + maSize.first = rInStrm.readInt32(); + maSize.second = rInStrm.readInt32(); + return !rInStrm.isEof(); + } + return false; +} + +bool ComCtlModelBase::importCommonPart( BinaryInputStream& rInStrm, sal_uInt32 nPartSize ) +{ + sal_Int64 nEndPos = rInStrm.tell() + nPartSize; + if( (nPartSize >= 16) && readPartHeader( rInStrm, COMCTL_ID_COMMONDATA, 5, 0 ) ) + { + rInStrm.skip( 4 ); + mnFlags = rInStrm.readuInt32(); + rInStrm.seek( nEndPos ); + return !rInStrm.isEof(); + } + return false; +} + +bool ComCtlModelBase::importComplexPart( BinaryInputStream& rInStrm ) +{ + if( readPartHeader( rInStrm, COMCTL_ID_COMPLEXDATA, 5, 1 ) ) + { + sal_uInt32 nContFlags = rInStrm.readuInt32(); + bool bReadOk = + (!getFlag( nContFlags, COMCTL_COMPLEX_FONT ) || OleHelper::importStdFont( maFontData, rInStrm, true )) && + (!getFlag( nContFlags, COMCTL_COMPLEX_MOUSEICON ) || OleHelper::importStdPic( maMouseIcon, rInStrm )); + return bReadOk && !rInStrm.isEof(); + } + return false; +} + +ComCtlScrollBarModel::ComCtlScrollBarModel( sal_uInt16 nVersion ) : + ComCtlModelBase( SAL_MAX_UINT32, COMCTL_ID_SCROLLBAR_60, nVersion ), + mnScrollBarFlags( 0x00000011 ), + mnLargeChange( 1 ), + mnSmallChange( 1 ), + mnMin( 0 ), + mnMax( 32767 ), + mnPosition( 0 ) +{ +} + +ApiControlType ComCtlScrollBarModel::getControlType() const +{ + return API_CONTROL_SCROLLBAR; +} + +void ComCtlScrollBarModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Border, API_BORDER_NONE ); + ControlConverter::convertOrientation( rPropMap, getFlag( mnScrollBarFlags, COMCTL_SCROLLBAR_HOR ) ); + ControlConverter::convertScrollBar( rPropMap, mnMin, mnMax, mnPosition, mnSmallChange, mnLargeChange, mbAwtModel ); + ComCtlModelBase::convertProperties( rPropMap, rConv ); +} + +void ComCtlScrollBarModel::importControlData( BinaryInputStream& rInStrm ) +{ + mnScrollBarFlags = rInStrm.readuInt32(); + mnLargeChange = rInStrm.readInt32(); + mnSmallChange = rInStrm.readInt32(); + mnMin = rInStrm.readInt32(); + mnMax = rInStrm.readInt32(); + mnPosition = rInStrm.readInt32(); +} + +ComCtlProgressBarModel::ComCtlProgressBarModel( sal_uInt16 nVersion ) : + ComCtlModelBase( COMCTL_ID_PROGRESSBAR_50, COMCTL_ID_PROGRESSBAR_60, nVersion ), + mfMin( 0.0 ), + mfMax( 100.0 ), + mnVertical( 0 ), + mnSmooth( 0 ) +{ +} + +ApiControlType ComCtlProgressBarModel::getControlType() const +{ + return API_CONTROL_PROGRESSBAR; +} + +void ComCtlProgressBarModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + sal_uInt16 nBorder = getFlag( mnFlags, COMCTL_COMMON_3DBORDER ) ? API_BORDER_SUNKEN : + (getFlag( mnFlags, COMCTL_COMMON_FLATBORDER ) ? API_BORDER_FLAT : API_BORDER_NONE); + rPropMap.setProperty( PROP_Border, nBorder ); + rPropMap.setProperty( PROP_ProgressValueMin, getLimitedValue< sal_Int32, double >( ::std::min( mfMin, mfMax ), 0.0, SAL_MAX_INT32 ) ); + rPropMap.setProperty( PROP_ProgressValueMax, getLimitedValue< sal_Int32, double >( ::std::max( mfMin, mfMax ), 0.0, SAL_MAX_INT32 ) ); + // ComCtl model does not provide current value? + ComCtlModelBase::convertProperties( rPropMap, rConv ); +} + +void ComCtlProgressBarModel::importControlData( BinaryInputStream& rInStrm ) +{ + mfMin = rInStrm.readFloat(); + mfMax = rInStrm.readFloat(); + if( mnVersion == COMCTL_VERSION_60 ) + { + mnVertical = rInStrm.readuInt16(); + mnSmooth = rInStrm.readuInt16(); + } +} + +AxControlModelBase::AxControlModelBase() +{ +} + +void AxControlModelBase::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + // size of the control shape: format is "width;height" + case XML_Size: + { + sal_Int32 nSepPos = rValue.indexOf( ';' ); + OSL_ENSURE( nSepPos >= 0, "AxControlModelBase::importProperty - missing separator in 'Size' property" ); + if( nSepPos >= 0 ) + { + maSize.first = o3tl::toInt32(rValue.subView( 0, nSepPos )); + maSize.second = o3tl::toInt32(rValue.subView( nSepPos + 1 )); + } + } + break; + } +} + +AxFontDataModel::AxFontDataModel( bool bSupportsAlign ) : + mbSupportsAlign( bSupportsAlign ) +{ +} + +void AxFontDataModel::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_FontName: maFontData.maFontName = rValue; break; + case XML_FontEffects: + maFontData.mnFontEffects = static_cast<AxFontFlags>(AttributeConversion::decodeUnsigned( rValue )); + break; + case XML_FontHeight: maFontData.mnFontHeight = AttributeConversion::decodeInteger( rValue ); break; + case XML_FontCharSet: maFontData.mnFontCharSet = AttributeConversion::decodeInteger( rValue ); break; + case XML_ParagraphAlign: + maFontData.mnHorAlign = static_cast<AxHorizontalAlign>(AttributeConversion::decodeInteger( rValue )); + break; + default: AxControlModelBase::importProperty( nPropId, rValue ); + } +} + +bool AxFontDataModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + return maFontData.importBinaryModel( rInStrm ); +} + +void AxFontDataModel::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + maFontData.exportBinaryModel( rOutStrm ); +} +void AxFontDataModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + // font name + if( !maFontData.maFontName.isEmpty() ) + rPropMap.setProperty( PROP_FontName, maFontData.maFontName ); + + // font effects + rPropMap.setProperty( PROP_FontWeight, maFontData.mnFontEffects & AxFontFlags::Bold ? awt::FontWeight::BOLD : awt::FontWeight::NORMAL ); + rPropMap.setProperty( PROP_FontSlant, maFontData.mnFontEffects & AxFontFlags::Italic ? FontSlant_ITALIC : FontSlant_NONE ); + if (maFontData.mnFontEffects & AxFontFlags::Underline) + rPropMap.setProperty( PROP_FontUnderline, maFontData.mbDblUnderline ? awt::FontUnderline::DOUBLE : awt::FontUnderline::SINGLE ); + else + rPropMap.setProperty( PROP_FontUnderline, awt::FontUnderline::NONE ); + rPropMap.setProperty( PROP_FontStrikeout, maFontData.mnFontEffects & AxFontFlags::Strikeout ? awt::FontStrikeout::SINGLE : awt::FontStrikeout::NONE ); + rPropMap.setProperty( PROP_FontHeight, maFontData.getHeightPoints() ); + + // font character set + rtl_TextEncoding eFontEnc = RTL_TEXTENCODING_DONTKNOW; + if( (0 <= maFontData.mnFontCharSet) && (maFontData.mnFontCharSet <= SAL_MAX_UINT8) ) + eFontEnc = rtl_getTextEncodingFromWindowsCharset( static_cast< sal_uInt8 >( maFontData.mnFontCharSet ) ); + if( eFontEnc != RTL_TEXTENCODING_DONTKNOW ) + rPropMap.setProperty( PROP_FontCharset, static_cast< sal_Int16 >( eFontEnc ) ); + + // text alignment + if( mbSupportsAlign ) + { + sal_Int32 nAlign = awt::TextAlign::LEFT; + switch( maFontData.mnHorAlign ) + { + case AxHorizontalAlign::Left: nAlign = awt::TextAlign::LEFT; break; + case AxHorizontalAlign::Right: nAlign = awt::TextAlign::RIGHT; break; + case AxHorizontalAlign::Center: nAlign = awt::TextAlign::CENTER; break; + default: OSL_FAIL( "AxFontDataModel::convertProperties - unknown text alignment" ); + } + // form controls expect short value + rPropMap.setProperty( PROP_Align, static_cast< sal_Int16 >( nAlign ) ); + } + + // process base class properties + AxControlModelBase::convertProperties( rPropMap, rConv ); +} + +void AxFontDataModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& /*rConv */) +{ + rPropSet.getProperty( maFontData.maFontName, PROP_FontName ); + float fontWeight = float(0); + if ( rPropSet.getProperty(fontWeight, PROP_FontWeight ) ) + setFlag( maFontData.mnFontEffects, AxFontFlags::Bold, ( fontWeight == awt::FontWeight::BOLD ) ); + FontSlant nSlant = FontSlant_NONE; + if ( rPropSet.getProperty( nSlant, PROP_FontSlant ) ) + setFlag( maFontData.mnFontEffects, AxFontFlags::Italic, ( nSlant == FontSlant_ITALIC ) ); + + sal_Int16 nUnderLine = awt::FontUnderline::NONE; + if ( rPropSet.getProperty( nUnderLine, PROP_FontUnderline ) ) + setFlag( maFontData.mnFontEffects, AxFontFlags::Underline, nUnderLine != awt::FontUnderline::NONE && nUnderLine != awt::FontUnderline::DONTKNOW); + sal_Int16 nStrikeout = awt::FontStrikeout::NONE ; + if ( rPropSet.getProperty( nStrikeout, PROP_FontStrikeout ) ) + setFlag( maFontData.mnFontEffects, AxFontFlags::Strikeout, nStrikeout != awt::FontStrikeout::NONE && nStrikeout != awt::FontStrikeout::DONTKNOW); + + float fontHeight = 0.0; + if ( rPropSet.getProperty( fontHeight, PROP_FontHeight ) ) + { + if ( fontHeight == 0 ) // tdf#118684 + { + vcl::Font aDefaultVCLFont = Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetAppFont(); + fontHeight = static_cast< float >( aDefaultVCLFont.GetFontHeight() ); + } + maFontData.setHeightPoints( static_cast< sal_Int16 >( fontHeight ) ); + } + + // TODO - handle textencoding + sal_Int16 nAlign = 0; + if ( rPropSet.getProperty( nAlign, PROP_Align ) ) + { + switch ( nAlign ) + { + case awt::TextAlign::LEFT: maFontData.mnHorAlign = AxHorizontalAlign::Left; break; + case awt::TextAlign::RIGHT: maFontData.mnHorAlign = AxHorizontalAlign::Right; break; + case awt::TextAlign::CENTER: maFontData.mnHorAlign = AxHorizontalAlign::Center; break; + default: OSL_FAIL( "AxFontDataModel::convertFromProperties - unknown text alignment" ); + } + } +} + +AxCommandButtonModel::AxCommandButtonModel() : + mnTextColor( AX_SYSCOLOR_BUTTONTEXT ), + mnBackColor( AX_SYSCOLOR_BUTTONFACE ), + mnFlags( AX_CMDBUTTON_DEFFLAGS ), + mnPicturePos( AX_PICPOS_ABOVECENTER ), + mnVerticalAlign( XML_Center ), + mbFocusOnClick( true ) +{ +} + +void AxCommandButtonModel::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_Caption: maCaption = rValue; break; + case XML_ForeColor: mnTextColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BackColor: mnBackColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_VariousPropertyBits: mnFlags = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_PicturePosition: mnPicturePos = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_TakeFocusOnClick: mbFocusOnClick = AttributeConversion::decodeInteger( rValue ) != 0; break; + default: AxFontDataModel::importProperty( nPropId, rValue ); + } +} + +void AxCommandButtonModel::importPictureData( sal_Int32 nPropId, BinaryInputStream& rInStrm ) +{ + switch( nPropId ) + { + case XML_Picture: OleHelper::importStdPic( maPictureData, rInStrm ); break; + default: AxFontDataModel::importPictureData( nPropId, rInStrm ); + } +} + +bool AxCommandButtonModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readIntProperty< sal_uInt32 >( mnTextColor ); + aReader.readIntProperty< sal_uInt32 >( mnBackColor ); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.readStringProperty( maCaption ); + aReader.readIntProperty< sal_uInt32 >( mnPicturePos ); + aReader.readPairProperty( maSize ); + aReader.skipIntProperty< sal_uInt8 >(); // mouse pointer + aReader.readPictureProperty( maPictureData ); + aReader.skipIntProperty< sal_uInt16 >(); // accelerator + aReader.readBoolProperty( mbFocusOnClick, true ); // binary flag means "do not take focus" + aReader.skipPictureProperty(); // mouse icon + return aReader.finalizeImport() && AxFontDataModel::importBinaryModel( rInStrm ); +} + +void AxCommandButtonModel::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + AxBinaryPropertyWriter aWriter( rOutStrm ); + aWriter.writeIntProperty< sal_uInt32 >( mnTextColor ); + if ( mnBackColor ) + aWriter.writeIntProperty< sal_uInt32 >( mnBackColor ); + else + aWriter.skipProperty(); // default backcolour + aWriter.writeIntProperty< sal_uInt32 >( mnFlags ); + aWriter.writeStringProperty( maCaption ); + aWriter.skipProperty(); // pict pos + aWriter.writePairProperty( maSize ); + aWriter.skipProperty(); // mouse pointer + aWriter.skipProperty(); // picture data + aWriter.skipProperty(); // accelerator + aWriter.writeBoolProperty( mbFocusOnClick ); // binary flag means "do not take focus" + aWriter.skipProperty(); // mouse icon + aWriter.finalizeExport(); + AxFontDataModel::exportBinaryModel( rOutStrm ); +} + +void AxCommandButtonModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x32, 0x05, 0xD7, + 0x69, 0xCE, 0xCD, 0x11, 0xA7, 0x77, 0x00, 0xDD, + 0x01, 0x14, 0x3C, 0x57, 0x22, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6d, 0x73, 0x20, + 0x32, 0x2e, 0x30, 0x20, 0x43, 0x6F, 0x6D, 0x6D, + 0x61, 0x6E, 0x64, 0x42, 0x75, 0x74, 0x74, 0x6F, + 0x6E, 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x6D, + 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x20, 0x4F, + 0x62, 0x6A, 0x65, 0x63, 0x74, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x2E, + 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x42, + 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x2E, 0x31, 0x00, + 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +ApiControlType AxCommandButtonModel::getControlType() const +{ + return API_CONTROL_BUTTON; +} + +void AxCommandButtonModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Label, maCaption ); + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_FLAGS_ENABLED ) ); + rPropMap.setProperty( PROP_MultiLine, getFlag( mnFlags, AX_FLAGS_WORDWRAP ) ); + rPropMap.setProperty( PROP_FocusOnClick, mbFocusOnClick ); + rConv.convertColor( rPropMap, PROP_TextColor, mnTextColor ); + ControlConverter::convertVerticalAlign( rPropMap, mnVerticalAlign ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::NotSupported ); + rConv.convertAxPicture( rPropMap, maPictureData, mnPicturePos ); + AxFontDataModel::convertProperties( rPropMap, rConv ); +} + +void AxCommandButtonModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + (void)rPropSet.getProperty(maCaption, PROP_Label); + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_Enabled ) ) + setFlag( mnFlags, AX_FLAGS_ENABLED, bRes ); + if ( rPropSet.getProperty( bRes, PROP_MultiLine ) ) + setFlag( mnFlags, AX_FLAGS_WORDWRAP, bRes ); + (void)rPropSet.getProperty(mbFocusOnClick, PROP_FocusOnClick); + + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + + AxFontDataModel::convertFromProperties( rPropSet, rConv ); +} + +AxLabelModel::AxLabelModel() : + mnTextColor( AX_SYSCOLOR_BUTTONTEXT ), + mnBackColor( AX_SYSCOLOR_BUTTONFACE ), + mnFlags( AX_LABEL_DEFFLAGS ), + mnBorderColor( AX_SYSCOLOR_WINDOWFRAME ), + mnBorderStyle( AX_BORDERSTYLE_NONE ), + mnSpecialEffect( AX_SPECIALEFFECT_FLAT ), + mnVerticalAlign( XML_Top ) +{ +} + +void AxLabelModel::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_Caption: maCaption = rValue; break; + case XML_ForeColor: mnTextColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BackColor: mnBackColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_VariousPropertyBits: mnFlags = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BorderColor: mnBorderColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BorderStyle: mnBorderStyle = AttributeConversion::decodeInteger( rValue ); break; + case XML_SpecialEffect: mnSpecialEffect = AttributeConversion::decodeInteger( rValue ); break; + default: AxFontDataModel::importProperty( nPropId, rValue ); + } +} + +bool AxLabelModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readIntProperty< sal_uInt32 >( mnTextColor ); + aReader.readIntProperty< sal_uInt32 >( mnBackColor ); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.readStringProperty( maCaption ); + aReader.skipIntProperty< sal_uInt32 >(); // picture position + aReader.readPairProperty( maSize ); + aReader.skipIntProperty< sal_uInt8 >(); // mouse pointer + aReader.readIntProperty< sal_uInt32 >( mnBorderColor ); + aReader.readIntProperty< sal_uInt16 >( mnBorderStyle ); + aReader.readIntProperty< sal_uInt16 >( mnSpecialEffect ); + aReader.skipPictureProperty(); // picture + aReader.skipIntProperty< sal_uInt16 >(); // accelerator + aReader.skipPictureProperty(); // mouse icon + return aReader.finalizeImport() && AxFontDataModel::importBinaryModel( rInStrm ); +} + +void AxLabelModel::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + AxBinaryPropertyWriter aWriter( rOutStrm ); + aWriter.writeIntProperty< sal_uInt32 >( mnTextColor ); + if ( mnBackColor ) + aWriter.writeIntProperty< sal_uInt32 >( mnBackColor ); + else + // if mnBackColor == 0 then it's the libreoffice default backcolour is + // the MSO Label default which is AX_SYSCOLOR_BUTTONFACE + aWriter.writeIntProperty< sal_uInt32 >( AX_SYSCOLOR_WINDOWBACK ); + aWriter.writeIntProperty< sal_uInt32 >( mnFlags ); + aWriter.writeStringProperty( maCaption ); + aWriter.skipProperty(); // picture position + aWriter.writePairProperty( maSize ); + aWriter.skipProperty(); // mouse pointer + aWriter.writeIntProperty< sal_uInt32 >( mnBorderColor ); + aWriter.writeIntProperty< sal_uInt16 >( mnBorderStyle ); + aWriter.writeIntProperty< sal_uInt16 >( mnSpecialEffect ); + aWriter.skipProperty(); // picture + aWriter.skipProperty(); // accelerator + aWriter.skipProperty(); // mouse icon + aWriter.finalizeExport(); + AxFontDataModel::exportBinaryModel( rOutStrm ); +} + +void AxLabelModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + rPropSet.getProperty( maCaption, PROP_Label ); + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_Enabled ) ) + setFlag( mnFlags, AX_FLAGS_ENABLED, bRes ); + if ( rPropSet.getProperty( bRes, PROP_MultiLine ) ) + setFlag( mnFlags, AX_FLAGS_WORDWRAP, bRes ); + + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + // VerticalAlign doesn't seem to be read from binary + + // not sure about background color, how do we decide when to set + // AX_FLAGS_OPAQUE ? + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + ControlConverter::convertToAxBorder( rPropSet, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + + AxFontDataModel::convertFromProperties( rPropSet, rConv ); +} + +void AxLabelModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x23, 0x9E, 0x8C, 0x97, + 0xB0, 0xD4, 0xCE, 0x11, 0xBF, 0x2D, 0x00, 0xAA, + 0x00, 0x3F, 0x40, 0xD0, 0x1A, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x4C, 0x61, 0x62, 0x65, + 0x6C, 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x6D, + 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x20, 0x4F, + 0x62, 0x6A, 0x65, 0x63, 0x74, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x2E, + 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x2E, 0x31, 0x00, + 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +ApiControlType AxLabelModel::getControlType() const +{ + return API_CONTROL_FIXEDTEXT; +} + +void AxLabelModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Label, maCaption ); + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_FLAGS_ENABLED ) ); + rPropMap.setProperty( PROP_MultiLine, getFlag( mnFlags, AX_FLAGS_WORDWRAP ) ); + rConv.convertColor( rPropMap, PROP_TextColor, mnTextColor ); + ControlConverter::convertVerticalAlign( rPropMap, mnVerticalAlign ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + rConv.convertAxBorder( rPropMap, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxFontDataModel::convertProperties( rPropMap, rConv ); +} + +AxImageModel::AxImageModel() : + mnBackColor( AX_SYSCOLOR_BUTTONFACE ), + mnFlags( AX_IMAGE_DEFFLAGS ), + mnBorderColor( AX_SYSCOLOR_WINDOWFRAME ), + mnBorderStyle( AX_BORDERSTYLE_SINGLE ), + mnSpecialEffect( AX_SPECIALEFFECT_FLAT ), + mnPicSizeMode( AX_PICSIZE_CLIP ), + mnPicAlign( AX_PICALIGN_CENTER ), + mbPicTiling( false ) +{ +} + +void AxImageModel::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_BackColor: mnBackColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_VariousPropertyBits: mnFlags = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BorderColor: mnBorderColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BorderStyle: mnBorderStyle = AttributeConversion::decodeInteger( rValue ); break; + case XML_SpecialEffect: mnSpecialEffect = AttributeConversion::decodeInteger( rValue ); break; + case XML_SizeMode: mnPicSizeMode = AttributeConversion::decodeInteger( rValue ); break; + case XML_PictureAlignment: mnPicAlign = AttributeConversion::decodeInteger( rValue ); break; + case XML_PictureTiling: mbPicTiling = AttributeConversion::decodeInteger( rValue ) != 0; break; + default: AxControlModelBase::importProperty( nPropId, rValue ); + } +} + +void AxImageModel::importPictureData( sal_Int32 nPropId, BinaryInputStream& rInStrm ) +{ + switch( nPropId ) + { + case XML_Picture: OleHelper::importStdPic( maPictureData, rInStrm ); break; + default: AxControlModelBase::importPictureData( nPropId, rInStrm ); + } +} + +bool AxImageModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.skipUndefinedProperty(); + aReader.skipUndefinedProperty(); + aReader.skipBoolProperty(); // auto-size + aReader.readIntProperty< sal_uInt32 >( mnBorderColor ); + aReader.readIntProperty< sal_uInt32 >( mnBackColor ); + aReader.readIntProperty< sal_uInt8 >( mnBorderStyle ); + aReader.skipIntProperty< sal_uInt8 >(); // mouse pointer + aReader.readIntProperty< sal_uInt8 >( mnPicSizeMode ); + aReader.readIntProperty< sal_uInt8 >( mnSpecialEffect ); + aReader.readPairProperty( maSize ); + aReader.readPictureProperty( maPictureData ); + aReader.readIntProperty< sal_uInt8 >( mnPicAlign ); + aReader.readBoolProperty( mbPicTiling ); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.skipPictureProperty(); // mouse icon + return aReader.finalizeImport(); +} + +void AxImageModel::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + AxBinaryPropertyWriter aWriter( rOutStrm ); + aWriter.skipProperty(); //undefined + aWriter.skipProperty(); //undefined + aWriter.skipProperty(); //auto-size + aWriter.writeIntProperty< sal_uInt32 >( mnBorderColor ); + if ( mnBackColor ) + aWriter.writeIntProperty< sal_uInt32 >( mnBackColor ); + else + aWriter.skipProperty(); // default backcolour + aWriter.writeIntProperty< sal_uInt8 >( mnBorderStyle ); + aWriter.skipProperty(); // mouse pointer + aWriter.writeIntProperty< sal_uInt8 >( mnPicSizeMode ); + aWriter.writeIntProperty< sal_uInt8 >( mnSpecialEffect ); + aWriter.writePairProperty( maSize ); + aWriter.skipProperty(); //maPictureData ); + aWriter.writeIntProperty< sal_uInt8 >( mnPicAlign ); + aWriter.writeBoolProperty( mbPicTiling ); + aWriter.writeIntProperty< sal_uInt32 >( mnFlags ); + aWriter.skipProperty(); // mouse icon + aWriter.finalizeExport(); +} + +void AxImageModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x41, 0x92, 0x59, 0x4C, + 0x26, 0x69, 0x1B, 0x10, 0x99, 0x92, 0x00, 0x00, + 0x0B, 0x65, 0xC6, 0xF9, 0x1A, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x49, 0x6D, 0x61, 0x67, + 0x65, 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x6D, + 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x20, 0x4F, + 0x62, 0x6A, 0x65, 0x63, 0x74, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x2E, + 0x49, 0x6D, 0x61, 0x67, 0x65, 0x2E, 0x31, 0x00, + 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +ApiControlType AxImageModel::getControlType() const +{ + return API_CONTROL_IMAGE; +} + +void AxImageModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_FLAGS_ENABLED ) ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + rConv.convertAxBorder( rPropMap, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + rConv.convertAxPicture( rPropMap, maPictureData, mnPicSizeMode ); + AxControlModelBase::convertProperties( rPropMap, rConv ); +} + +AxTabStripModel::AxTabStripModel() : + mnListIndex( 0 ), + mnTabStyle( 0 ), + mnTabData( 0 ), + mnVariousPropertyBits( 0 ) +{ +} + +bool AxTabStripModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + // not worth reading much here, basically we are interested + // in whether we have tabs, the width, the height and the + // captions, everything else we can pretty much discard ( for now ) + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readIntProperty< sal_uInt32 >( mnListIndex ); // ListIndex + aReader.skipIntProperty< sal_uInt32 >(); // Backcolor + aReader.skipIntProperty< sal_uInt32 >(); // ForeColor + aReader.skipUndefinedProperty(); + aReader.readPairProperty( maSize ); + aReader.readArrayStringProperty( maItems ); + aReader.skipIntProperty< sal_uInt8 >(); // MousePointer + aReader.skipUndefinedProperty(); + aReader.skipIntProperty< sal_uInt32 >(); // TabOrientation + aReader.readIntProperty< sal_uInt32 >(mnTabStyle); // TabStyle + aReader.skipBoolProperty(); // MultiRow + aReader.skipIntProperty< sal_uInt32 >(); // TabFixedWidth + aReader.skipIntProperty< sal_uInt32 >(); // TabFixedHeight + aReader.skipBoolProperty(); // ToolTips + aReader.skipUndefinedProperty(); + aReader.skipArrayStringProperty(); // ToolTip strings + aReader.skipUndefinedProperty(); + aReader.readArrayStringProperty( maTabNames ); // Tab names + aReader.readIntProperty< sal_uInt32 >(mnVariousPropertyBits); // VariousPropertyBits + aReader.skipBoolProperty();// NewVersion + aReader.skipIntProperty< sal_uInt32 >(); // TabsAllocated + aReader.skipArrayStringProperty(); // Tags + aReader.readIntProperty<sal_uInt32 >(mnTabData); // TabData + aReader.skipArrayStringProperty(); // Accelerators + aReader.skipPictureProperty(); // Mouse Icon + return aReader.finalizeImport() && AxFontDataModel::importBinaryModel( rInStrm ); +} + +ApiControlType AxTabStripModel::getControlType() const +{ + return API_CONTROL_TABSTRIP; +} + +AxMorphDataModelBase::AxMorphDataModelBase() : + mnTextColor( AX_SYSCOLOR_WINDOWTEXT ), + mnBackColor( AX_SYSCOLOR_WINDOWBACK ), + mnFlags( AX_MORPHDATA_DEFFLAGS ), + mnPicturePos( AX_PICPOS_ABOVECENTER ), + mnBorderColor( AX_SYSCOLOR_WINDOWFRAME ), + mnBorderStyle( AX_BORDERSTYLE_NONE ), + mnSpecialEffect( AX_SPECIALEFFECT_SUNKEN ), + mnDisplayStyle( AX_DISPLAYSTYLE_TEXT ), + mnMultiSelect( AX_SELECTION_SINGLE ), + mnScrollBars( AX_SCROLLBAR_NONE ), + mnMatchEntry( AX_MATCHENTRY_NONE ), + mnShowDropButton( AX_SHOWDROPBUTTON_NEVER ), + mnMaxLength( 0 ), + mnPasswordChar( 0 ), + mnListRows( 8 ), + mnVerticalAlign( XML_Center ) +{ +} + +void AxMorphDataModelBase::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_Caption: maCaption = rValue; break; + case XML_Value: maValue = rValue; break; + case XML_GroupName: maGroupName = rValue; break; + case XML_ForeColor: mnTextColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BackColor: mnBackColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_VariousPropertyBits: mnFlags = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_PicturePosition: mnPicturePos = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BorderColor: mnBorderColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BorderStyle: mnBorderStyle = AttributeConversion::decodeInteger( rValue ); break; + case XML_SpecialEffect: mnSpecialEffect = AttributeConversion::decodeInteger( rValue ); break; + case XML_DisplayStyle: mnDisplayStyle = AttributeConversion::decodeInteger( rValue ); break; + case XML_MultiSelect: mnMultiSelect = AttributeConversion::decodeInteger( rValue ); break; + case XML_ScrollBars: mnScrollBars = AttributeConversion::decodeInteger( rValue ); break; + case XML_MatchEntry: mnMatchEntry = AttributeConversion::decodeInteger( rValue ); break; + case XML_ShowDropButtonWhen: mnShowDropButton = AttributeConversion::decodeInteger( rValue );break; + case XML_MaxLength: mnMaxLength = AttributeConversion::decodeInteger( rValue ); break; + case XML_PasswordChar: mnPasswordChar = AttributeConversion::decodeInteger( rValue ); break; + case XML_ListRows: mnListRows = AttributeConversion::decodeInteger( rValue ); break; + default: AxFontDataModel::importProperty( nPropId, rValue ); + } +} + +void AxMorphDataModelBase::importPictureData( sal_Int32 nPropId, BinaryInputStream& rInStrm ) +{ + switch( nPropId ) + { + case XML_Picture: OleHelper::importStdPic( maPictureData, rInStrm ); break; + default: AxFontDataModel::importPictureData( nPropId, rInStrm ); + } +} + +bool AxMorphDataModelBase::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm, true ); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.readIntProperty< sal_uInt32 >( mnBackColor ); + aReader.readIntProperty< sal_uInt32 >( mnTextColor ); + aReader.readIntProperty< sal_Int32 >( mnMaxLength ); + aReader.readIntProperty< sal_uInt8 >( mnBorderStyle ); + aReader.readIntProperty< sal_uInt8 >( mnScrollBars ); + aReader.readIntProperty< sal_uInt8 >( mnDisplayStyle ); + aReader.skipIntProperty< sal_uInt8 >(); // mouse pointer + aReader.readPairProperty( maSize ); + aReader.readIntProperty< sal_uInt16 >( mnPasswordChar ); + aReader.skipIntProperty< sal_uInt32 >(); // list width + aReader.skipIntProperty< sal_uInt16 >(); // bound column + aReader.skipIntProperty< sal_Int16 >(); // text column + aReader.skipIntProperty< sal_Int16 >(); // column count + aReader.readIntProperty< sal_uInt16 >( mnListRows ); + aReader.skipIntProperty< sal_uInt16 >(); // column info count + aReader.readIntProperty< sal_uInt8 >( mnMatchEntry ); + aReader.skipIntProperty< sal_uInt8 >(); // list style + aReader.readIntProperty< sal_uInt8 >( mnShowDropButton ); + aReader.skipUndefinedProperty(); + aReader.skipIntProperty< sal_uInt8 >(); // drop down style + aReader.readIntProperty< sal_uInt8 >( mnMultiSelect ); + aReader.readStringProperty( maValue ); + aReader.readStringProperty( maCaption ); + aReader.readIntProperty< sal_uInt32 >( mnPicturePos ); + aReader.readIntProperty< sal_uInt32 >( mnBorderColor ); + aReader.readIntProperty< sal_uInt32 >( mnSpecialEffect ); + aReader.skipPictureProperty(); // mouse icon + aReader.readPictureProperty( maPictureData ); + aReader.skipIntProperty< sal_uInt16 >(); // accelerator + aReader.skipUndefinedProperty(); + aReader.skipBoolProperty(); + aReader.readStringProperty( maGroupName ); + return aReader.finalizeImport() && AxFontDataModel::importBinaryModel( rInStrm ); +} + +void AxMorphDataModelBase::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + AxBinaryPropertyWriter aWriter( rOutStrm, true ); + if ( mnFlags != AX_MORPHDATA_DEFFLAGS ) + aWriter.writeIntProperty< sal_uInt32 >( mnFlags ); + else + aWriter.skipProperty(); //mnFlags + if ( mnBackColor ) + aWriter.writeIntProperty< sal_uInt32 >( mnBackColor ); + else + aWriter.skipProperty(); // default backcolour + aWriter.writeIntProperty< sal_uInt32 >( mnTextColor ); + + // only write if different from default + if ( ( ( mnDisplayStyle == AX_DISPLAYSTYLE_TEXT ) || ( mnDisplayStyle == AX_DISPLAYSTYLE_COMBOBOX ) ) && mnMaxLength != 0 ) + aWriter.writeIntProperty< sal_Int32 >( mnMaxLength ); + else + aWriter.skipProperty(); //mnMaxLength + if ( ( ( mnDisplayStyle == AX_DISPLAYSTYLE_COMBOBOX ) || ( mnDisplayStyle == AX_DISPLAYSTYLE_LISTBOX ) || ( mnDisplayStyle == AX_DISPLAYSTYLE_TEXT ) ) && mnBorderStyle != AX_BORDERSTYLE_NONE ) + aWriter.writeIntProperty< sal_uInt8 >( mnBorderStyle ); + else + aWriter.skipProperty(); //mnBorderStyle + + if ( ( mnDisplayStyle == AX_DISPLAYSTYLE_LISTBOX || mnDisplayStyle == AX_DISPLAYSTYLE_TEXT ) && mnScrollBars != AX_SCROLLBAR_NONE ) + aWriter.writeIntProperty< sal_uInt8 >( mnScrollBars ); + else + aWriter.skipProperty(); //mnScrollBars + aWriter.writeIntProperty< sal_uInt8 >( mnDisplayStyle ); + aWriter.skipProperty(); // mouse pointer + aWriter.writePairProperty( maSize ); + if ( mnDisplayStyle == AX_DISPLAYSTYLE_TEXT ) + aWriter.writeIntProperty< sal_uInt16 >( mnPasswordChar ); + else + aWriter.skipProperty(); // mnPasswordChar + aWriter.skipProperty(); // list width + aWriter.skipProperty(); // bound column + aWriter.skipProperty(); // text column + aWriter.skipProperty(); // column count + aWriter.skipProperty(); // mnListRows + aWriter.skipProperty(); // column info count + aWriter.skipProperty(); // mnMatchEntry + aWriter.skipProperty(); // list style + aWriter.skipProperty(); // mnShowDropButton ); + aWriter.skipProperty(); + aWriter.skipProperty(); // drop down style + if ( (mnDisplayStyle == AX_DISPLAYSTYLE_LISTBOX || mnDisplayStyle == AX_DISPLAYSTYLE_CHECKBOX) && mnMultiSelect != AX_SELECTION_SINGLE ) + aWriter.writeIntProperty< sal_uInt8 >( mnMultiSelect ); + // although CheckBox, ListBox, OptionButton, ToggleButton are also supported + // they can only have the fileformat default + else + aWriter.skipProperty(); //mnMultiSelect + aWriter.writeStringProperty( maValue ); + + if ( ( mnDisplayStyle == AX_DISPLAYSTYLE_CHECKBOX ) || ( mnDisplayStyle == AX_DISPLAYSTYLE_OPTBUTTON ) || ( mnDisplayStyle == AX_DISPLAYSTYLE_TOGGLE ) ) + aWriter.writeStringProperty( maCaption ); + else + aWriter.skipProperty(); // mnCaption + aWriter.skipProperty(); // mnPicturePos ); + if ( ( mnDisplayStyle == AX_DISPLAYSTYLE_COMBOBOX || mnDisplayStyle == AX_DISPLAYSTYLE_LISTBOX || mnDisplayStyle == AX_DISPLAYSTYLE_TEXT ) && mnBorderColor != AX_SYSCOLOR_WINDOWFRAME ) + aWriter.writeIntProperty< sal_uInt32 >( mnBorderColor ); + else + aWriter.skipProperty(); // mnBorderColor + if ( mnSpecialEffect != AX_SPECIALEFFECT_SUNKEN ) + aWriter.writeIntProperty< sal_uInt32 >( mnSpecialEffect ); + else + aWriter.skipProperty(); //mnSpecialEffect + aWriter.skipProperty(); // mouse icon + aWriter.skipProperty(); // maPictureData + aWriter.skipProperty(); // accelerator + aWriter.skipProperty(); // undefined + aWriter.writeBoolProperty(true); // must be 1 for morph + if ( mnDisplayStyle == AX_DISPLAYSTYLE_OPTBUTTON ) + aWriter.writeStringProperty( maGroupName ); + else + aWriter.skipProperty(); //maGroupName + aWriter.finalizeExport(); + AxFontDataModel::exportBinaryModel( rOutStrm ); +} + +void AxMorphDataModelBase::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_FLAGS_ENABLED ) ); + rConv.convertColor( rPropMap, PROP_TextColor, mnTextColor ); + if ( mnDisplayStyle == AX_DISPLAYSTYLE_OPTBUTTON ) + { + // If unspecified, radio buttons autoGroup in the same document/sheet + // NOTE: form controls should not autoGroup with ActiveX controls - see drawingfragment.cxx + OUString sGroupName = !maGroupName.isEmpty() ? maGroupName : "autoGroup_"; + rPropMap.setProperty( PROP_GroupName, sGroupName ); + } + AxFontDataModel::convertProperties( rPropMap, rConv ); +} + +void AxMorphDataModelBase::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + if ( mnDisplayStyle == AX_DISPLAYSTYLE_OPTBUTTON ) + rPropSet.getProperty( maGroupName, PROP_GroupName ); + AxFontDataModel::convertFromProperties( rPropSet, rConv ); +} + +AxToggleButtonModel::AxToggleButtonModel() +{ + mnDisplayStyle = AX_DISPLAYSTYLE_TOGGLE; +} + +ApiControlType AxToggleButtonModel::getControlType() const +{ + OSL_ENSURE( mnDisplayStyle == AX_DISPLAYSTYLE_TOGGLE, "AxToggleButtonModel::getControlType - invalid control type" ); + return API_CONTROL_BUTTON; +} + +void AxToggleButtonModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + rPropSet.getProperty( maCaption, PROP_Label ); + + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_MultiLine ) ) + setFlag( mnFlags, AX_FLAGS_WORDWRAP, bRes ); + + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + // need to process the image if one exists + ControlConverter::convertToAxState( rPropSet, maValue, mnMultiSelect, API_DEFAULTSTATE_BOOLEAN ); + AxMorphDataModelBase::convertFromProperties( rPropSet, rConv ); +} + +void AxToggleButtonModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Label, maCaption ); + rPropMap.setProperty( PROP_MultiLine, getFlag( mnFlags, AX_FLAGS_WORDWRAP ) ); + rPropMap.setProperty( PROP_Toggle, true ); + ControlConverter::convertVerticalAlign( rPropMap, mnVerticalAlign ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::NotSupported ); + rConv.convertAxPicture( rPropMap, maPictureData, mnPicturePos ); + ControlConverter::convertAxState( rPropMap, maValue, mnMultiSelect, API_DEFAULTSTATE_BOOLEAN, mbAwtModel ); + AxMorphDataModelBase::convertProperties( rPropMap, rConv ); +} + +void AxToggleButtonModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x1D, 0xD2, 0x8B, + 0x42, 0xEC, 0xCE, 0x11, 0x9E, 0x0D, 0x00, 0xAA, + 0x00, 0x60, 0x02, 0xF3, 0x21, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x54, 0x6F, 0x67, 0x67, + 0x6C, 0x65, 0x42, 0x75, 0x74, 0x74, 0x6F, 0x6E, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x6D, 0x62, + 0x65, 0x64, 0x64, 0x65, 0x64, 0x20, 0x4F, 0x62, + 0x6A, 0x65, 0x63, 0x74, 0x00, 0x15, 0x00, 0x00, + 0x00, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x2E, 0x54, + 0x6F, 0x67, 0x67, 0x6C, 0x65, 0x42, 0x75, 0x74, + 0x74, 0x6F, 0x6E, 0x2E, 0x31, 0x00, 0xF4, 0x39, + 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxCheckBoxModel::AxCheckBoxModel() +{ + mnDisplayStyle = AX_DISPLAYSTYLE_CHECKBOX; +} + +ApiControlType AxCheckBoxModel::getControlType() const +{ + OSL_ENSURE( mnDisplayStyle == AX_DISPLAYSTYLE_CHECKBOX, "AxCheckBoxModel::getControlType - invalid control type" ); + return API_CONTROL_CHECKBOX; +} + +void AxCheckBoxModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Label, maCaption ); + rPropMap.setProperty( PROP_MultiLine, getFlag( mnFlags, AX_FLAGS_WORDWRAP ) ); + ControlConverter::convertVerticalAlign( rPropMap, mnVerticalAlign ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + ControlConverter::convertAxVisualEffect( rPropMap, mnSpecialEffect ); + rConv.convertAxPicture( rPropMap, maPictureData, mnPicturePos ); + ControlConverter::convertAxState( rPropMap, maValue, mnMultiSelect, API_DEFAULTSTATE_TRISTATE, mbAwtModel ); + AxMorphDataModelBase::convertProperties( rPropMap, rConv ); +} + +void AxCheckBoxModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + rPropSet.getProperty( maCaption, PROP_Label ); + + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_MultiLine ) ) + setFlag( mnFlags, AX_FLAGS_WORDWRAP, bRes ); + + ControlConverter::convertToAxVisualEffect( rPropSet, mnSpecialEffect ); + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + // need to process the image if one exists + ControlConverter::convertToAxState( rPropSet, maValue, mnMultiSelect, API_DEFAULTSTATE_TRISTATE ); + AxMorphDataModelBase::convertFromProperties( rPropSet, rConv ); +} + +void AxCheckBoxModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x1D, 0xD2, 0x8B, + 0x42, 0xEC, 0xCE, 0x11, 0x9E, 0x0D, 0x00, 0xAA, + 0x00, 0x60, 0x02, 0xF3, 0x1D, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x43, 0x68, 0x65, 0x63, + 0x6B, 0x42, 0x6F, 0x78, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x45, 0x6D, 0x62, 0x65, 0x64, 0x64, 0x65, + 0x64, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, + 0x00, 0x11, 0x00, 0x00, 0x00, 0x46, 0x6F, 0x72, + 0x6D, 0x73, 0x2E, 0x43, 0x68, 0x65, 0x63, 0x6B, + 0x42, 0x6F, 0x78, 0x2E, 0x31, 0x00, 0xF4, 0x39, + 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxOptionButtonModel::AxOptionButtonModel() +{ + mnDisplayStyle = AX_DISPLAYSTYLE_OPTBUTTON; +} + +ApiControlType AxOptionButtonModel::getControlType() const +{ + OSL_ENSURE( mnDisplayStyle == AX_DISPLAYSTYLE_OPTBUTTON, "AxOptionButtonModel::getControlType - invalid control type" ); + return API_CONTROL_RADIOBUTTON; +} + +void AxOptionButtonModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Label, maCaption ); + rPropMap.setProperty( PROP_MultiLine, getFlag( mnFlags, AX_FLAGS_WORDWRAP ) ); + ControlConverter::convertVerticalAlign( rPropMap, mnVerticalAlign ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + ControlConverter::convertAxVisualEffect( rPropMap, mnSpecialEffect ); + rConv.convertAxPicture( rPropMap, maPictureData, mnPicturePos ); + ControlConverter::convertAxState( rPropMap, maValue, mnMultiSelect, API_DEFAULTSTATE_SHORT, mbAwtModel ); + AxMorphDataModelBase::convertProperties( rPropMap, rConv ); +} + +void AxOptionButtonModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + rPropSet.getProperty( maCaption, PROP_Label ); + + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_MultiLine ) ) + setFlag( mnFlags, AX_FLAGS_WORDWRAP, bRes ); + + ControlConverter::convertToAxVisualEffect( rPropSet, mnSpecialEffect ); + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + // need to process the image if one exists + ControlConverter::convertToAxState( rPropSet, maValue, mnMultiSelect, API_DEFAULTSTATE_BOOLEAN ); + AxMorphDataModelBase::convertFromProperties( rPropSet, rConv ); +} + +void AxOptionButtonModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0x1D, 0xD2, 0x8B, + 0x42, 0xEC, 0xCE, 0x11, 0x9E, 0x0D, 0x00, 0xAA, + 0x00, 0x60, 0x02, 0xF3, 0x21, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x4F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x42, 0x75, 0x74, 0x74, 0x6F, 0x6E, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x6D, 0x62, + 0x65, 0x64, 0x64, 0x65, 0x64, 0x20, 0x4F, 0x62, + 0x6A, 0x65, 0x63, 0x74, 0x00, 0x15, 0x00, 0x00, + 0x00, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x2E, 0x4F, + 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x42, 0x75, 0x74, + 0x74, 0x6F, 0x6E, 0x2E, 0x31, 0x00, 0xF4, 0x39, + 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxTextBoxModel::AxTextBoxModel() +{ + mnDisplayStyle = AX_DISPLAYSTYLE_TEXT; +} + +ApiControlType AxTextBoxModel::getControlType() const +{ + OSL_ENSURE( mnDisplayStyle == AX_DISPLAYSTYLE_TEXT, "AxTextBoxModel::getControlType - invalid control type" ); + return API_CONTROL_EDIT; +} + +void AxTextBoxModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + if (getFlag( mnFlags, AX_FLAGS_MULTILINE ) && getFlag( mnFlags, AX_FLAGS_WORDWRAP )) + rPropMap.setProperty( PROP_MultiLine, true ); + else + rPropMap.setProperty( PROP_MultiLine, false ); + rPropMap.setProperty( PROP_HideInactiveSelection, getFlag( mnFlags, AX_FLAGS_HIDESELECTION ) ); + rPropMap.setProperty( PROP_ReadOnly, getFlag( mnFlags, AX_FLAGS_LOCKED ) ); + rPropMap.setProperty( mbAwtModel ? PROP_Text : PROP_DefaultText, maValue ); + rPropMap.setProperty( PROP_MaxTextLen, getLimitedValue< sal_Int16, sal_Int32 >( mnMaxLength, 0, SAL_MAX_INT16 ) ); + if( (0 < mnPasswordChar) && (mnPasswordChar <= SAL_MAX_INT16) ) + rPropMap.setProperty( PROP_EchoChar, static_cast< sal_Int16 >( mnPasswordChar ) ); + rPropMap.setProperty( PROP_HScroll, getFlag( mnScrollBars, AX_SCROLLBAR_HORIZONTAL ) ); + rPropMap.setProperty( PROP_VScroll, getFlag( mnScrollBars, AX_SCROLLBAR_VERTICAL ) ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + rConv.convertAxBorder( rPropMap, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxMorphDataModelBase::convertProperties( rPropMap, rConv ); +} + +void AxTextBoxModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_MultiLine ) ) { + setFlag( mnFlags, AX_FLAGS_WORDWRAP, bRes ); + setFlag( mnFlags, AX_FLAGS_MULTILINE, bRes ); + } + if ( rPropSet.getProperty( bRes, PROP_HideInactiveSelection ) ) + setFlag( mnFlags, AX_FLAGS_HIDESELECTION, bRes ); + if ( rPropSet.getProperty( bRes, PROP_ReadOnly ) ) + setFlag( mnFlags, AX_FLAGS_LOCKED, bRes ); + rPropSet.getProperty( maValue, ( mbAwtModel ? PROP_Text : PROP_DefaultText ) ); + if (maValue.isEmpty() && !mbAwtModel) + // No default value? Then try exporting the current one. + rPropSet.getProperty( maValue, PROP_Text); + sal_Int16 nTmp(0); + if ( rPropSet.getProperty( nTmp, PROP_MaxTextLen ) ) + mnMaxLength = nTmp; + if ( rPropSet.getProperty( nTmp, PROP_EchoChar ) ) + mnPasswordChar = nTmp; + if ( rPropSet.getProperty( bRes, PROP_HScroll ) ) + setFlag( mnScrollBars, AX_SCROLLBAR_HORIZONTAL, bRes ); + if ( rPropSet.getProperty( bRes, PROP_VScroll ) ) + setFlag( mnScrollBars, AX_SCROLLBAR_VERTICAL, bRes ); + + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor, 0x80000005L ); + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + + ControlConverter::convertToAxBorder( rPropSet, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxMorphDataModelBase::convertFromProperties( rPropSet, rConv ); +} + +void AxTextBoxModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x10, 0x1D, 0xD2, 0x8B, + 0x42, 0xEC, 0xCE, 0x11, 0x9E, 0x0D, 0x00, 0xAA, + 0x00, 0x60, 0x02, 0xF3, 0x1C, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x54, 0x65, 0x78, 0x74, + 0x42, 0x6F, 0x78, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x45, 0x6D, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, + 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x46, 0x6F, 0x72, 0x6D, + 0x73, 0x2E, 0x54, 0x65, 0x78, 0x74, 0x42, 0x6F, + 0x78, 0x2E, 0x31, 0x00, 0xF4, 0x39, 0xB2, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxNumericFieldModel::AxNumericFieldModel() +{ + mnDisplayStyle = AX_DISPLAYSTYLE_TEXT; +} + +ApiControlType AxNumericFieldModel::getControlType() const +{ + OSL_ENSURE( mnDisplayStyle == AX_DISPLAYSTYLE_TEXT, "AxNumericFieldModel::getControlType - invalid control type" ); + return API_CONTROL_NUMERIC; +} + +void AxNumericFieldModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_HideInactiveSelection, getFlag( mnFlags, AX_FLAGS_HIDESELECTION ) ); + // TODO: OUString::toDouble() does not handle local decimal separator + rPropMap.setProperty( mbAwtModel ? PROP_Value : PROP_DefaultValue, maValue.toDouble() ); + rPropMap.setProperty( PROP_Spin, getFlag( mnScrollBars, AX_SCROLLBAR_VERTICAL ) ); + rPropMap.setProperty( PROP_Repeat, true ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + rConv.convertAxBorder( rPropMap, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxMorphDataModelBase::convertProperties( rPropMap, rConv ); +} + +void AxNumericFieldModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_HideInactiveSelection ) ) + setFlag( mnFlags, AX_FLAGS_HIDESELECTION, bRes ); + rPropSet.getProperty( maValue, ( mbAwtModel ? PROP_Text : PROP_DefaultText ) ); + if ( rPropSet.getProperty( bRes, PROP_Spin ) ) + setFlag( mnScrollBars, AX_SCROLLBAR_VERTICAL, bRes ); + + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + + ControlConverter::convertToAxBorder( rPropSet, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxMorphDataModelBase::convertFromProperties( rPropSet, rConv ); +} + +void AxNumericFieldModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x10, 0x1D, 0xD2, 0x8B, + 0x42, 0xEC, 0xCE, 0x11, 0x9E, 0x0D, 0x00, 0xAA, + 0x00, 0x60, 0x02, 0xF3, 0x1C, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x54, 0x65, 0x78, 0x74, + 0x42, 0x6F, 0x78, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x45, 0x6D, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, + 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x46, 0x6F, 0x72, 0x6D, + 0x73, 0x2E, 0x54, 0x65, 0x78, 0x74, 0x42, 0x6F, + 0x78, 0x2E, 0x31, 0x00, 0xF4, 0x39, 0xB2, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxListBoxModel::AxListBoxModel() +{ + mnDisplayStyle = AX_DISPLAYSTYLE_LISTBOX; +} + +ApiControlType AxListBoxModel::getControlType() const +{ + OSL_ENSURE( mnDisplayStyle == AX_DISPLAYSTYLE_LISTBOX, "AxListBoxModel::getControlType - invalid control type" ); + return API_CONTROL_LISTBOX; +} + +void AxListBoxModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + bool bMultiSelect = (mnMultiSelect == AX_SELECTION_MULTI) || (mnMultiSelect == AX_SELECTION_EXTENDED); + rPropMap.setProperty( PROP_MultiSelection, bMultiSelect ); + rPropMap.setProperty( PROP_Dropdown, false ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + rConv.convertAxBorder( rPropMap, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxMorphDataModelBase::convertProperties( rPropMap, rConv ); +} + +void AxListBoxModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_MultiSelection ) ) + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + + ControlConverter::convertToAxBorder( rPropSet, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + AxMorphDataModelBase::convertFromProperties( rPropSet, rConv ); +} + +void AxListBoxModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x1D, 0xD2, 0x8B, + 0x42, 0xEC, 0xCE, 0x11, 0x9E, 0x0D, 0x00, 0xAA, + 0x00, 0x60, 0x02, 0xF3, 0x1C, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x4C, 0x69, 0x73, 0x74, + 0x42, 0x6F, 0x78, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x45, 0x6D, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, + 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x46, 0x6F, 0x72, 0x6D, + 0x73, 0x2E, 0x4C, 0x69, 0x73, 0x74, 0x42, 0x6F, + 0x78, 0x2E, 0x31, 0x00, 0xF4, 0x39, 0xB2, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxComboBoxModel::AxComboBoxModel() +{ + mnDisplayStyle = AX_DISPLAYSTYLE_COMBOBOX; + mnFlags = 0x2c80481b; +} + +ApiControlType AxComboBoxModel::getControlType() const +{ + OSL_ENSURE( (mnDisplayStyle == AX_DISPLAYSTYLE_COMBOBOX) || (mnDisplayStyle == AX_DISPLAYSTYLE_DROPDOWN), "AxComboBoxModel::getControlType - invalid control type" ); + return (mnDisplayStyle == AX_DISPLAYSTYLE_DROPDOWN) ? API_CONTROL_LISTBOX : API_CONTROL_COMBOBOX; +} + +void AxComboBoxModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + if( mnDisplayStyle != AX_DISPLAYSTYLE_DROPDOWN ) + { + rPropMap.setProperty( PROP_HideInactiveSelection, getFlag( mnFlags, AX_FLAGS_HIDESELECTION ) ); + rPropMap.setProperty( mbAwtModel ? PROP_Text : PROP_DefaultText, maValue ); + rPropMap.setProperty( PROP_MaxTextLen, getLimitedValue< sal_Int16, sal_Int32 >( mnMaxLength, 0, SAL_MAX_INT16 ) ); + bool bAutoComplete = (mnMatchEntry == AX_MATCHENTRY_FIRSTLETTER) || (mnMatchEntry == AX_MATCHENTRY_COMPLETE); + rPropMap.setProperty( PROP_Autocomplete, bAutoComplete ); + } + bool bShowDropdown = (mnShowDropButton == AX_SHOWDROPBUTTON_FOCUS) || (mnShowDropButton == AX_SHOWDROPBUTTON_ALWAYS); + rPropMap.setProperty( PROP_Dropdown, bShowDropdown ); + rPropMap.setProperty( PROP_LineCount, getLimitedValue< sal_Int16, sal_Int32 >( mnListRows, 1, SAL_MAX_INT16 ) ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::Void ); + rConv.convertAxBorder( rPropMap, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxMorphDataModelBase::convertProperties( rPropMap, rConv ); +} + +void AxComboBoxModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& rConv ) +{ + // when would we have mnDisplayStyle = AX_DISPLAYSTYLE_DROPDOWN ? + // #TODO check against msocximex + mnDisplayStyle = AX_DISPLAYSTYLE_COMBOBOX; + bool bRes = false; + + if ( rPropSet.getProperty( bRes, PROP_HideInactiveSelection ) ) + setFlag( mnFlags, AX_FLAGS_HIDESELECTION, bRes ); + rPropSet.getProperty( maValue, ( mbAwtModel ? PROP_Text : PROP_DefaultText ) ); + + sal_Int16 nTmp(0); + if ( rPropSet.getProperty( nTmp, PROP_MaxTextLen ) ) + mnMaxLength = nTmp; + if ( rPropSet.getProperty( bRes, PROP_Autocomplete ) ) + { + // when to choose AX_MATCHENTRY_FIRSTLETTER ? + // #TODO check against msocximex + if ( bRes ) + mnMatchEntry = AX_MATCHENTRY_COMPLETE; + } + if ( rPropSet.getProperty( bRes, PROP_Dropdown ) ) + { + rPropSet.getProperty( mnListRows, PROP_LineCount ); + if ( !mnListRows ) + mnListRows = 1; + } + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + ControlConverter::convertToMSColor( rPropSet, PROP_TextColor, mnTextColor ); + + ControlConverter::convertToAxBorder( rPropSet, mnBorderColor, mnBorderStyle, mnSpecialEffect ); + AxMorphDataModelBase::convertFromProperties( rPropSet, rConv ); +} + +void AxComboBoxModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x1D, 0xD2, 0x8B, + 0x42, 0xEC, 0xCE, 0x11, 0x9E, 0x0D, 0x00, 0xAA, + 0x00, 0x60, 0x02, 0xF3, 0x1D, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x43, 0x6F, 0x6D, 0x62, + 0x6F, 0x42, 0x6F, 0x78, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x45, 0x6D, 0x62, 0x65, 0x64, 0x64, 0x65, + 0x64, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, + 0x00, 0x11, 0x00, 0x00, 0x00, 0x46, 0x6F, 0x72, + 0x6D, 0x73, 0x2E, 0x43, 0x6F, 0x6D, 0x62, 0x6F, + 0x42, 0x6F, 0x78, 0x2E, 0x31, 0x00, 0xF4, 0x39, + 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxSpinButtonModel::AxSpinButtonModel() : + mnArrowColor( AX_SYSCOLOR_BUTTONTEXT ), + mnBackColor( AX_SYSCOLOR_BUTTONFACE ), + mnFlags( AX_SPINBUTTON_DEFFLAGS ), + mnOrientation( AX_ORIENTATION_AUTO ), + mnMin( 0 ), + mnMax( 100 ), + mnPosition( 0 ), + mnSmallChange( 1 ), + mnDelay( 50 ) +{ +} + +ApiControlType AxSpinButtonModel::getControlType() const +{ + return API_CONTROL_SPINBUTTON; +} + +void AxSpinButtonModel::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_ForeColor: mnArrowColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BackColor: mnBackColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_VariousPropertyBits: mnFlags = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_Orientation: mnOrientation = AttributeConversion::decodeInteger( rValue ); break; + case XML_Min: mnMin = AttributeConversion::decodeInteger( rValue ); break; + case XML_Max: mnMax = AttributeConversion::decodeInteger( rValue ); break; + case XML_Position: mnPosition = AttributeConversion::decodeInteger( rValue ); break; + case XML_SmallChange: mnSmallChange = AttributeConversion::decodeInteger( rValue ); break; + case XML_Delay: mnDelay = AttributeConversion::decodeInteger( rValue ); break; + default: AxControlModelBase::importProperty( nPropId, rValue ); + } +} + +bool AxSpinButtonModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readIntProperty< sal_uInt32 >( mnArrowColor ); + aReader.readIntProperty< sal_uInt32 >( mnBackColor ); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.readPairProperty( maSize ); + aReader.skipIntProperty< sal_uInt32 >(); // unused + aReader.readIntProperty< sal_Int32 >( mnMin ); + aReader.readIntProperty< sal_Int32 >( mnMax ); + aReader.readIntProperty< sal_Int32 >( mnPosition ); + aReader.skipIntProperty< sal_uInt32 >(); // prev enabled + aReader.skipIntProperty< sal_uInt32 >(); // next enabled + aReader.readIntProperty< sal_Int32 >( mnSmallChange ); + aReader.readIntProperty< sal_Int32 >( mnOrientation ); + aReader.readIntProperty< sal_Int32 >( mnDelay ); + aReader.skipPictureProperty(); // mouse icon + aReader.skipIntProperty< sal_uInt8 >(); // mouse pointer + return aReader.finalizeImport(); +} + +void AxSpinButtonModel::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + AxBinaryPropertyWriter aWriter( rOutStrm ); + aWriter.writeIntProperty< sal_uInt32 >( mnArrowColor ); + if ( mnBackColor ) + aWriter.writeIntProperty< sal_uInt32 >( mnBackColor ); + else + aWriter.skipProperty(); // default backcolour + aWriter.writeIntProperty< sal_uInt32 >( mnFlags ); + aWriter.writePairProperty( maSize ); + aWriter.skipProperty(); // unused + aWriter.writeIntProperty< sal_Int32 >( mnMin ); + aWriter.writeIntProperty< sal_Int32 >( mnMax ); + aWriter.writeIntProperty< sal_Int32 >( mnPosition ); + aWriter.skipProperty(); // prev enabled + aWriter.skipProperty(); // next enabled + aWriter.writeIntProperty< sal_Int32 >( mnSmallChange ); + aWriter.writeIntProperty< sal_Int32 >( mnOrientation ); + aWriter.writeIntProperty< sal_Int32 >( mnDelay ); + aWriter.skipProperty(); // mouse icon + aWriter.skipProperty(); // mouse pointer + + aWriter.finalizeExport(); +} + +void AxSpinButtonModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + sal_Int32 nMin = ::std::min( mnMin, mnMax ); + sal_Int32 nMax = ::std::max( mnMin, mnMax ); + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_FLAGS_ENABLED ) ); + rPropMap.setProperty( PROP_SpinValueMin, nMin ); + rPropMap.setProperty( PROP_SpinValueMax, nMax ); + rPropMap.setProperty( PROP_SpinIncrement, mnSmallChange ); + rPropMap.setProperty( mbAwtModel ? PROP_SpinValue : PROP_DefaultSpinValue, mnPosition ); + rPropMap.setProperty( PROP_Repeat, true ); + rPropMap.setProperty( PROP_RepeatDelay, mnDelay ); + rPropMap.setProperty( PROP_Border, API_BORDER_NONE ); + rConv.convertColor( rPropMap, PROP_SymbolColor, mnArrowColor ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::NotSupported ); + ControlConverter::convertAxOrientation( rPropMap, maSize, mnOrientation ); + AxControlModelBase::convertProperties( rPropMap, rConv ); +} + +void AxSpinButtonModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& ) +{ + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_Enabled ) ) + setFlag( mnFlags, AX_FLAGS_ENABLED, bRes ); + rPropSet.getProperty( mnMin, PROP_SpinValueMin ); + rPropSet.getProperty( mnMax, PROP_SpinValueMax ); + rPropSet.getProperty( mnSmallChange, PROP_SpinIncrement ); + rPropSet.getProperty( mnPosition, ( mbAwtModel ? PROP_SpinValue : PROP_DefaultSpinValue ) ); + rPropSet.getProperty( mnDelay, PROP_RepeatDelay ); + ControlConverter::convertToMSColor( rPropSet, PROP_SymbolColor, mnArrowColor); + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + + ControlConverter::convertToAxOrientation( rPropSet, mnOrientation ); +} + +void AxSpinButtonModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = + { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xB0, 0x6F, 0x17, 0x79, + 0xF2, 0xB7, 0xCE, 0x11, 0x97, 0xEF, 0x00, 0xAA, + 0x00, 0x6D, 0x27, 0x76, 0x1F, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x53, 0x70, 0x69, 0x6E, + 0x42, 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x45, 0x6D, 0x62, 0x65, 0x64, + 0x64, 0x65, 0x64, 0x20, 0x4F, 0x62, 0x6A, 0x65, + 0x63, 0x74, 0x00, 0x13, 0x00, 0x00, 0x00, 0x46, + 0x6F, 0x72, 0x6D, 0x73, 0x2E, 0x53, 0x70, 0x69, + 0x6E, 0x42, 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x2E, + 0x31, 0x00, 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +AxScrollBarModel::AxScrollBarModel() : + mnArrowColor( AX_SYSCOLOR_BUTTONTEXT ), + mnBackColor( AX_SYSCOLOR_BUTTONFACE ), + mnFlags( AX_SCROLLBAR_DEFFLAGS ), + mnOrientation( AX_ORIENTATION_AUTO ), + mnPropThumb( AX_PROPTHUMB_ON ), + mnMin( 0 ), + mnMax( 32767 ), + mnPosition( 0 ), + mnSmallChange( 1 ), + mnLargeChange( 1 ), + mnDelay( 50 ) +{ +} + +ApiControlType AxScrollBarModel::getControlType() const +{ + return API_CONTROL_SCROLLBAR; +} + +void AxScrollBarModel::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_ForeColor: mnArrowColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_BackColor: mnBackColor = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_VariousPropertyBits: mnFlags = AttributeConversion::decodeUnsigned( rValue ); break; + case XML_Orientation: mnOrientation = AttributeConversion::decodeInteger( rValue ); break; + case XML_ProportionalThumb: mnPropThumb = AttributeConversion::decodeInteger( rValue ); break; + case XML_Min: mnMin = AttributeConversion::decodeInteger( rValue ); break; + case XML_Max: mnMax = AttributeConversion::decodeInteger( rValue ); break; + case XML_Position: mnPosition = AttributeConversion::decodeInteger( rValue ); break; + case XML_SmallChange: mnSmallChange = AttributeConversion::decodeInteger( rValue ); break; + case XML_LargeChange: mnLargeChange = AttributeConversion::decodeInteger( rValue ); break; + case XML_Delay: mnDelay = AttributeConversion::decodeInteger( rValue ); break; + default: AxControlModelBase::importProperty( nPropId, rValue ); + } +} + +bool AxScrollBarModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readIntProperty< sal_uInt32 >( mnArrowColor ); + aReader.readIntProperty< sal_uInt32 >( mnBackColor ); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.readPairProperty( maSize ); + aReader.skipIntProperty< sal_uInt8 >(); // mouse pointer + aReader.readIntProperty< sal_Int32 >( mnMin ); + aReader.readIntProperty< sal_Int32 >( mnMax ); + aReader.readIntProperty< sal_Int32 >( mnPosition ); + aReader.skipIntProperty< sal_uInt32 >(); // unused + aReader.skipIntProperty< sal_uInt32 >(); // prev enabled + aReader.skipIntProperty< sal_uInt32 >(); // next enabled + aReader.readIntProperty< sal_Int32 >( mnSmallChange ); + aReader.readIntProperty< sal_Int32 >( mnLargeChange ); + aReader.readIntProperty< sal_Int32 >( mnOrientation ); + aReader.readIntProperty< sal_Int16 >( mnPropThumb ); + aReader.readIntProperty< sal_Int32 >( mnDelay ); + aReader.skipPictureProperty(); // mouse icon + return aReader.finalizeImport(); +} + +void AxScrollBarModel::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + AxBinaryPropertyWriter aWriter( rOutStrm ); + aWriter.writeIntProperty< sal_uInt32 >( mnArrowColor ); + if ( mnBackColor ) + aWriter.writeIntProperty< sal_uInt32 >( mnBackColor ); + else + aWriter.skipProperty(); // default backcolour + aWriter.writeIntProperty< sal_uInt32 >( mnFlags ); + aWriter.writePairProperty( maSize ); + aWriter.skipProperty(); // mouse pointer + aWriter.writeIntProperty< sal_Int32 >( mnMin ); + aWriter.writeIntProperty< sal_Int32 >( mnMax ); + aWriter.writeIntProperty< sal_Int32 >( mnPosition ); + aWriter.skipProperty(); // unused + aWriter.skipProperty(); // prev enabled + aWriter.skipProperty(); // next enabled + aWriter.writeIntProperty< sal_Int32 >( mnSmallChange ); + aWriter.writeIntProperty< sal_Int32 >( mnLargeChange ); + aWriter.writeIntProperty< sal_Int32 >( mnOrientation ); + aWriter.writeIntProperty< sal_Int16 >( mnPropThumb ); + aWriter.writeIntProperty< sal_Int32 >( mnDelay ); + aWriter.skipProperty(); // mouse icon + aWriter.finalizeExport(); +} + +void AxScrollBarModel::exportCompObj( BinaryOutputStream& rOutStream ) +{ + // should be able to replace this hardcoded foo with + // proper export info from MS-OLEDS spec. + static sal_uInt8 const aCompObj[] = + { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x81, 0xD1, 0xDF, + 0x2F, 0x5E, 0xCE, 0x11, 0xA4, 0x49, 0x00, 0xAA, + 0x00, 0x4A, 0x80, 0x3D, 0x1E, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x73, 0x20, + 0x32, 0x2E, 0x30, 0x20, 0x53, 0x63, 0x72, 0x6F, + 0x6C, 0x6C, 0x42, 0x61, 0x72, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x45, 0x6D, 0x62, 0x65, 0x64, 0x64, + 0x65, 0x64, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, + 0x74, 0x00, 0x12, 0x00, 0x00, 0x00, 0x46, 0x6F, + 0x72, 0x6D, 0x73, 0x2E, 0x53, 0x63, 0x72, 0x6F, + 0x6C, 0x6C, 0x42, 0x61, 0x72, 0x2E, 0x31, 0x00, + 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + rOutStream.writeMemory( aCompObj, sizeof( aCompObj ) ); +} + +void AxScrollBarModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_FLAGS_ENABLED ) ); + rPropMap.setProperty( PROP_RepeatDelay, mnDelay ); + rPropMap.setProperty( PROP_Border, API_BORDER_NONE ); + if( (mnPropThumb == AX_PROPTHUMB_ON) && (mnMin != mnMax) && (mnLargeChange > 0) ) + { + // use double to prevent integer overflow in division (fInterval+mnLargeChange may become 0 when performed as int) + double fInterval = fabs( static_cast< double >( mnMax - mnMin ) ); + sal_Int32 nThumbLen = getLimitedValue< sal_Int32, double >( (fInterval * mnLargeChange) / (fInterval + mnLargeChange), 1, SAL_MAX_INT32 ); + rPropMap.setProperty( PROP_VisibleSize, nThumbLen ); + } + rConv.convertColor( rPropMap, PROP_SymbolColor, mnArrowColor ); + rConv.convertAxBackground( rPropMap, mnBackColor, mnFlags, ApiTransparencyMode::NotSupported ); + ControlConverter::convertAxOrientation( rPropMap, maSize, mnOrientation ); + ControlConverter::convertScrollBar( rPropMap, mnMin, mnMax, mnPosition, mnSmallChange, mnLargeChange, mbAwtModel ); + AxControlModelBase::convertProperties( rPropMap, rConv ); +} + +void AxScrollBarModel::convertFromProperties( PropertySet& rPropSet, const ControlConverter& ) +{ + bool bRes = false; + if ( rPropSet.getProperty( bRes, PROP_Enabled ) ) + setFlag( mnFlags, AX_FLAGS_ENABLED, bRes ); + rPropSet.getProperty( mnDelay, PROP_RepeatDelay ); + mnPropThumb = AX_PROPTHUMB_ON; // default + ControlConverter::convertToMSColor( rPropSet, PROP_SymbolColor, mnArrowColor); + ControlConverter::convertToMSColor( rPropSet, PROP_BackgroundColor, mnBackColor ); + ControlConverter::convertToAxOrientation( rPropSet, mnOrientation ); + + rPropSet.getProperty( mnMin, PROP_ScrollValueMin ); + rPropSet.getProperty( mnMax, PROP_ScrollValueMax ); + rPropSet.getProperty( mnSmallChange, PROP_LineIncrement ); + rPropSet.getProperty( mnLargeChange, PROP_BlockIncrement ); + rPropSet.getProperty( mnPosition, ( mbAwtModel ? PROP_ScrollValue : PROP_DefaultScrollValue ) ); + +} + +AxContainerModelBase::AxContainerModelBase( bool bFontSupport ) : + AxFontDataModel( false ), // no support for alignment properties + maLogicalSize( AX_CONTAINER_DEFWIDTH, AX_CONTAINER_DEFHEIGHT ), + maScrollPos( 0, 0 ), + mnBackColor( AX_SYSCOLOR_BUTTONFACE ), + mnTextColor( AX_SYSCOLOR_BUTTONTEXT ), + mnFlags( AX_CONTAINER_DEFFLAGS ), + mnBorderColor( AX_SYSCOLOR_BUTTONTEXT ), + mnBorderStyle( AX_BORDERSTYLE_NONE ), + mnScrollBars( AX_CONTAINER_SCR_NONE ), + mnCycleType( AX_CONTAINER_CYCLEALL ), + mnSpecialEffect( AX_SPECIALEFFECT_FLAT ), + mnPicAlign( AX_PICALIGN_CENTER ), + mnPicSizeMode( AX_PICSIZE_CLIP ), + mbPicTiling( false ), + mbFontSupport( bFontSupport ) +{ + setAwtModelMode(); + // different default size for frame + maSize = AxPairData( AX_CONTAINER_DEFWIDTH, AX_CONTAINER_DEFHEIGHT ); +} + +void AxContainerModelBase::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + if( nPropId == XML_Caption ) + maCaption = rValue; +} + +bool AxContainerModelBase::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.skipUndefinedProperty(); + aReader.readIntProperty< sal_uInt32 >( mnBackColor ); + aReader.readIntProperty< sal_uInt32 >( mnTextColor ); + aReader.skipIntProperty< sal_uInt32 >(); // next available control ID + aReader.skipUndefinedProperty(); + aReader.skipUndefinedProperty(); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.readIntProperty< sal_uInt8 >( mnBorderStyle ); + aReader.skipIntProperty< sal_uInt8 >(); // mouse pointer + aReader.readIntProperty< sal_uInt8 >( mnScrollBars ); + aReader.readPairProperty( maSize ); + aReader.readPairProperty( maLogicalSize ); + aReader.readPairProperty( maScrollPos ); + aReader.skipIntProperty< sal_uInt32 >(); // number of control groups + aReader.skipUndefinedProperty(); + aReader.skipPictureProperty(); // mouse icon + aReader.readIntProperty< sal_uInt8 >( mnCycleType ); + aReader.readIntProperty< sal_uInt8 >( mnSpecialEffect ); + aReader.readIntProperty< sal_uInt32 >( mnBorderColor ); + aReader.readStringProperty( maCaption ); + aReader.readFontProperty( maFontData ); + aReader.readPictureProperty( maPictureData ); + aReader.skipIntProperty< sal_Int32 >(); // zoom + aReader.readIntProperty< sal_uInt8 >( mnPicAlign ); + aReader.readBoolProperty( mbPicTiling ); + aReader.readIntProperty< sal_uInt8 >( mnPicSizeMode ); + aReader.skipIntProperty< sal_uInt32 >(); // shape cookie + aReader.skipIntProperty< sal_uInt32 >(); // draw buffer size + return aReader.finalizeImport(); +} + +void AxContainerModelBase::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + if( mbFontSupport ) + { + rConv.convertColor( rPropMap, PROP_TextColor, mnTextColor ); + AxFontDataModel::convertProperties( rPropMap, rConv ); + } +} + +bool AxContainerModelBase::importClassTable( BinaryInputStream& rInStrm, AxClassTable& orClassTable ) +{ + bool bValid = true; + orClassTable.clear(); + if( !getFlag( mnFlags, AX_CONTAINER_NOCLASSTABLE ) ) + { + sal_uInt16 nCount = rInStrm.readuInt16(); + for( sal_uInt16 nIndex = 0; bValid && (nIndex < nCount); ++nIndex ) + { + orClassTable.emplace_back( ); + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readGuidProperty( orClassTable.back() ); + aReader.skipGuidProperty(); // source interface GUID + aReader.skipUndefinedProperty(); + aReader.skipGuidProperty(); // default interface GUID + aReader.skipIntProperty< sal_uInt32 >(); // class table and var flags + aReader.skipIntProperty< sal_uInt32 >(); // method count + aReader.skipIntProperty< sal_Int32 >(); // IDispatch identifier for linked cell access + aReader.skipIntProperty< sal_uInt16 >(); // get function index for linked cell access + aReader.skipIntProperty< sal_uInt16 >(); // put function index for linked cell access + aReader.skipIntProperty< sal_uInt16 >(); // linked cell access property type + aReader.skipIntProperty< sal_uInt16 >(); // get function index of value + aReader.skipIntProperty< sal_uInt16 >(); // put function index of value + aReader.skipIntProperty< sal_uInt16 >(); // value type + aReader.skipIntProperty< sal_Int32 >(); // IDispatch identifier for source range access + aReader.skipIntProperty< sal_uInt16 >(); // get function index for source range access + bValid = aReader.finalizeImport(); + } + } + return bValid; +} + +AxFrameModel::AxFrameModel() : + AxContainerModelBase( true ) +{ +} + +ApiControlType AxFrameModel::getControlType() const +{ + return mbAwtModel ? API_CONTROL_FRAME : API_CONTROL_GROUPBOX; +} + +void AxFrameModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Label, maCaption ); + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_CONTAINER_ENABLED ) ); + AxContainerModelBase::convertProperties( rPropMap, rConv ); +} + +AxPageModel::AxPageModel() +{ +} + +ApiControlType AxPageModel::getControlType() const +{ + return API_CONTROL_PAGE; +} + +void AxPageModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Title, maCaption ); + rConv.convertColor( rPropMap, PROP_BackgroundColor, mnBackColor ); + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_CONTAINER_ENABLED ) ); + AxContainerModelBase::convertProperties( rPropMap, rConv ); +} + +AxMultiPageModel::AxMultiPageModel() : + mnActiveTab( 0 ), + mnTabStyle( AX_TABSTRIP_TABS ) +{ +} + +ApiControlType AxMultiPageModel::getControlType() const +{ + return API_CONTROL_MULTIPAGE; +} + +void AxMultiPageModel::importPageAndMultiPageProperties( BinaryInputStream& rInStrm, sal_Int32 nPages ) +{ + // PageProperties + for ( sal_Int32 nPage = 0; nPage < nPages; ++nPage ) + { + AxBinaryPropertyReader aReader( rInStrm ); + aReader.skipUndefinedProperty(); + aReader.skipIntProperty< sal_uInt32 >(); // TransitionEffect + aReader.skipIntProperty< sal_uInt32 >(); // TransitionPeriod + } + // MultiPageProperties + AxBinaryPropertyReader aReader( rInStrm ); + sal_uInt32 nPageCount = 0; + aReader.skipUndefinedProperty(); + aReader.readIntProperty< sal_uInt32 >(nPageCount); // PageCount + aReader.skipIntProperty< sal_uInt32 >(); //ID + + // IDs + for ( sal_uInt32 count = 0; count < nPageCount; ++count ) + { + mnIDs.push_back( rInStrm.readInt32() ); + } +} + +void AxMultiPageModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Title, maCaption ); + rPropMap.setProperty( PROP_MultiPageValue, mnActiveTab + 1); + rConv.convertColor( rPropMap, PROP_BackgroundColor, mnBackColor ); + rPropMap.setProperty( PROP_Enabled, getFlag( mnFlags, AX_CONTAINER_ENABLED ) ); + rPropMap.setProperty( PROP_Decoration, mnTabStyle != AX_TABSTRIP_NONE ); + + AxContainerModelBase::convertProperties( rPropMap, rConv ); +} + +AxUserFormModel::AxUserFormModel() +{ +} + +ApiControlType AxUserFormModel::getControlType() const +{ + return API_CONTROL_DIALOG; +} + +void AxUserFormModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_Title, maCaption ); + rConv.convertColor( rPropMap, PROP_BackgroundColor, mnBackColor ); + rConv.convertAxPicture( rPropMap, maPictureData, AX_PICPOS_CENTER ); + rConv.convertScrollabilitySettings( rPropMap, maScrollPos, maLogicalSize, mnScrollBars ); + AxContainerModelBase::convertProperties( rPropMap, rConv ); +} + +HtmlSelectModel::HtmlSelectModel() +{ +} + +bool +HtmlSelectModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + if (rInStrm.size()<=0) + return true; + + OUString sStringContents = rInStrm.readUnicodeArray( rInStrm.size() ); + + // replace crlf with lf + OUString data = sStringContents.replaceAll( "\x0D\x0A" , "\x0A" ); + + std::vector< OUString > listValues; + std::vector< sal_Int16 > selectedIndices; + + // Ultra hacky parser for the info + sal_Int32 nLineIdx {0}; + // first line will tell us if multiselect is enabled + if (o3tl::getToken(data, 0, '\n', nLineIdx ) == u"<SELECT MULTIPLE") + mnMultiSelect = AX_SELECTION_MULTI; + // skip first and last lines, no data there + if (nLineIdx>0) + { + for (;;) + { + std::u16string_view sLine( o3tl::getToken(data, 0, '\n', nLineIdx ) ); + if (nLineIdx<0) + break; // skip last line + + if ( !sLine.empty() ) + { + OUString displayValue( o3tl::getToken(sLine, 1, '>' ) ); + if ( displayValue.getLength() ) + { + // Really we should be using a proper html parser + // escaping some common bits to be escaped + displayValue = displayValue.replaceAll( "<", "<" ); + displayValue = displayValue.replaceAll( ">", ">" ); + displayValue = displayValue.replaceAll( """, "\"" ); + displayValue = displayValue.replaceAll( "&", "&" ); + listValues.push_back( displayValue ); + if( sLine.find( u"OPTION SELECTED" ) != std::u16string_view::npos ) + selectedIndices.push_back( static_cast< sal_Int16 >( listValues.size() ) - 1 ); + } + } + } + } + if ( !listValues.empty() ) + { + msListData.realloc( listValues.size() ); + auto psListData = msListData.getArray(); + sal_Int32 index = 0; + for (auto const& listValue : listValues) + psListData[ index++ ] = listValue; + } + if ( !selectedIndices.empty() ) + { + msIndices.realloc( selectedIndices.size() ); + auto psIndices = msIndices.getArray(); + sal_Int32 index = 0; + for (auto const& selectedIndice : selectedIndices) + psIndices[ index++ ] = selectedIndice; + } + return true; +} + +void +HtmlSelectModel::convertProperties( PropertyMap& rPropMap, const ControlConverter& rConv ) const +{ + rPropMap.setProperty( PROP_StringItemList, msListData ); + rPropMap.setProperty( PROP_SelectedItems, msIndices ); + rPropMap.setProperty( PROP_Dropdown, true ); + AxListBoxModel::convertProperties( rPropMap, rConv ); +} + +HtmlTextBoxModel::HtmlTextBoxModel() +{ +} + +bool +HtmlTextBoxModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ +#ifdef DEBUG + OUString sStringContents = rInStrm.readUnicodeArray( rInStrm.size() ); + // in msocximex ( where this is ported from, it appears *nothing* is read + // from the control stream ), surely there is some useful info there ? + SAL_WARN("oox", "HtmlTextBoxModel::importBinaryModel - string contents of stream: " << sStringContents ); +#else + (void) rInStrm; +#endif + return true; +} + +EmbeddedControl::EmbeddedControl( const OUString& rName ) : + maName( rName ) +{ +} + +ControlModelBase* EmbeddedControl::createModelFromGuid( std::u16string_view rClassId ) +{ + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_COMMANDBUTTON ) ) return &createModel< AxCommandButtonModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_LABEL ) ) return &createModel< AxLabelModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_IMAGE ) ) return &createModel< AxImageModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_TOGGLEBUTTON ) ) return &createModel< AxToggleButtonModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_CHECKBOX ) ) return &createModel< AxCheckBoxModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_OPTIONBUTTON ) ) return &createModel< AxOptionButtonModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_TEXTBOX ) ) return &createModel< AxTextBoxModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_LISTBOX ) ) return &createModel< AxListBoxModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_COMBOBOX ) ) return &createModel< AxComboBoxModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_SPINBUTTON ) ) return &createModel< AxSpinButtonModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_SCROLLBAR ) ) return &createModel< AxScrollBarModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" AX_GUID_FRAME ) ) return &createModel< AxFrameModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" COMCTL_GUID_SCROLLBAR_60 ) ) return &createModel< ComCtlScrollBarModel >( COMCTL_VERSION_60 ); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" HTML_GUID_SELECT ) ) return &createModel< HtmlSelectModel >(); + if( o3tl::equalsIgnoreAsciiCase(rClassId, u"" HTML_GUID_TEXTBOX ) ) return &createModel< HtmlTextBoxModel >(); + + mxModel.reset(); + return nullptr; +} + +OUString EmbeddedControl::getServiceName() const +{ + return mxModel ? mxModel->getServiceName() : OUString(); +} + +bool EmbeddedControl::convertProperties( const Reference< XControlModel >& rxCtrlModel, const ControlConverter& rConv ) const +{ + if( mxModel && rxCtrlModel.is() && !maName.isEmpty() ) + { + PropertyMap aPropMap; + aPropMap.setProperty( PROP_Name, maName ); + try + { + aPropMap.setProperty( PROP_GenerateVbaEvents, true); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox", ""); + } + mxModel->convertProperties( aPropMap, rConv ); + PropertySet aPropSet( rxCtrlModel ); + aPropSet.setProperties( aPropMap ); + return true; + } + return false; +} + +void EmbeddedControl::convertFromProperties( const Reference< XControlModel >& rxCtrlModel, const ControlConverter& rConv ) +{ + if( mxModel && rxCtrlModel.is() && !maName.isEmpty() ) + { + PropertySet aPropSet( rxCtrlModel ); + aPropSet.getProperty( maName, PROP_Name ); + mxModel->convertFromProperties( aPropSet, rConv ); + } +} + +EmbeddedForm::EmbeddedForm( const Reference< XModel >& rxDocModel, + const Reference< XDrawPage >& rxDrawPage, const GraphicHelper& rGraphicHelper ) : + maControlConv( rxDocModel, rGraphicHelper, true/*bDefaultColorBgr*/ ), + mxModelFactory( rxDocModel, UNO_QUERY ), + mxFormsSupp( rxDrawPage, UNO_QUERY ) +{ + OSL_ENSURE( mxModelFactory.is(), "EmbeddedForm::EmbeddedForm - missing service factory" ); +} + +Reference< XControlModel > EmbeddedForm::convertAndInsert( const EmbeddedControl& rControl, sal_Int32& rnCtrlIndex ) +{ + Reference< XControlModel > xRet; + if( mxModelFactory.is() && rControl.hasModel() ) try + { + // create the UNO control model + OUString aServiceName = rControl.getServiceName(); + Reference< XFormComponent > xFormComp( mxModelFactory->createInstance( aServiceName ), UNO_QUERY_THROW ); + Reference< XControlModel > xCtrlModel( xFormComp, UNO_QUERY_THROW ); + + // convert the control properties + if( rControl.convertProperties( xCtrlModel, maControlConv ) ) + xRet = xCtrlModel; + // insert the control into the form + Reference< XIndexContainer > xFormIC( createXForm(), UNO_SET_THROW ); + rnCtrlIndex = xFormIC->getCount(); + xFormIC->insertByIndex( rnCtrlIndex, Any( xFormComp ) ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox", "exception creating Control"); + } + return xRet; +} + +Reference< XIndexContainer > const & EmbeddedForm::createXForm() +{ + if( mxFormsSupp.is() ) + { + try + { + Reference< XNameContainer > xFormsNC( mxFormsSupp->getForms(), UNO_SET_THROW ); + OUString aFormName = "Standard"; + if( xFormsNC->hasByName( aFormName ) ) + { + mxFormIC.set( xFormsNC->getByName( aFormName ), UNO_QUERY_THROW ); + } + else if( mxModelFactory.is() ) + { + Reference< XForm > xForm( mxModelFactory->createInstance( "com.sun.star.form.component.Form" ), UNO_QUERY_THROW ); + xFormsNC->insertByName( aFormName, Any( xForm ) ); + mxFormIC.set( xForm, UNO_QUERY_THROW ); + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox", "exception creating Form"); + } + // always clear the forms supplier to not try to create the form again + mxFormsSupp.clear(); + } + return mxFormIC; +} + +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/axcontrolfragment.cxx b/oox/source/ole/axcontrolfragment.cxx new file mode 100644 index 000000000..a0f0b1096 --- /dev/null +++ b/oox/source/ole/axcontrolfragment.cxx @@ -0,0 +1,167 @@ +/* -*- 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 <oox/ole/axcontrolfragment.hxx> + +#include <com/sun/star/io/XInputStream.hpp> +#include <oox/core/xmlfilterbase.hxx> +#include <oox/helper/attributelist.hxx> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/ole/axcontrol.hxx> +#include <oox/ole/olehelper.hxx> +#include <oox/ole/olestorage.hxx> +#include <oox/token/namespaces.hxx> +#include <oox/token/tokens.hxx> + +#include <osl/diagnose.h> + +namespace oox::ole { + +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::uno; + +using ::oox::core::ContextHandler2; +using ::oox::core::ContextHandlerRef; +using ::oox::core::FragmentHandler2; +using ::oox::core::XmlFilterBase; + +AxControlPropertyContext::AxControlPropertyContext( FragmentHandler2 const & rFragment, ControlModelBase& rModel ) : + ContextHandler2( rFragment ), + mrModel( rModel ), + mnPropId( XML_TOKEN_INVALID ) +{ +} + +ContextHandlerRef AxControlPropertyContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + switch( getCurrentElement() ) + { + case AX_TOKEN( ocx ): + if( nElement == AX_TOKEN( ocxPr ) ) + { + mnPropId = rAttribs.getToken( AX_TOKEN( name ), XML_TOKEN_INVALID ); + switch( mnPropId ) + { + case XML_TOKEN_INVALID: + return nullptr; + case XML_Picture: + case XML_MouseIcon: + return this; // import picture path from ax:picture child element + default: + mrModel.importProperty( mnPropId, rAttribs.getString( AX_TOKEN( value ), OUString() ) ); + } + } + break; + + case AX_TOKEN( ocxPr ): + if( nElement == AX_TOKEN( picture ) ) + { + OUString aPicturePath = getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ); + if( !aPicturePath.isEmpty() ) + { + BinaryXInputStream aInStrm( getFilter().openInputStream( aPicturePath ), true ); + mrModel.importPictureData( mnPropId, aInStrm ); + } + } + break; + } + return nullptr; +} + +AxControlFragment::AxControlFragment( XmlFilterBase& rFilter, const OUString& rFragmentPath, EmbeddedControl& rControl ) : + FragmentHandler2( rFilter, rFragmentPath, true ), + mrControl( rControl ) +{ +} + +ContextHandlerRef AxControlFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + if( isRootElement() && (nElement == AX_TOKEN( ocx )) ) + { + OUString aClassId = rAttribs.getString( AX_TOKEN( classid ), OUString() ); + switch( rAttribs.getToken( AX_TOKEN( persistence ), XML_TOKEN_INVALID ) ) + { + case XML_persistPropertyBag: + if( ControlModelBase* pModel = mrControl.createModelFromGuid( aClassId ) ) + return new AxControlPropertyContext( *this, *pModel ); + break; + + case XML_persistStreamInit: + { + OUString aFragmentPath = getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ); + if( !aFragmentPath.isEmpty() ) + { + BinaryXInputStream aInStrm( getFilter().openInputStream( aFragmentPath ), true ); + if( !aInStrm.isEof() ) + { + // binary stream contains a copy of the class ID, must be equal to attribute value + OUString aStrmClassId = OleHelper::importGuid( aInStrm ); + OSL_ENSURE( aClassId.equalsIgnoreAsciiCase( aStrmClassId ), + "AxControlFragment::importBinaryControl - form control class ID mismatch" ); + if( ControlModelBase* pModel = mrControl.createModelFromGuid( aStrmClassId ) ) + pModel->importBinaryModel( aInStrm ); + } + } + } + break; + + case XML_persistStorage: + { + OUString aFragmentPath = getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ); + if( !aFragmentPath.isEmpty() ) + { + Reference< XInputStream > xStrgStrm = getFilter().openInputStream( aFragmentPath ); + if( xStrgStrm.is() ) + { + // Try to import as a parent control + bool bImportedAsParent = false; + OleStorage aStorage( getFilter().getComponentContext(), xStrgStrm, false ); + BinaryXInputStream aInStrm( aStorage.openInputStream( "f" ), true ); + if( !aInStrm.isEof() ) + { + if( AxContainerModelBase* pModel = dynamic_cast< AxContainerModelBase* >( mrControl.createModelFromGuid( aClassId ) ) ) + { + pModel->importBinaryModel( aInStrm ); + bImportedAsParent = true; + } + } + // Import it as a non-parent control + if(!bImportedAsParent) + { + BinaryXInputStream aInStrm2(aStorage.openInputStream("contents"), true); + if (!aInStrm2.isEof()) + { + if (ControlModelBase* pModel = mrControl.createModelFromGuid(aClassId)) + { + pModel->importBinaryModel(aInStrm2); + } + } + } + } + } + } + break; + } + } + return nullptr; +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/axfontdata.cxx b/oox/source/ole/axfontdata.cxx new file mode 100644 index 000000000..2505168bc --- /dev/null +++ b/oox/source/ole/axfontdata.cxx @@ -0,0 +1,117 @@ +/* -*- 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 <oox/ole/axfontdata.hxx> +#include <oox/ole/olehelper.hxx> +#include <oox/ole/axbinaryreader.hxx> +#include <oox/ole/axbinarywriter.hxx> + +namespace oox::ole { + +AxFontData::AxFontData() : + mnFontEffects( AxFontFlags::NONE ), + mnFontHeight( 160 ), + mnFontCharSet( WINDOWS_CHARSET_DEFAULT ), + mnHorAlign( AxHorizontalAlign::Left ), + mbDblUnderline( false ) +{ +} + +sal_Int16 AxFontData::getHeightPoints() const +{ + /* MSO uses weird font sizes: + 1pt->30, 2pt->45, 3pt->60, 4pt->75, 5pt->105, 6pt->120, 7pt->135, + 8pt->165, 9pt->180, 10pt->195, 11pt->225, ... */ + return getLimitedValue< sal_Int16, sal_Int32 >( (mnFontHeight + 10) / 20, 1, SAL_MAX_INT16 ); +} + +void AxFontData::setHeightPoints( sal_Int16 nPoints ) +{ + mnFontHeight = getLimitedValue< sal_Int32, sal_Int32 >( ((nPoints * 4 + 1) / 3) * 15, 30, 4294967 ); +} + +bool AxFontData::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readStringProperty( maFontName ); + sal_uInt32 nTmp32 = 0; + aReader.readIntProperty< sal_uInt32 >( nTmp32 ); + mnFontEffects = static_cast<AxFontFlags>(nTmp32); + aReader.readIntProperty< sal_Int32 >( mnFontHeight ); + aReader.skipIntProperty< sal_Int32 >(); // font offset + aReader.readIntProperty< sal_uInt8 >( mnFontCharSet ); + aReader.skipIntProperty< sal_uInt8 >(); // font pitch/family + sal_uInt8 nTmp = static_cast<sal_uInt8>(AxHorizontalAlign::Left); + aReader.readIntProperty< sal_uInt8 >( nTmp ); + mnHorAlign = static_cast<AxHorizontalAlign>(nTmp); + aReader.skipIntProperty< sal_uInt16 >(); // font weight + mbDblUnderline = false; + return aReader.finalizeImport(); +} + +void AxFontData::exportBinaryModel( BinaryOutputStream& rOutStrm ) +{ + AxBinaryPropertyWriter aWriter( rOutStrm ); + aWriter.writeStringProperty( maFontName ); + aWriter.writeIntProperty< sal_uInt32 >( static_cast<sal_uInt32>(mnFontEffects) ); + aWriter.writeIntProperty< sal_Int32 >( mnFontHeight ); + aWriter.skipProperty(); // font offset + // TODO make AxFontDataModel::convertFromProperties convert the textencoding + aWriter.writeIntProperty< sal_uInt8 >( mnFontCharSet ); + aWriter.skipProperty(); // font pitch/family + + aWriter.writeIntProperty< sal_uInt8 >( static_cast<sal_uInt8>(mnHorAlign) ); + aWriter.skipProperty(); // font weight + aWriter.finalizeExport(); +} + +bool AxFontData::importStdFont( BinaryInputStream& rInStrm ) +{ + StdFontInfo aFontInfo; + if( OleHelper::importStdFont( aFontInfo, rInStrm, false ) ) + { + maFontName = aFontInfo.maName; + mnFontEffects = AxFontFlags::NONE; + setFlag( mnFontEffects, AxFontFlags::Bold, aFontInfo.mnWeight >= OLE_STDFONT_BOLD ); + setFlag( mnFontEffects, AxFontFlags::Italic, getFlag( aFontInfo.mnFlags, OLE_STDFONT_ITALIC ) ); + setFlag( mnFontEffects, AxFontFlags::Underline, getFlag( aFontInfo.mnFlags, OLE_STDFONT_UNDERLINE ) ); + setFlag( mnFontEffects, AxFontFlags::Strikeout, getFlag( aFontInfo.mnFlags,OLE_STDFONT_STRIKE ) ); + mbDblUnderline = false; + // StdFont stores font height in 1/10,000 of points + setHeightPoints( getLimitedValue< sal_Int16, sal_Int32 >( aFontInfo.mnHeight / 10000, 0, SAL_MAX_INT16 ) ); + mnFontCharSet = aFontInfo.mnCharSet; + mnHorAlign = AxHorizontalAlign::Left; + return true; + } + return false; +} + +bool AxFontData::importGuidAndFont( BinaryInputStream& rInStrm ) +{ + OUString aGuid = OleHelper::importGuid( rInStrm ); + if( aGuid == "{AFC20920-DA4E-11CE-B943-00AA006887B4}" ) + return importBinaryModel( rInStrm ); + if ( aGuid == OLE_GUID_STDFONT ) + return importStdFont( rInStrm ); + return false; +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/olehelper.cxx b/oox/source/ole/olehelper.cxx new file mode 100644 index 000000000..41ab34feb --- /dev/null +++ b/oox/source/ole/olehelper.cxx @@ -0,0 +1,571 @@ +/* -*- 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 <oox/ole/olehelper.hxx> + +#include <rtl/ustrbuf.hxx> +#include <sot/storage.hxx> +#include <osl/diagnose.h> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/helper/binaryoutputstream.hxx> +#include <oox/helper/graphichelper.hxx> +#include <oox/token/properties.hxx> +#include <oox/token/tokens.hxx> +#include <oox/ole/axcontrol.hxx> +#include <oox/helper/propertymap.hxx> +#include <oox/helper/propertyset.hxx> + +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/form/XFormComponent.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <tools/globname.hxx> +#include <unotools/streamwrap.hxx> +#include <comphelper/processfactory.hxx> + +namespace oox::ole { + +using ::com::sun::star::form::XFormComponent; +using ::com::sun::star::awt::XControlModel; +using ::com::sun::star::awt::Size; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::io::XOutputStream; +using ::com::sun::star::io::XInputStream; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::XComponentContext; +using ::com::sun::star::lang::XServiceInfo; + +using namespace ::com::sun::star::form; + +namespace { + +const sal_uInt32 OLE_COLORTYPE_MASK = 0xFF000000; +const sal_uInt32 OLE_COLORTYPE_CLIENT = 0x00000000; +const sal_uInt32 OLE_COLORTYPE_PALETTE = 0x01000000; +const sal_uInt32 OLE_COLORTYPE_BGR = 0x02000000; +const sal_uInt32 OLE_COLORTYPE_SYSCOLOR = 0x80000000; + +const sal_uInt32 OLE_PALETTECOLOR_MASK = 0x0000FFFF; +const sal_uInt32 OLE_SYSTEMCOLOR_MASK = 0x0000FFFF; + +/** Swaps the red and blue component of the passed color. */ +sal_uInt32 lclSwapRedBlue( sal_uInt32 nColor ) +{ + return static_cast< sal_uInt32 >( (nColor & 0xFF00FF00) | ((nColor & 0x0000FF) << 16) | ((nColor & 0xFF0000) >> 16) ); +} + +/** Returns the UNO RGB color from the passed encoded OLE BGR color. */ +::Color lclDecodeBgrColor( sal_uInt32 nOleColor ) +{ + return ::Color( ColorTransparency, lclSwapRedBlue( nOleColor ) & 0xFFFFFF ); +} + +const sal_uInt32 OLE_STDPIC_ID = 0x0000746C; + +struct GUIDCNamePair +{ + const char* sGUID; + const char* sName; +}; + +struct IdCntrlData +{ + sal_Int16 nId; + GUIDCNamePair aData; +}; + +const sal_Int16 TOGGLEBUTTON = -1; +const sal_Int16 FORMULAFIELD = -2; + +typedef std::map< sal_Int16, GUIDCNamePair > GUIDCNamePairMap; +class classIdToGUIDCNamePairMap +{ + GUIDCNamePairMap mnIdToGUIDCNamePairMap; + classIdToGUIDCNamePairMap(); +public: + static GUIDCNamePairMap& get(); +}; + +classIdToGUIDCNamePairMap::classIdToGUIDCNamePairMap() +{ + static IdCntrlData const initialCntrlData[] = + { + // Command button MUST be at index 0 + { FormComponentType::COMMANDBUTTON, + { AX_GUID_COMMANDBUTTON, "CommandButton"} , + }, + // Toggle button MUST be at index 1 + { TOGGLEBUTTON, + { AX_GUID_TOGGLEBUTTON, "ToggleButton"}, + }, + { FormComponentType::FIXEDTEXT, + { AX_GUID_LABEL, "Label"}, + }, + { FormComponentType::TEXTFIELD, + { AX_GUID_TEXTBOX, "TextBox"}, + }, + { FormComponentType::LISTBOX, + { AX_GUID_LISTBOX, "ListBox"}, + }, + { FormComponentType::COMBOBOX, + { AX_GUID_COMBOBOX, "ComboBox"}, + }, + { FormComponentType::CHECKBOX, + { AX_GUID_CHECKBOX, "CheckBox"}, + }, + { FormComponentType::RADIOBUTTON, + { AX_GUID_OPTIONBUTTON, "OptionButton"}, + }, + { FormComponentType::IMAGECONTROL, + { AX_GUID_IMAGE, "Image"}, + }, + { FormComponentType::DATEFIELD, + { AX_GUID_TEXTBOX, "TextBox"}, + }, + { FormComponentType::TIMEFIELD, + { AX_GUID_TEXTBOX, "TextBox"}, + }, + { FormComponentType::NUMERICFIELD, + { AX_GUID_TEXTBOX, "TextBox"}, + }, + { FormComponentType::CURRENCYFIELD, + { AX_GUID_TEXTBOX, "TextBox"}, + }, + { FormComponentType::PATTERNFIELD, + { AX_GUID_TEXTBOX, "TextBox"}, + }, + { FORMULAFIELD, + { AX_GUID_TEXTBOX, "TextBox"}, + }, + { FormComponentType::IMAGEBUTTON, + { AX_GUID_COMMANDBUTTON, "CommandButton"}, + }, + { FormComponentType::SPINBUTTON, + { AX_GUID_SPINBUTTON, "SpinButton"}, + }, + { FormComponentType::SCROLLBAR, + { AX_GUID_SCROLLBAR, "ScrollBar"}, + } + }; + int const length = SAL_N_ELEMENTS( initialCntrlData ); + IdCntrlData const * pData = initialCntrlData; + for ( int index = 0; index < length; ++index, ++pData ) + mnIdToGUIDCNamePairMap[ pData->nId ] = pData->aData; +}; + +GUIDCNamePairMap& classIdToGUIDCNamePairMap::get() +{ + static classIdToGUIDCNamePairMap theInst; + return theInst.mnIdToGUIDCNamePairMap; +} + +template< typename Type > +void lclAppendHex( OUStringBuffer& orBuffer, Type nValue ) +{ + const sal_Int32 nWidth = 2 * sizeof( Type ); + static const sal_Unicode spcHexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + orBuffer.setLength( orBuffer.getLength() + nWidth ); + for( sal_Int32 nCharIdx = orBuffer.getLength() - 1, nCharEnd = nCharIdx - nWidth; nCharIdx > nCharEnd; --nCharIdx, nValue >>= 4 ) + orBuffer[nCharIdx] = spcHexChars[ nValue & 0xF ]; +} + +} // namespace + +StdFontInfo::StdFontInfo() : + mnHeight( 0 ), + mnWeight( OLE_STDFONT_NORMAL ), + mnCharSet( WINDOWS_CHARSET_ANSI ), + mnFlags( 0 ) +{ +} + +StdFontInfo::StdFontInfo( const OUString& rName, sal_uInt32 nHeight ) : + maName( rName ), + mnHeight( nHeight ), + mnWeight( OLE_STDFONT_NORMAL ), + mnCharSet( WINDOWS_CHARSET_ANSI ), + mnFlags( 0 ) +{ +} + +::Color OleHelper::decodeOleColor( + const GraphicHelper& rGraphicHelper, sal_uInt32 nOleColor, bool bDefaultColorBgr ) +{ + static const sal_Int32 spnSystemColors[] = + { + XML_scrollBar, XML_background, XML_activeCaption, XML_inactiveCaption, + XML_menu, XML_window, XML_windowFrame, XML_menuText, + XML_windowText, XML_captionText, XML_activeBorder, XML_inactiveBorder, + XML_appWorkspace, XML_highlight, XML_highlightText, XML_btnFace, + XML_btnShadow, XML_grayText, XML_btnText, XML_inactiveCaptionText, + XML_btnHighlight, XML_3dDkShadow, XML_3dLight, XML_infoText, + XML_infoBk + }; + + switch( nOleColor & OLE_COLORTYPE_MASK ) + { + case OLE_COLORTYPE_CLIENT: + return bDefaultColorBgr ? lclDecodeBgrColor( nOleColor ) : rGraphicHelper.getPaletteColor( nOleColor & OLE_PALETTECOLOR_MASK ); + + case OLE_COLORTYPE_PALETTE: + return rGraphicHelper.getPaletteColor( nOleColor & OLE_PALETTECOLOR_MASK ); + + case OLE_COLORTYPE_BGR: + return lclDecodeBgrColor( nOleColor ); + + case OLE_COLORTYPE_SYSCOLOR: + return rGraphicHelper.getSystemColor( STATIC_ARRAY_SELECT( spnSystemColors, nOleColor & OLE_SYSTEMCOLOR_MASK, XML_TOKEN_INVALID ), API_RGB_WHITE ); + } + OSL_FAIL( "OleHelper::decodeOleColor - unknown color type" ); + return API_RGB_BLACK; +} + +sal_uInt32 OleHelper::encodeOleColor( sal_Int32 nRgbColor ) +{ + return OLE_COLORTYPE_BGR | lclSwapRedBlue( static_cast< sal_uInt32 >( nRgbColor & 0xFFFFFF ) ); +} + +void OleHelper::exportGuid( BinaryOutputStream& rOStr, const SvGlobalName& rId ) +{ + rOStr.WriteUInt32( rId.GetCLSID().Data1 ); + rOStr.WriteUInt16( rId.GetCLSID().Data2 ); + rOStr.WriteUInt16( rId.GetCLSID().Data3 ); + rOStr.writeArray( rId.GetCLSID().Data4, 8 ); +} + +OUString OleHelper::importGuid( BinaryInputStream& rInStrm ) +{ + OUStringBuffer aBuffer(40); + aBuffer.append( '{' ); + lclAppendHex( aBuffer, rInStrm.readuInt32() ); + aBuffer.append( '-' ); + lclAppendHex( aBuffer, rInStrm.readuInt16() ); + aBuffer.append( '-' ); + lclAppendHex( aBuffer, rInStrm.readuInt16() ); + aBuffer.append( '-' ); + lclAppendHex( aBuffer, rInStrm.readuInt8() ); + lclAppendHex( aBuffer, rInStrm.readuInt8() ); + aBuffer.append( '-' ); + for( int nIndex = 0; nIndex < 6; ++nIndex ) + lclAppendHex( aBuffer, rInStrm.readuInt8() ); + aBuffer.append( '}' ); + return aBuffer.makeStringAndClear(); +} + +bool OleHelper::importStdFont( StdFontInfo& orFontInfo, BinaryInputStream& rInStrm, bool bWithGuid ) +{ + if( bWithGuid ) + { + bool bIsStdFont = importGuid( rInStrm ) == OLE_GUID_STDFONT; + OSL_ENSURE( bIsStdFont, "OleHelper::importStdFont - unexpected header GUID, expected StdFont" ); + if( !bIsStdFont ) + return false; + } + + sal_uInt8 nVersion, nNameLen; + nVersion = rInStrm.readuChar(); + orFontInfo.mnCharSet = rInStrm.readuInt16(); + orFontInfo.mnFlags = rInStrm.readuChar(); + orFontInfo.mnWeight = rInStrm.readuInt16(); + orFontInfo.mnHeight = rInStrm.readuInt32(); + nNameLen = rInStrm.readuChar(); + // according to spec the name is ASCII + orFontInfo.maName = rInStrm.readCharArrayUC( nNameLen, RTL_TEXTENCODING_ASCII_US ); + OSL_ENSURE( nVersion <= 1, "OleHelper::importStdFont - wrong version" ); + return !rInStrm.isEof() && (nVersion <= 1); +} + +bool OleHelper::importStdPic( StreamDataSequence& orGraphicData, BinaryInputStream& rInStrm ) +{ + bool bIsStdPic = importGuid( rInStrm ) == OLE_GUID_STDPIC; + OSL_ENSURE( bIsStdPic, "OleHelper::importStdPic - unexpected header GUID, expected StdPic" ); + if( !bIsStdPic ) + return false; + + sal_uInt32 nStdPicId; + sal_Int32 nBytes; + nStdPicId = rInStrm.readuInt32(); + nBytes = rInStrm.readInt32(); + OSL_ENSURE( nStdPicId == OLE_STDPIC_ID, "OleHelper::importStdPic - unexpected header version" ); + return !rInStrm.isEof() && (nStdPicId == OLE_STDPIC_ID) && (nBytes > 0) && (rInStrm.readData( orGraphicData, nBytes ) == nBytes); +} + +static Reference< css::frame::XFrame > lcl_getFrame( const Reference< css::frame::XModel >& rxModel ) +{ + Reference< css::frame::XFrame > xFrame; + if ( rxModel.is() ) + { + Reference< css::frame::XController > xController = rxModel->getCurrentController(); + xFrame = xController.is() ? xController->getFrame() : nullptr; + } + return xFrame; +} + +OleFormCtrlExportHelper::OleFormCtrlExportHelper( const Reference< XComponentContext >& rxCtx, const Reference< XModel >& rxDocModel, const Reference< XControlModel >& xCntrlModel ) : mpModel( nullptr ), maGrfHelper( rxCtx, lcl_getFrame( rxDocModel ), StorageRef() ), mxDocModel( rxDocModel ), mxControlModel( xCntrlModel ) +{ + // try to get the guid + Reference< css::beans::XPropertySet > xProps( xCntrlModel, UNO_QUERY ); + if ( !xProps.is() ) + return; + + sal_Int16 nClassId = 0; + PropertySet aPropSet( mxControlModel ); + if ( !aPropSet.getProperty( nClassId, PROP_ClassId ) ) + return; + + /* pseudo ripped from legacy msocximex: + "There is a truly horrible thing with EditControls and FormattedField + Controls, they both pretend to have an EDITBOX ClassId for compatibility + reasons, at some stage in the future hopefully there will be a proper + FormulaField ClassId rather than this piggybacking two controls onto the + same ClassId, cmc." - when fixed the fake FORMULAFIELD id entry + and definition above can be removed/replaced + */ + if ( nClassId == FormComponentType::TEXTFIELD) + { + Reference< XServiceInfo > xInfo( xCntrlModel, + UNO_QUERY); + if (xInfo-> + supportsService( "com.sun.star.form.component.FormattedField" ) ) + nClassId = FORMULAFIELD; + } + else if ( nClassId == FormComponentType::COMMANDBUTTON ) + { + bool bToggle = false; + if ( aPropSet.getProperty( bToggle, PROP_Toggle ) && bToggle ) + nClassId = TOGGLEBUTTON; + } + else if ( nClassId == FormComponentType::CONTROL ) + { + Reference< XServiceInfo > xInfo( xCntrlModel, + UNO_QUERY); + if (xInfo->supportsService("com.sun.star.form.component.ImageControl" ) ) + nClassId = FormComponentType::IMAGECONTROL; + } + + GUIDCNamePairMap& cntrlMap = classIdToGUIDCNamePairMap::get(); + GUIDCNamePairMap::iterator it = cntrlMap.find( nClassId ); + if ( it != cntrlMap.end() ) + { + aPropSet.getProperty(maName, PROP_Name ); + maTypeName = OUString::createFromAscii( it->second.sName ); + maFullName = "Microsoft Forms 2.0 " + maTypeName; + mpControl.reset(new EmbeddedControl( maName )); + maGUID = OUString::createFromAscii( it->second.sGUID ); + mpModel = mpControl->createModelFromGuid( maGUID ); + } +} + +OleFormCtrlExportHelper::~OleFormCtrlExportHelper() +{ +} + +void OleFormCtrlExportHelper::exportName( const Reference< XOutputStream >& rxOut ) +{ + oox::BinaryXOutputStream aOut( rxOut, false ); + aOut.writeUnicodeArray( maName ); + aOut.WriteInt32(0); +} + +void OleFormCtrlExportHelper::exportCompObj( const Reference< XOutputStream >& rxOut ) +{ + oox::BinaryXOutputStream aOut( rxOut, false ); + if ( mpModel ) + mpModel->exportCompObj( aOut ); +} + +void OleFormCtrlExportHelper::exportControl( const Reference< XOutputStream >& rxOut, const Size& rSize, bool bAutoClose ) +{ + oox::BinaryXOutputStream aOut( rxOut, bAutoClose ); + if ( mpModel ) + { + ::oox::ole::ControlConverter aConv( mxDocModel, maGrfHelper ); + if(mpControl) + mpControl->convertFromProperties( mxControlModel, aConv ); + mpModel->maSize.first = rSize.Width; + mpModel->maSize.second = rSize.Height; + mpModel->exportBinaryModel( aOut ); + } +} + +MSConvertOCXControls::MSConvertOCXControls( const Reference< css::frame::XModel >& rxModel ) : SvxMSConvertOCXControls( rxModel ), mxCtx( comphelper::getProcessComponentContext() ), maGrfHelper( mxCtx, lcl_getFrame( rxModel ), StorageRef() ) +{ +} + +MSConvertOCXControls::~MSConvertOCXControls() +{ +} + +bool +MSConvertOCXControls::importControlFromStream( ::oox::BinaryInputStream& rInStrm, Reference< XFormComponent >& rxFormComp, std::u16string_view rGuidString ) +{ + ::oox::ole::EmbeddedControl aControl( "Unknown" ); + if( ::oox::ole::ControlModelBase* pModel = aControl.createModelFromGuid( rGuidString ) ) + { + pModel->importBinaryModel( rInStrm ); + rxFormComp.set( mxCtx->getServiceManager()->createInstanceWithContext( pModel->getServiceName(), mxCtx ), UNO_QUERY ); + Reference< XControlModel > xCtlModel( rxFormComp, UNO_QUERY ); + ::oox::ole::ControlConverter aConv( mxModel, maGrfHelper ); + aControl.convertProperties( xCtlModel, aConv ); + } + return rxFormComp.is(); +} + +bool +MSConvertOCXControls::ReadOCXCtlsStream( tools::SvRef<SotStorageStream> const & rSrc1, Reference< XFormComponent > & rxFormComp, + sal_Int32 nPos, + sal_Int32 nStreamSize) +{ + if ( rSrc1.is() ) + { + BinaryXInputStream aCtlsStrm( Reference< XInputStream >( new utl::OSeekableInputStreamWrapper( *rSrc1 ) ), true ); + aCtlsStrm.seek( nPos ); + OUString aStrmClassId = ::oox::ole::OleHelper::importGuid( aCtlsStrm ); + return importControlFromStream( aCtlsStrm, rxFormComp, aStrmClassId, nStreamSize ); + } + return false; +} + +bool MSConvertOCXControls::importControlFromStream( ::oox::BinaryInputStream& rInStrm, Reference< XFormComponent >& rxFormComp, const OUString& rStrmClassId, + sal_Int32 nStreamSize) +{ + if ( !rInStrm.isEof() ) + { + // Special processing for those html controls + bool bOneOfHtmlControls = false; + if ( rStrmClassId.toAsciiUpperCase() == HTML_GUID_SELECT + || rStrmClassId.toAsciiUpperCase() == HTML_GUID_TEXTBOX ) + bOneOfHtmlControls = true; + + if ( bOneOfHtmlControls ) + { + // html controls don't seem have a handy record length following the GUID + // in the binary stream. + // Given the control stream length create a stream of nStreamSize bytes starting from + // nPos ( offset by the guid already read in ) + if ( !nStreamSize ) + return false; + const int nGuidSize = 0x10; + StreamDataSequence aDataSeq; + sal_Int32 nBytesToRead = nStreamSize - nGuidSize; + while ( nBytesToRead ) + nBytesToRead -= rInStrm.readData( aDataSeq, nBytesToRead ); + SequenceInputStream aInSeqStream( aDataSeq ); + importControlFromStream( aInSeqStream, rxFormComp, rStrmClassId ); + } + else + { + importControlFromStream( rInStrm, rxFormComp, rStrmClassId ); + } + } + return rxFormComp.is(); +} + +bool MSConvertOCXControls::ReadOCXStorage( tools::SvRef<SotStorage> const & xOleStg, + Reference< XFormComponent > & rxFormComp ) +{ + if ( xOleStg.is() ) + { + tools::SvRef<SotStorageStream> pNameStream = xOleStg->OpenSotStream("\3OCXNAME", StreamMode::READ); + BinaryXInputStream aNameStream( Reference< XInputStream >( new utl::OSeekableInputStreamWrapper( *pNameStream ) ), true ); + + tools::SvRef<SotStorageStream> pContents = xOleStg->OpenSotStream("contents", StreamMode::READ); + BinaryXInputStream aInStrm( Reference< XInputStream >( new utl::OSeekableInputStreamWrapper( *pContents ) ), true ); + + tools::SvRef<SotStorageStream> pClsStrm = xOleStg->OpenSotStream("\1CompObj", StreamMode::READ); + BinaryXInputStream aClsStrm( Reference< XInputStream >( new utl::OSeekableInputStreamWrapper(*pClsStrm ) ), true ); + aClsStrm.skip(12); + + OUString aStrmClassId = ::oox::ole::OleHelper::importGuid( aClsStrm ); + if ( importControlFromStream( aInStrm, rxFormComp, aStrmClassId, aInStrm.size() ) ) + { + OUString aName = aNameStream.readNulUnicodeArray(); + Reference< XControlModel > xCtlModel( rxFormComp, UNO_QUERY ); + if ( !aName.isEmpty() && xCtlModel.is() ) + { + PropertyMap aPropMap; + aPropMap.setProperty( PROP_Name, aName ); + PropertySet aPropSet( xCtlModel ); + aPropSet.setProperties( aPropMap ); + } + return rxFormComp.is(); + } + } + return false; +} + +bool MSConvertOCXControls::WriteOCXExcelKludgeStream( const css::uno::Reference< css::frame::XModel >& rxModel, const css::uno::Reference< css::io::XOutputStream >& xOutStrm, const css::uno::Reference< css::awt::XControlModel > &rxControlModel, const css::awt::Size& rSize,OUString &rName ) +{ + OleFormCtrlExportHelper exportHelper( comphelper::getProcessComponentContext(), rxModel, rxControlModel ); + if ( !exportHelper.isValid() ) + return false; + rName = exportHelper.getTypeName(); + SvGlobalName aName; + aName.MakeId(exportHelper.getGUID()); + BinaryXOutputStream aOut( xOutStrm, false ); + OleHelper::exportGuid( aOut, aName ); + exportHelper.exportControl( xOutStrm, rSize ); + return true; +} + +bool MSConvertOCXControls::WriteOCXStream( const Reference< XModel >& rxModel, tools::SvRef<SotStorage> const &xOleStg, + const Reference< XControlModel > &rxControlModel, + const css::awt::Size& rSize, OUString &rName) +{ + SvGlobalName aName; + + OleFormCtrlExportHelper exportHelper( comphelper::getProcessComponentContext(), rxModel, rxControlModel ); + + if ( !exportHelper.isValid() ) + return false; + + aName.MakeId(exportHelper.getGUID()); + + OUString sFullName = exportHelper.getFullName(); + rName = exportHelper.getTypeName(); + xOleStg->SetClass( aName, SotClipboardFormatId::EMBEDDED_OBJ_OLE, sFullName); + { + tools::SvRef<SotStorageStream> pNameStream = xOleStg->OpenSotStream("\3OCXNAME"); + Reference< XOutputStream > xOut = new utl::OSeekableOutputStreamWrapper( *pNameStream ); + exportHelper.exportName( xOut ); + } + { + tools::SvRef<SotStorageStream> pObjStream = xOleStg->OpenSotStream("\1CompObj"); + Reference< XOutputStream > xOut = new utl::OSeekableOutputStreamWrapper( *pObjStream ); + exportHelper.exportCompObj( xOut ); + } + { + tools::SvRef<SotStorageStream> pContents = xOleStg->OpenSotStream("contents"); + Reference< XOutputStream > xOut = new utl::OSeekableOutputStreamWrapper( *pContents ); + exportHelper.exportControl( xOut, rSize ); + } + return true; +} + +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/oleobjecthelper.cxx b/oox/source/ole/oleobjecthelper.cxx new file mode 100644 index 000000000..9282e2944 --- /dev/null +++ b/oox/source/ole/oleobjecthelper.cxx @@ -0,0 +1,176 @@ +/* -*- 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 <oox/ole/oleobjecthelper.hxx> + +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/document/XEmbeddedObjectResolver.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <osl/diagnose.h> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <oox/helper/propertymap.hxx> +#include <oox/token/properties.hxx> + +namespace oox::ole { + +using namespace ::com::sun::star; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + +OleObjectInfo::OleObjectInfo() : + mbLinked( false ), + mbShowAsIcon( false ), + mbAutoUpdate( false ), + mbHasPicture( false ) +{ +} + +const char g_aEmbeddedObjScheme[] = "vnd.sun.star.EmbeddedObject:"; + +OleObjectHelper::OleObjectHelper( + const Reference< XMultiServiceFactory >& rxModelFactory, + uno::Reference<frame::XModel> const& xModel) + : m_xModel(xModel) + , mnObjectId( 100 ) +{ + assert(m_xModel.is()); + if( rxModelFactory.is() ) try + { + mxResolver.set( rxModelFactory->createInstance( "com.sun.star.document.ImportEmbeddedObjectResolver" ), UNO_QUERY ); + } + catch(const Exception& ) + { + } +} + +OleObjectHelper::~OleObjectHelper() +{ + try + { + Reference< XComponent > xResolverComp( mxResolver, UNO_QUERY_THROW ); + xResolverComp->dispose(); + } + catch(const Exception& ) + { + } +} + +// TODO: this is probably a sub-optimal approach: ideally the media type +// of the stream from [Content_Types].xml should be stored somewhere for this +// purpose, but currently the media type of all OLE streams in the storage is +// just "application/vnd.sun.star.oleobject" +void SaveInteropProperties(uno::Reference<frame::XModel> const& xModel, + OUString const& rObjectName, OUString const*const pOldObjectName, + OUString const& rProgId) +{ + static constexpr OUStringLiteral sEmbeddingsPropName = u"EmbeddedObjects"; + + // get interop grab bag from document + uno::Reference<beans::XPropertySet> const xDocProps(xModel, uno::UNO_QUERY); + comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag")); + + // get EmbeddedObjects property inside grab bag + comphelper::SequenceAsHashMap objectsList; + if (aGrabBag.find(sEmbeddingsPropName) != aGrabBag.end()) + objectsList << aGrabBag[sEmbeddingsPropName]; + + uno::Sequence< beans::PropertyValue > aGrabBagAttribute{ comphelper::makePropertyValue("ProgID", + rProgId) }; + + // If we got an "old name", erase that first. + if (pOldObjectName) + { + comphelper::SequenceAsHashMap::iterator it = objectsList.find(*pOldObjectName); + if (it != objectsList.end()) + objectsList.erase(it); + } + + objectsList[rObjectName] <<= aGrabBagAttribute; + + // put objects list back into the grab bag + aGrabBag[sEmbeddingsPropName] <<= objectsList.getAsConstPropertyValueList(); + + // put grab bag back into the document + xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList())); +} + +bool OleObjectHelper::importOleObject( PropertyMap& rPropMap, const OleObjectInfo& rOleObject, const awt::Size& rObjSize ) +{ + bool bRet = false; + + if( rOleObject.mbLinked ) + { + // linked OLE object - set target URL + if( !rOleObject.maTargetLink.isEmpty() ) + { + rPropMap.setProperty( PROP_LinkURL, rOleObject.maTargetLink); + bRet = true; + } + } + else + { + // embedded OLE object - import the embedded data + if( rOleObject.maEmbeddedData.hasElements() && mxResolver.is() ) try + { + OUString aObjectId = "Obj" + OUString::number( mnObjectId++ ); + + Reference< XNameAccess > xResolverNA( mxResolver, UNO_QUERY_THROW ); + Reference< XOutputStream > xOutStrm( xResolverNA->getByName( aObjectId ), UNO_QUERY_THROW ); + xOutStrm->writeBytes( rOleObject.maEmbeddedData ); + xOutStrm->closeOutput(); + + SaveInteropProperties(m_xModel, aObjectId, nullptr, rOleObject.maProgId); + + OUString aUrl = mxResolver->resolveEmbeddedObjectURL( aObjectId ); + OSL_ENSURE( aUrl.match( g_aEmbeddedObjScheme ), "OleObjectHelper::importOleObject - unexpected URL scheme" ); + OUString aPersistName = aUrl.copy( strlen(g_aEmbeddedObjScheme) ); + if( !aPersistName.isEmpty() ) + { + rPropMap.setProperty( PROP_PersistName, aPersistName); + bRet = true; + } + } + catch(const Exception& ) + { + } + } + + if( bRet ) + { + rPropMap.setProperty( PROP_Aspect, (rOleObject.mbShowAsIcon ? Aspects::MSOLE_ICON : Aspects::MSOLE_CONTENT)); + rPropMap.setProperty( PROP_VisualArea, awt::Rectangle( 0, 0, rObjSize.Width, rObjSize.Height )); + } + return bRet; +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/olestorage.cxx b/oox/source/ole/olestorage.cxx new file mode 100644 index 000000000..4b20e20f8 --- /dev/null +++ b/oox/source/ole/olestorage.cxx @@ -0,0 +1,376 @@ +/* -*- 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 <oox/ole/olestorage.hxx> + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/NotConnectedException.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/helper/binaryoutputstream.hxx> +#include <oox/helper/containerhelper.hxx> + +namespace oox::ole { + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + +namespace { + +/** Implementation of an OLE storage output stream that inserts itself into the + storage when it is closed. + */ +class OleOutputStream : public ::cppu::WeakImplHelper< XSeekable, XOutputStream > +{ +public: + explicit OleOutputStream( + const Reference< XComponentContext >& rxContext, + const Reference< XNameContainer >& rxStorage, + const OUString& rElementName ); + + virtual void SAL_CALL seek( sal_Int64 nPos ) override; + virtual sal_Int64 SAL_CALL getPosition() override; + virtual sal_Int64 SAL_CALL getLength() override; + + virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) override; + virtual void SAL_CALL flush() override; + virtual void SAL_CALL closeOutput() override; + +private: + /// @throws IOException + void ensureSeekable() const; + /// @throws NotConnectedException + void ensureConnected() const; + +private: + Reference< XNameContainer > mxStorage; + Reference< XStream > mxTempFile; + Reference< XOutputStream > mxOutStrm; + Reference< XSeekable > mxSeekable; + OUString maElementName; +}; + +OleOutputStream::OleOutputStream( const Reference< XComponentContext >& rxContext, + const Reference< XNameContainer >& rxStorage, const OUString& rElementName ) : + mxStorage( rxStorage ), + maElementName( rElementName ) +{ + try + { + mxTempFile.set( TempFile::create(rxContext), UNO_QUERY_THROW ); + mxOutStrm = mxTempFile->getOutputStream(); + mxSeekable.set( mxOutStrm, UNO_QUERY ); + } + catch(const Exception& ) + { + } +} + +void SAL_CALL OleOutputStream::seek( sal_Int64 nPos ) +{ + ensureSeekable(); + mxSeekable->seek( nPos ); +} + +sal_Int64 SAL_CALL OleOutputStream::getPosition() +{ + ensureSeekable(); + return mxSeekable->getPosition(); +} + +sal_Int64 SAL_CALL OleOutputStream::getLength() +{ + ensureSeekable(); + return mxSeekable->getLength(); +} + +void SAL_CALL OleOutputStream::writeBytes( const Sequence< sal_Int8 >& rData ) +{ + ensureConnected(); + mxOutStrm->writeBytes( rData ); +} + +void SAL_CALL OleOutputStream::flush() +{ + ensureConnected(); + mxOutStrm->flush(); +} + +void SAL_CALL OleOutputStream::closeOutput() +{ + ensureConnected(); + ensureSeekable(); + // remember the class members + Reference< XOutputStream > xOutStrm = mxOutStrm; + Reference< XSeekable > xSeekable = mxSeekable; + // reset all class members + mxOutStrm.clear(); + mxSeekable.clear(); + // close stream (and let it throw something if needed) + xOutStrm->closeOutput(); + // on success, insert the stream into the OLE storage (must be seek-ed back before) + xSeekable->seek( 0 ); + if( !ContainerHelper::insertByName( mxStorage, maElementName, Any( mxTempFile ) ) ) + throw IOException(); +} + +void OleOutputStream::ensureSeekable() const +{ + if( !mxSeekable.is() ) + throw IOException(); +} + +void OleOutputStream::ensureConnected() const +{ + if( !mxOutStrm.is() ) + throw NotConnectedException(); +} + +} // namespace + +OleStorage::OleStorage( const Reference< XComponentContext >& rxContext, + const Reference< XInputStream >& rxInStream, bool bBaseStreamAccess ) : + StorageBase( rxInStream, bBaseStreamAccess ), + mxContext( rxContext ), + mpParentStorage( nullptr ) +{ + OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" ); + initStorage( rxInStream ); +} + +OleStorage::OleStorage( const Reference< XComponentContext >& rxContext, + const Reference< XStream >& rxOutStream, bool bBaseStreamAccess ) : + StorageBase( rxOutStream, bBaseStreamAccess ), + mxContext( rxContext ), + mpParentStorage( nullptr ) +{ + OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" ); + initStorage( rxOutStream ); +} + +OleStorage::OleStorage( const OleStorage& rParentStorage, + const Reference< XNameContainer >& rxStorage, const OUString& rElementName, bool bReadOnly ) : + StorageBase( rParentStorage, rElementName, bReadOnly ), + mxContext( rParentStorage.mxContext ), + mxStorage( rxStorage ), + mpParentStorage( &rParentStorage ) +{ + OSL_ENSURE( mxStorage.is(), "OleStorage::OleStorage - missing substorage elements" ); +} + +OleStorage::OleStorage( const OleStorage& rParentStorage, + const Reference< XStream >& rxOutStream, const OUString& rElementName ) : + StorageBase( rParentStorage, rElementName, false ), + mxContext( rParentStorage.mxContext ), + mpParentStorage( &rParentStorage ) +{ + initStorage( rxOutStream ); +} + +OleStorage::~OleStorage() +{ +} + +void OleStorage::initStorage( const Reference< XInputStream >& rxInStream ) +{ + // if stream is not seekable, create temporary copy + Reference< XInputStream > xInStrm = rxInStream; + if( !Reference< XSeekable >( xInStrm, UNO_QUERY ).is() ) try + { + Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW ); + { + Reference< XOutputStream > xOutStrm( xTempFile->getOutputStream(), UNO_SET_THROW ); + /* Pass false to both binary stream objects to keep the UNO + streams alive. Life time of these streams is controlled by the + tempfile implementation. */ + BinaryXOutputStream aOutStrm( xOutStrm, false ); + BinaryXInputStream aInStrm( xInStrm, false ); + aInStrm.copyToStream( aOutStrm ); + } // scope closes output stream of tempfile + xInStrm = xTempFile->getInputStream(); + } + catch(const Exception& ) + { + OSL_FAIL( "OleStorage::initStorage - cannot create temporary copy of input stream" ); + } + + // create base storage object + if( xInStrm.is() ) try + { + Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW ); + Sequence< Any > aArgs{ Any(xInStrm), + Any(true) };// true = do not create a copy of the input stream + mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW ); + } + catch(const Exception& ) + { + } +} + +void OleStorage::initStorage( const Reference< XStream >& rxOutStream ) +{ + // create base storage object + if( rxOutStream.is() ) try + { + Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW ); + Sequence< Any > aArgs{ Any(rxOutStream), + Any(true) }; // true = do not create a copy of the stream + mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW ); + } + catch(const Exception& ) + { + } +} + +// StorageBase interface ------------------------------------------------------ + +bool OleStorage::implIsStorage() const +{ + if( mxStorage.is() ) try + { + /* If this is not an OLE storage, hasElements() of the OLESimpleStorage + implementation throws an exception. But we do not return the result + of hasElements(), because an empty storage is a valid storage too. */ + (void)mxStorage->hasElements(); + return true; + } + catch(const Exception& ) + { + } + return false; +} + +Reference< XStorage > OleStorage::implGetXStorage() const +{ + OSL_FAIL( "OleStorage::getXStorage - not implemented" ); + return Reference< XStorage >(); +} + +void OleStorage::implGetElementNames( ::std::vector< OUString >& orElementNames ) const +{ + if( mxStorage.is() ) try + { + const Sequence<OUString> aNames = mxStorage->getElementNames(); + if( aNames.hasElements() ) + orElementNames.insert( orElementNames.end(), aNames.begin(), aNames.end() ); + } + catch(const Exception& ) + { + } +} + +StorageRef OleStorage::implOpenSubStorage( const OUString& rElementName, bool bCreateMissing ) +{ + StorageRef xSubStorage; + if( mxStorage.is() && !rElementName.isEmpty() ) + { + try + { + Reference< XNameContainer > xSubElements( mxStorage->getByName( rElementName ), UNO_QUERY_THROW ); + xSubStorage.reset( new OleStorage( *this, xSubElements, rElementName, true ) ); + } + catch(const Exception& ) + { + } + + /* The OLESimpleStorage API implementation seems to be buggy in the + area of writable inplace substorage (sometimes it overwrites other + unrelated streams with zero bytes). We go the save way and create a + new OLE storage based on a temporary file. All operations are + performed on this clean storage. On committing, the storage will be + completely re-inserted into the parent storage. */ + if( !isReadOnly() && (bCreateMissing || xSubStorage) ) try + { + // create new storage based on a temp file + Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW ); + StorageRef xTempStorage( new OleStorage( *this, xTempFile, rElementName ) ); + // copy existing substorage into temp storage + if( xSubStorage ) + xSubStorage->copyStorageToStorage( *xTempStorage ); + // return the temp storage to caller + xSubStorage = xTempStorage; + } + catch(const Exception& ) + { + } + } + return xSubStorage; +} + +Reference< XInputStream > OleStorage::implOpenInputStream( const OUString& rElementName ) +{ + Reference< XInputStream > xInStream; + if( mxStorage.is() ) try + { + xInStream.set( mxStorage->getByName( rElementName ), UNO_QUERY ); + } + catch(const Exception& ) + { + } + return xInStream; +} + +Reference< XOutputStream > OleStorage::implOpenOutputStream( const OUString& rElementName ) +{ + Reference< XOutputStream > xOutStream; + if( mxStorage.is() && !rElementName.isEmpty() ) + xOutStream.set( new OleOutputStream( mxContext, mxStorage, rElementName ) ); + return xOutStream; +} + +void OleStorage::implCommit() const +{ + try + { + // commit this storage (finalizes the file this storage is based on) + Reference< XTransactedObject >( mxStorage, UNO_QUERY_THROW )->commit(); + // re-insert this storage into the parent storage + if( mpParentStorage ) + { + if( mpParentStorage->mxStorage->hasByName( getName() ) ) + { + // replaceByName() does not work (#i109539#) + mpParentStorage->mxStorage->removeByName( getName() ); + Reference< XTransactedObject >( mpParentStorage->mxStorage, UNO_QUERY_THROW )->commit(); + } + mpParentStorage->mxStorage->insertByName( getName(), Any( mxStorage ) ); + // this requires another commit(), which will be performed by the parent storage + } + } + catch(const Exception& ) + { + } +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/vbacontrol.cxx b/oox/source/ole/vbacontrol.cxx new file mode 100644 index 000000000..9c2a30163 --- /dev/null +++ b/oox/source/ole/vbacontrol.cxx @@ -0,0 +1,874 @@ +/* -*- 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 <oox/ole/vbacontrol.hxx> + +#include <algorithm> +#include <set> +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/io/XInputStreamProvider.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/UnitConversion.hxx> +#include <xmlscript/xmldlg_imexp.hxx> +#include <oox/helper/attributelist.hxx> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/helper/containerhelper.hxx> +#include <oox/helper/propertymap.hxx> +#include <oox/helper/propertyset.hxx> +#include <oox/helper/storagebase.hxx> +#include <oox/helper/textinputstream.hxx> +#include <oox/ole/vbahelper.hxx> +#include <oox/token/properties.hxx> +#include <oox/token/tokens.hxx> +#include <unordered_map> + +namespace oox::ole { + +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + +namespace { + +const sal_uInt16 VBA_SITE_CLASSIDINDEX = 0x8000; +const sal_uInt16 VBA_SITE_INDEXMASK = 0x7FFF; +const sal_uInt16 VBA_SITE_FORM = 7; +const sal_uInt16 VBA_SITE_IMAGE = 12; +const sal_uInt16 VBA_SITE_FRAME = 14; +const sal_uInt16 VBA_SITE_SPINBUTTON = 16; +const sal_uInt16 VBA_SITE_COMMANDBUTTON = 17; +const sal_uInt16 VBA_SITE_TABSTRIP = 18; +const sal_uInt16 VBA_SITE_LABEL = 21; +const sal_uInt16 VBA_SITE_TEXTBOX = 23; +const sal_uInt16 VBA_SITE_LISTBOX = 24; +const sal_uInt16 VBA_SITE_COMBOBOX = 25; +const sal_uInt16 VBA_SITE_CHECKBOX = 26; +const sal_uInt16 VBA_SITE_OPTIONBUTTON = 27; +const sal_uInt16 VBA_SITE_TOGGLEBUTTON = 28; +const sal_uInt16 VBA_SITE_SCROLLBAR = 47; +const sal_uInt16 VBA_SITE_MULTIPAGE = 57; +const sal_uInt16 VBA_SITE_UNKNOWN = 0x7FFF; + +const sal_uInt32 VBA_SITE_TABSTOP = 0x00000001; +const sal_uInt32 VBA_SITE_VISIBLE = 0x00000002; +const sal_uInt32 VBA_SITE_OSTREAM = 0x00000010; +const sal_uInt32 VBA_SITE_DEFFLAGS = 0x00000033; + +const sal_uInt8 VBA_SITEINFO_COUNT = 0x80; +const sal_uInt8 VBA_SITEINFO_MASK = 0x7F; + +/** Collects names of all controls in a user form or container control. Allows + to generate unused names for dummy controls separating option groups. + */ +class VbaControlNamesSet +{ +public: + explicit VbaControlNamesSet(); + + /** Inserts the name of the passed control. */ + void insertName( const VbaFormControl& rControl ); + /** Returns a name that is not contained in this set. */ + OUString generateDummyName(); + +private: + ::std::set< OUString > + maCtrlNames; + sal_Int32 mnIndex; +}; + +constexpr OUStringLiteral gaDummyBaseName( u"DummyGroupSep" ); + +VbaControlNamesSet::VbaControlNamesSet() : + mnIndex( 0 ) +{ +} + +void VbaControlNamesSet::insertName( const VbaFormControl& rControl ) +{ + OUString aName = rControl.getControlName(); + if( !aName.isEmpty() ) + maCtrlNames.insert( aName ); +} + +OUString VbaControlNamesSet::generateDummyName() +{ + OUString aCtrlName; + do + { + aCtrlName = gaDummyBaseName + OUString::number( ++mnIndex ); + } + while( maCtrlNames.count( aCtrlName ) > 0 ); + maCtrlNames.insert( aCtrlName ); + return aCtrlName; +} + +/** Functor that inserts the name of a control into a VbaControlNamesSet. */ +struct VbaControlNameInserter +{ +public: + VbaControlNamesSet& mrCtrlNames; + explicit VbaControlNameInserter( VbaControlNamesSet& rCtrlNames ) : mrCtrlNames( rCtrlNames ) {} + void operator()( const VbaFormControl& rControl ) { mrCtrlNames.insertName( rControl ); } +}; + +/** A dummy invisible form control (fixed label without text) that is used to + separate two groups of option buttons. + */ +class VbaDummyFormControl : public VbaFormControl +{ +public: + explicit VbaDummyFormControl( const OUString& rName ); +}; + +VbaDummyFormControl::VbaDummyFormControl( const OUString& rName ) +{ + mxSiteModel = std::make_shared<VbaSiteModel>(); + mxSiteModel->importProperty( XML_Name, rName ); + mxSiteModel->importProperty( XML_VariousPropertyBits, OUString( '0' ) ); + + mxCtrlModel = std::make_shared<AxLabelModel>(); + mxCtrlModel->setAwtModelMode(); + mxCtrlModel->importProperty( XML_Size, "10;10" ); +} + +} // namespace + +VbaSiteModel::VbaSiteModel() : + maPos( 0, 0 ), + mnId( 0 ), + mnHelpContextId( 0 ), + mnFlags( VBA_SITE_DEFFLAGS ), + mnStreamLen( 0 ), + mnTabIndex( -1 ), + mnClassIdOrCache( VBA_SITE_UNKNOWN ), + mnGroupId( 0 ) +{ +} + +VbaSiteModel::~VbaSiteModel() +{ +} + +void VbaSiteModel::importProperty( sal_Int32 nPropId, const OUString& rValue ) +{ + switch( nPropId ) + { + case XML_Name: maName = rValue; break; + case XML_Tag: maTag = rValue; break; + case XML_VariousPropertyBits: mnFlags = AttributeConversion::decodeUnsigned( rValue ); break; + } +} + +bool VbaSiteModel::importBinaryModel( BinaryInputStream& rInStrm ) +{ + AxBinaryPropertyReader aReader( rInStrm ); + aReader.readStringProperty( maName ); + aReader.readStringProperty( maTag ); + aReader.readIntProperty< sal_Int32 >( mnId ); + aReader.readIntProperty< sal_Int32 >( mnHelpContextId ); + aReader.readIntProperty< sal_uInt32 >( mnFlags ); + aReader.readIntProperty< sal_uInt32 >( mnStreamLen ); + aReader.readIntProperty< sal_Int16 >( mnTabIndex ); + aReader.readIntProperty< sal_uInt16 >( mnClassIdOrCache ); + aReader.readPairProperty( maPos ); + aReader.readIntProperty< sal_uInt16 >( mnGroupId ); + aReader.skipUndefinedProperty(); + aReader.readStringProperty( maToolTip ); + aReader.skipStringProperty(); // license key + aReader.readStringProperty( maControlSource ); + aReader.readStringProperty( maRowSource ); + return aReader.finalizeImport(); +} + +void VbaSiteModel::moveRelative( const AxPairData& rDistance ) +{ + maPos.first += rDistance.first; + maPos.second += rDistance.second; +} + +bool VbaSiteModel::isContainer() const +{ + return !getFlag( mnFlags, VBA_SITE_OSTREAM ); +} + +sal_uInt32 VbaSiteModel::getStreamLength() const +{ + return isContainer() ? 0 : mnStreamLen; +} + +OUString VbaSiteModel::getSubStorageName() const +{ + if( mnId >= 0 ) + { + OUStringBuffer aBuffer; + aBuffer.append( 'i' ); + if( mnId < 10 ) + aBuffer.append( '0' ); + aBuffer.append( mnId ); + return aBuffer.makeStringAndClear(); + } + return OUString(); +} + +ControlModelRef VbaSiteModel::createControlModel( const AxClassTable& rClassTable ) const +{ + ControlModelRef xCtrlModel; + + sal_Int32 nTypeIndex = static_cast< sal_Int32 >( mnClassIdOrCache & VBA_SITE_INDEXMASK ); + if( !getFlag( mnClassIdOrCache, VBA_SITE_CLASSIDINDEX ) ) + { + switch( nTypeIndex ) + { + case VBA_SITE_COMMANDBUTTON: xCtrlModel= std::make_shared<AxCommandButtonModel>(); break; + case VBA_SITE_LABEL: xCtrlModel= std::make_shared<AxLabelModel>(); break; + case VBA_SITE_IMAGE: xCtrlModel= std::make_shared<AxImageModel>(); break; + case VBA_SITE_TOGGLEBUTTON: xCtrlModel= std::make_shared<AxToggleButtonModel>(); break; + case VBA_SITE_CHECKBOX: xCtrlModel= std::make_shared<AxCheckBoxModel>(); break; + case VBA_SITE_OPTIONBUTTON: xCtrlModel= std::make_shared<AxOptionButtonModel>(); break; + case VBA_SITE_TEXTBOX: xCtrlModel= std::make_shared<AxTextBoxModel>(); break; + case VBA_SITE_LISTBOX: xCtrlModel= std::make_shared<AxListBoxModel>(); break; + case VBA_SITE_COMBOBOX: xCtrlModel= std::make_shared<AxComboBoxModel>(); break; + case VBA_SITE_SPINBUTTON: xCtrlModel= std::make_shared<AxSpinButtonModel>(); break; + case VBA_SITE_SCROLLBAR: xCtrlModel= std::make_shared<AxScrollBarModel>(); break; + case VBA_SITE_TABSTRIP: xCtrlModel= std::make_shared<AxTabStripModel>(); + break; + case VBA_SITE_FRAME: xCtrlModel= std::make_shared<AxFrameModel>(); break; + case VBA_SITE_MULTIPAGE: xCtrlModel= std::make_shared<AxMultiPageModel>(); + break; + case VBA_SITE_FORM: xCtrlModel= std::make_shared<AxPageModel>(); + break; + default: OSL_FAIL( "VbaSiteModel::createControlModel - unknown type index" ); + } + } + else + { + const OUString* pGuid = ContainerHelper::getVectorElement( rClassTable, nTypeIndex ); + OSL_ENSURE( pGuid, "VbaSiteModel::createControlModel - invalid class table index" ); + if( pGuid ) + { + if( *pGuid == COMCTL_GUID_SCROLLBAR_60 ) + xCtrlModel = std::make_shared<ComCtlScrollBarModel>( 6 ); + else if( *pGuid == COMCTL_GUID_PROGRESSBAR_50 ) + xCtrlModel = std::make_shared<ComCtlProgressBarModel>( 5 ); + else if( *pGuid == COMCTL_GUID_PROGRESSBAR_60 ) + xCtrlModel = std::make_shared<ComCtlProgressBarModel>( 6 ); + } + } + + if( xCtrlModel ) + { + // user form controls are AWT models + xCtrlModel->setAwtModelMode(); + + // check that container model matches container flag in site data + bool bModelIsContainer = dynamic_cast< const AxContainerModelBase* >( xCtrlModel.get() ) != nullptr; + bool bTypeMatch = bModelIsContainer == isContainer(); + OSL_ENSURE( bTypeMatch, "VbaSiteModel::createControlModel - container type does not match container flag" ); + if( !bTypeMatch ) + xCtrlModel.reset(); + } + return xCtrlModel; +} + +void VbaSiteModel::convertProperties( PropertyMap& rPropMap, + const ControlConverter& rConv, ApiControlType eCtrlType, sal_Int32 nCtrlIndex ) const +{ + rPropMap.setProperty( PROP_Name, maName ); + rPropMap.setProperty( PROP_Tag, maTag ); + + if( eCtrlType != API_CONTROL_DIALOG ) + { + rPropMap.setProperty( PROP_HelpText, maToolTip ); + rPropMap.setProperty( PROP_EnableVisible, getFlag( mnFlags, VBA_SITE_VISIBLE ) ); + // we need to set the passed control index to make option button groups work + if( (0 <= nCtrlIndex) && (nCtrlIndex <= SAL_MAX_INT16) ) + rPropMap.setProperty( PROP_TabIndex, static_cast< sal_Int16 >( nCtrlIndex ) ); + // progress bar and group box support TabIndex, but not Tabstop... + if( (eCtrlType != API_CONTROL_PROGRESSBAR) && (eCtrlType != API_CONTROL_GROUPBOX) && (eCtrlType != API_CONTROL_FRAME) && (eCtrlType != API_CONTROL_PAGE) ) + rPropMap.setProperty( PROP_Tabstop, getFlag( mnFlags, VBA_SITE_TABSTOP ) ); + rConv.convertPosition( rPropMap, maPos ); + } +} + +VbaFormControl::VbaFormControl() +{ +} + +VbaFormControl::~VbaFormControl() +{ +} + +void VbaFormControl::importModelOrStorage( BinaryInputStream& rInStrm, StorageBase& rStrg, const AxClassTable& rClassTable ) +{ + if( !mxSiteModel ) + return; + + if( mxSiteModel->isContainer() ) + { + StorageRef xSubStrg = rStrg.openSubStorage( mxSiteModel->getSubStorageName(), false ); + OSL_ENSURE( xSubStrg, "VbaFormControl::importModelOrStorage - cannot find storage for embedded control" ); + if( xSubStrg ) + importStorage( *xSubStrg, rClassTable ); + } + else if( !rInStrm.isEof() ) + { + sal_Int64 nNextStrmPos = rInStrm.tell() + mxSiteModel->getStreamLength(); + importControlModel( rInStrm, rClassTable ); + rInStrm.seek( nNextStrmPos ); + } +} + +OUString VbaFormControl::getControlName() const +{ + return mxSiteModel ? mxSiteModel->getName() : OUString(); +} + +void VbaFormControl::createAndConvert( sal_Int32 nCtrlIndex, + const Reference< XNameContainer >& rxParentNC, const ControlConverter& rConv ) const +{ + if( !(rxParentNC.is() && mxSiteModel && mxCtrlModel) ) + return; + + try + { + // create the control model + OUString aServiceName = mxCtrlModel->getServiceName(); + Reference< XMultiServiceFactory > xModelFactory( rxParentNC, UNO_QUERY_THROW ); + Reference< XControlModel > xCtrlModel( xModelFactory->createInstance( aServiceName ), UNO_QUERY_THROW ); + + // convert all properties and embedded controls + if( convertProperties( xCtrlModel, rConv, nCtrlIndex ) ) + { + // insert into parent container + const OUString& rCtrlName = mxSiteModel->getName(); + OSL_ENSURE( !rxParentNC->hasByName( rCtrlName ), "VbaFormControl::createAndConvert - multiple controls with equal name" ); + ContainerHelper::insertByName( rxParentNC, rCtrlName, Any( xCtrlModel ) ); + } + } + catch(const Exception& ) + { + } +} + +// protected ------------------------------------------------------------------ + +void VbaFormControl::importControlModel( BinaryInputStream& rInStrm, const AxClassTable& rClassTable ) +{ + createControlModel( rClassTable ); + if( mxCtrlModel ) + mxCtrlModel->importBinaryModel( rInStrm ); +} + +void VbaFormControl::importStorage( StorageBase& rStrg, const AxClassTable& rClassTable ) +{ + createControlModel( rClassTable ); + AxContainerModelBase* pContainerModel = dynamic_cast< AxContainerModelBase* >( mxCtrlModel.get() ); + OSL_ENSURE( pContainerModel, "VbaFormControl::importStorage - missing container control model" ); + if( !pContainerModel ) + return; + + /* Open the 'f' stream containing the model of this control and a list + of site models for all child controls. */ + BinaryXInputStream aFStrm( rStrg.openInputStream( "f" ), true ); + OSL_ENSURE( !aFStrm.isEof(), "VbaFormControl::importStorage - missing 'f' stream" ); + + /* Read the properties of this container control and the class table + (into the maClassTable vector) containing a list of GUIDs for + exotic embedded controls. */ + if( !(!aFStrm.isEof() && pContainerModel->importBinaryModel( aFStrm ) && pContainerModel->importClassTable( aFStrm, maClassTable )) ) + return; + + /* Read the site models of all embedded controls (this fills the + maControls vector). Ignore failure of importSiteModels() but + try to import as much controls as possible. */ + importEmbeddedSiteModels( aFStrm ); + /* Open the 'o' stream containing models of embedded simple + controls. Stream may be empty or missing, if this control + contains no controls or only container controls. */ + BinaryXInputStream aOStrm( rStrg.openInputStream( "o" ), true ); + + /* Iterate over all embedded controls, import model from 'o' + stream (for embedded simple controls) or from the substorage + (for embedded container controls). */ + maControls.forEachMem( &VbaFormControl::importModelOrStorage, + ::std::ref( aOStrm ), ::std::ref( rStrg ), ::std::cref( maClassTable ) ); + + // Special handling for multi-page which has non-standard + // containment and additionally needs to re-order Page children + if ( pContainerModel->getControlType() == API_CONTROL_MULTIPAGE ) + { + AxMultiPageModel* pMultiPage = dynamic_cast< AxMultiPageModel* >( pContainerModel ); + assert(pMultiPage); + { + BinaryXInputStream aXStrm( rStrg.openInputStream( "x" ), true ); + pMultiPage->importPageAndMultiPageProperties( aXStrm, maControls.size() ); + } + typedef std::unordered_map< sal_uInt32, std::shared_ptr< VbaFormControl > > IdToPageMap; + IdToPageMap idToPage; + AxArrayString sCaptions; + + for (auto const& control : maControls) + { + auto& elem = control->mxCtrlModel; + if (!elem) + { + SAL_WARN("oox", "empty control model"); + continue; + } + if (elem->getControlType() == API_CONTROL_PAGE) + { + VbaSiteModelRef xPageSiteRef = control->mxSiteModel; + if ( xPageSiteRef ) + idToPage[ xPageSiteRef->getId() ] = control; + } + else if (elem->getControlType() == API_CONTROL_TABSTRIP) + { + AxTabStripModel* pTabStrip = static_cast<AxTabStripModel*>(elem.get()); + sCaptions = pTabStrip->maItems; + pMultiPage->mnActiveTab = pTabStrip->mnListIndex; + pMultiPage->mnTabStyle = pTabStrip->mnTabStyle; + } + else + { + SAL_WARN("oox", "unexpected control type " << elem->getControlType()); + } + } + // apply caption/titles to pages + + maControls.clear(); + // need to sort the controls according to the order of the ids + if ( sCaptions.size() == idToPage.size() ) + { + AxArrayString::iterator itCaption = sCaptions.begin(); + for ( const auto& rCtrlId : pMultiPage->mnIDs ) + { + IdToPageMap::iterator iter = idToPage.find( rCtrlId ); + if ( iter != idToPage.end() ) + { + AxPageModel* pPage = static_cast<AxPageModel*> ( iter->second->mxCtrlModel.get() ); + + pPage->importProperty( XML_Caption, *itCaption ); + maControls.push_back( iter->second ); + } + ++itCaption; + } + } + } + /* Reorder the controls (sorts all option buttons of an option + group together), and move all children of all embedded frames + (group boxes) to this control (UNO group boxes cannot contain + other controls). */ + finalizeEmbeddedControls(); +} + +bool VbaFormControl::convertProperties( const Reference< XControlModel >& rxCtrlModel, + const ControlConverter& rConv, sal_Int32 nCtrlIndex ) const +{ + if( rxCtrlModel.is() && mxSiteModel && mxCtrlModel ) + { + const OUString& rCtrlName = mxSiteModel->getName(); + OSL_ENSURE( !rCtrlName.isEmpty(), "VbaFormControl::convertProperties - control without name" ); + if( !rCtrlName.isEmpty() ) + { + // convert all properties + PropertyMap aPropMap; + mxSiteModel->convertProperties( aPropMap, rConv, mxCtrlModel->getControlType(), nCtrlIndex ); + rConv.bindToSources( rxCtrlModel, mxSiteModel->getControlSource(), mxSiteModel->getRowSource() ); + mxCtrlModel->convertProperties( aPropMap, rConv ); + mxCtrlModel->convertSize( aPropMap, rConv ); + PropertySet aPropSet( rxCtrlModel ); + aPropSet.setProperties( aPropMap ); + + // create and convert all embedded controls + if( !maControls.empty() ) try + { + Reference< XNameContainer > xCtrlModelNC( rxCtrlModel, UNO_QUERY_THROW ); + /* Call conversion for all controls. Pass vector index as new + tab order to make option button groups work correctly. */ + maControls.forEachMemWithIndex( &VbaFormControl::createAndConvert, + ::std::cref( xCtrlModelNC ), ::std::cref( rConv ) ); + } + catch(const Exception& ) + { + OSL_FAIL( "VbaFormControl::convertProperties - cannot get control container interface" ); + } + + return true; + } + } + return false; +} + +// private -------------------------------------------------------------------- + +void VbaFormControl::createControlModel( const AxClassTable& rClassTable ) +{ + // derived classes may have created their own control model + if( !mxCtrlModel && mxSiteModel ) + mxCtrlModel = mxSiteModel->createControlModel( rClassTable ); +} + +bool VbaFormControl::importSiteModel( BinaryInputStream& rInStrm ) +{ + mxSiteModel = std::make_shared<VbaSiteModel>(); + return mxSiteModel->importBinaryModel( rInStrm ); +} + +void VbaFormControl::importEmbeddedSiteModels( BinaryInputStream& rInStrm ) +{ + sal_uInt64 nAnchorPos = rInStrm.tell(); + sal_uInt32 nSiteCount, nSiteDataSize; + nSiteCount = rInStrm.readuInt32(); + nSiteDataSize = rInStrm.readuInt32(); + sal_Int64 nSiteEndPos = rInStrm.tell() + nSiteDataSize; + + // skip the site info structure + sal_uInt32 nSiteIndex = 0; + while( !rInStrm.isEof() && (nSiteIndex < nSiteCount) ) + { + rInStrm.skip( 1 ); // site depth + sal_uInt8 nTypeCount = rInStrm.readuInt8(); // 'type-or-count' byte + if( getFlag( nTypeCount, VBA_SITEINFO_COUNT ) ) + { + /* Count flag is set: the 'type-or-count' byte contains the number + of controls in the lower bits, the type specifier follows in + the next byte. The type specifier should always be 1 according + to the specification. */ + rInStrm.skip( 1 ); + nSiteIndex += (nTypeCount & VBA_SITEINFO_MASK); + } + else + { + /* Count flag is not set: the 'type-or-count' byte contains the + type specifier of *one* control in the lower bits (this type + should be 1, see above). */ + ++nSiteIndex; + } + } + // align the stream to 32bit, relative to start of entire site info + rInStrm.alignToBlock( 4, nAnchorPos ); + + // import the site models for all embedded controls + maControls.clear(); + bool bValid = !rInStrm.isEof(); + for( nSiteIndex = 0; bValid && (nSiteIndex < nSiteCount); ++nSiteIndex ) + { + VbaFormControlRef xControl = std::make_shared<VbaFormControl>(); + maControls.push_back( xControl ); + bValid = xControl->importSiteModel( rInStrm ); + } + + rInStrm.seek( nSiteEndPos ); +} + +void VbaFormControl::finalizeEmbeddedControls() +{ + /* This function performs two tasks: + + 1) Reorder the controls appropriately (sort all option buttons of an + option group together to make grouping work). + 2) Move all children of all embedded frames (group boxes) to this + control (UNO group boxes cannot contain other controls). + */ + + // first, sort all controls by original tab index + ::std::sort( maControls.begin(), maControls.end(), &compareByTabIndex ); + + /* Collect the programmatical names of all embedded controls (needed to be + able to set unused names to new dummy controls created below). Also + collect the names of all children of embedded frames (group boxes). + Luckily, names of controls must be unique in the entire form, not just + in the current container. */ + VbaControlNamesSet aControlNames; + VbaControlNameInserter aInserter( aControlNames ); + maControls.forEach( aInserter ); + for (auto const& control : maControls) + if( control->mxCtrlModel && (control->mxCtrlModel->getControlType() == API_CONTROL_GROUPBOX) ) + control->maControls.forEach( aInserter ); + + /* Reprocess the sorted list and collect all option button controls that + are part of the same option group (determined by group name). All + controls will be stored in a vector of vectors, that collects every + option button group in one vector element, and other controls between + these option groups (or leading or trailing controls) in other vector + elements. If an option button group follows another group, a dummy + separator control has to be inserted. */ + typedef RefVector< VbaFormControlVector > VbaFormControlVectorVector; + VbaFormControlVectorVector aControlGroups; + + typedef RefMap< OUString, VbaFormControlVector > VbaFormControlVectorMap; + VbaFormControlVectorMap aOptionGroups; + + typedef VbaFormControlVectorMap::mapped_type VbaFormControlVectorRef; + bool bLastWasOptionButton = false; + for (auto const& control : maControls) + { + const ControlModelBase* pCtrlModel = control->mxCtrlModel.get(); + + if( const AxOptionButtonModel* pOptButtonModel = dynamic_cast< const AxOptionButtonModel* >( pCtrlModel ) ) + { + // check if a new option group needs to be created + const OUString& rGroupName = pOptButtonModel->getGroupName(); + VbaFormControlVectorRef& rxOptionGroup = aOptionGroups[ rGroupName ]; + if( !rxOptionGroup ) + { + /* If last control was an option button too, we have two + option groups following each other, so a dummy separator + control is needed. */ + if( bLastWasOptionButton ) + { + VbaFormControlVectorRef xDummyGroup = std::make_shared<VbaFormControlVector>(); + aControlGroups.push_back( xDummyGroup ); + OUString aName = aControlNames.generateDummyName(); + VbaFormControlRef xDummyControl = std::make_shared<VbaDummyFormControl>( aName ); + xDummyGroup->push_back( xDummyControl ); + } + rxOptionGroup = std::make_shared<VbaFormControlVector>(); + aControlGroups.push_back( rxOptionGroup ); + } + /* Append the option button to the control group (which is now + referred by the vector aControlGroups and by the map + aOptionGroups). */ + rxOptionGroup->push_back(control); + bLastWasOptionButton = true; + } + else + { + // open a new control group, if the last group is an option group + if( bLastWasOptionButton || aControlGroups.empty() ) + { + VbaFormControlVectorRef xControlGroup = std::make_shared<VbaFormControlVector>(); + aControlGroups.push_back( xControlGroup ); + } + // append the control to the last control group + VbaFormControlVector& rLastGroup = *aControlGroups.back(); + rLastGroup.push_back(control); + bLastWasOptionButton = false; + + // if control is a group box, move all its children to this control + if( pCtrlModel && (pCtrlModel->getControlType() == API_CONTROL_GROUPBOX) ) + { + /* Move all embedded controls of the group box relative to the + position of the group box. */ + control->moveEmbeddedToAbsoluteParent(); + /* Insert all children of the group box into the last control + group (following the group box). */ + rLastGroup.insert( rLastGroup.end(), control->maControls.begin(), control->maControls.end() ); + control->maControls.clear(); + // check if last control of the group box is an option button + bLastWasOptionButton = dynamic_cast< const AxOptionButtonModel* >( rLastGroup.back()->mxCtrlModel.get() ) != nullptr; + } + } + } + + // flatten the vector of vectors of form controls to a single vector + maControls.clear(); + for (auto const& controlGroup : aControlGroups) + maControls.insert( maControls.end(), controlGroup->begin(), controlGroup->end() ); +} + +void VbaFormControl::moveRelative( const AxPairData& rDistance ) +{ + if( mxSiteModel ) + mxSiteModel->moveRelative( rDistance ); +} + +void VbaFormControl::moveEmbeddedToAbsoluteParent() +{ + if( !mxSiteModel || maControls.empty() ) + return; + + // distance to move is equal to position of this control in its parent + AxPairData aDistance = mxSiteModel->getPosition(); + + /* For group boxes: add half of the font height to Y position (VBA + positions relative to frame border line, not to 'top' of frame). */ + const AxFontDataModel* pFontModel = dynamic_cast< const AxFontDataModel* >( mxCtrlModel.get() ); + if( pFontModel && (pFontModel->getControlType() == API_CONTROL_GROUPBOX) ) + { + sal_Int32 nFontHeight = convertPointToMm100(pFontModel->getFontHeight()); + aDistance.second += nFontHeight / 2; + } + + // move the embedded controls + maControls.forEachMem( &VbaFormControl::moveRelative, ::std::cref( aDistance ) ); +} + +bool VbaFormControl::compareByTabIndex( const VbaFormControlRef& rxLeft, const VbaFormControlRef& rxRight ) +{ + // sort controls without model to the end + sal_Int32 nLeftTabIndex = rxLeft->mxSiteModel ? rxLeft->mxSiteModel->getTabIndex() : SAL_MAX_INT32; + sal_Int32 nRightTabIndex = rxRight->mxSiteModel ? rxRight->mxSiteModel->getTabIndex() : SAL_MAX_INT32; + return nLeftTabIndex < nRightTabIndex; +} + +namespace { + +OUString lclGetQuotedString( const OUString& rCodeLine ) +{ + OUStringBuffer aBuffer; + sal_Int32 nLen = rCodeLine.getLength(); + if( (nLen > 0) && (rCodeLine[ 0 ] == '"') ) + { + bool bExitLoop = false; + for( sal_Int32 nIndex = 1; !bExitLoop && (nIndex < nLen); ++nIndex ) + { + sal_Unicode cChar = rCodeLine[ nIndex ]; + // exit on closing quote char (but check on double quote chars) + bExitLoop = (cChar == '"') && ((nIndex + 1 == nLen) || (rCodeLine[ nIndex + 1 ] != '"')); + if( !bExitLoop ) + { + aBuffer.append( cChar ); + // skip second quote char + if( cChar == '"' ) + ++nIndex; + } + } + } + return aBuffer.makeStringAndClear(); +} + +bool lclEatWhitespace( OUString& rCodeLine ) +{ + sal_Int32 nIndex = 0; + while( (nIndex < rCodeLine.getLength()) && ((rCodeLine[ nIndex ] == ' ') || (rCodeLine[ nIndex ] == '\t')) ) + ++nIndex; + if( nIndex > 0 ) + { + rCodeLine = rCodeLine.copy( nIndex ); + return true; + } + return false; +} + +bool lclEatKeyword( OUString& rCodeLine, const OUString& rKeyword ) +{ + if( rCodeLine.matchIgnoreAsciiCase( rKeyword ) ) + { + rCodeLine = rCodeLine.copy( rKeyword.getLength() ); + // success, if code line ends after keyword, or if whitespace follows + return rCodeLine.isEmpty() || lclEatWhitespace( rCodeLine ); + } + return false; +} + +} // namespace + +VbaUserForm::VbaUserForm( const Reference< XComponentContext >& rxContext, + const Reference< XModel >& rxDocModel, const GraphicHelper& rGraphicHelper, bool bDefaultColorBgr ) : + mxContext( rxContext ), + mxDocModel( rxDocModel ), + maConverter( rxDocModel, rGraphicHelper, bDefaultColorBgr ) +{ + OSL_ENSURE( mxContext.is(), "VbaUserForm::VbaUserForm - missing component context" ); + OSL_ENSURE( mxDocModel.is(), "VbaUserForm::VbaUserForm - missing document model" ); +} + +void VbaUserForm::importForm( const Reference< XNameContainer >& rxDialogLib, + StorageBase& rVbaFormStrg, const OUString& rModuleName, rtl_TextEncoding eTextEnc ) +{ + OSL_ENSURE( rxDialogLib.is(), "VbaUserForm::importForm - missing dialog library" ); + if( !mxContext.is() || !mxDocModel.is() || !rxDialogLib.is() ) + return; + + // check that the '03VBFrame' stream exists, this is required for forms + BinaryXInputStream aInStrm( rVbaFormStrg.openInputStream( "\003VBFrame" ), true ); + OSL_ENSURE( !aInStrm.isEof(), "VbaUserForm::importForm - missing \\003VBFrame stream" ); + if( aInStrm.isEof() ) + return; + + // scan for the line 'Begin {GUID} <FormName>' + TextInputStream aFrameTextStrm( mxContext, aInStrm, eTextEnc ); + static const OUStringLiteral aBegin = u"Begin"; + OUString aLine; + bool bBeginFound = false; + while( !bBeginFound && !aFrameTextStrm.isEof() ) + { + aLine = aFrameTextStrm.readLine().trim(); + bBeginFound = lclEatKeyword( aLine, aBegin ); + } + // check for the specific GUID that represents VBA forms + if( !bBeginFound || !lclEatKeyword( aLine, "{C62A69F0-16DC-11CE-9E98-00AA00574A4F}" ) ) + return; + + // remaining line is the form name + OUString aFormName = aLine.trim(); + OSL_ENSURE( !aFormName.isEmpty(), "VbaUserForm::importForm - missing form name" ); + OSL_ENSURE( rModuleName.equalsIgnoreAsciiCase( aFormName ), "VbaUserForm::importFrameStream - form and module name mismatch" ); + if( aFormName.isEmpty() ) + aFormName = rModuleName; + if( aFormName.isEmpty() ) + return; + mxSiteModel = std::make_shared<VbaSiteModel>(); + mxSiteModel->importProperty( XML_Name, aFormName ); + + // read the form properties (caption is contained in this '03VBFrame' stream, not in the 'f' stream) + mxCtrlModel = std::make_shared<AxUserFormModel>(); + OUString aKey, aValue; + bool bExitLoop = false; + while( !bExitLoop && !aFrameTextStrm.isEof() ) + { + aLine = aFrameTextStrm.readLine().trim(); + bExitLoop = aLine.equalsIgnoreAsciiCase( "End" ); + if( !bExitLoop && VbaHelper::extractKeyValue( aKey, aValue, aLine ) ) + { + if( aKey.equalsIgnoreAsciiCase( "Caption" ) ) + mxCtrlModel->importProperty( XML_Caption, lclGetQuotedString( aValue ) ); + else if( aKey.equalsIgnoreAsciiCase( "Tag" ) ) + mxSiteModel->importProperty( XML_Tag, lclGetQuotedString( aValue ) ); + } + } + + // use generic container control functionality to import the embedded controls + importStorage( rVbaFormStrg, AxClassTable() ); + + try + { + // create the dialog model + OUString aServiceName = mxCtrlModel->getServiceName(); + Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW ); + Reference< XControlModel > xDialogModel( xFactory->createInstance( aServiceName ), UNO_QUERY_THROW ); + Reference< XNameContainer > xDialogNC( xDialogModel, UNO_QUERY_THROW ); + + // convert properties and embedded controls + if( convertProperties( xDialogModel, maConverter, 0 ) ) + { + // export the dialog to XML and insert it into the dialog library + Reference< XInputStreamProvider > xDialogSource( ::xmlscript::exportDialogModel( xDialogNC, mxContext, mxDocModel ), UNO_SET_THROW ); + OSL_ENSURE( !rxDialogLib->hasByName( aFormName ), "VbaUserForm::importForm - multiple dialogs with equal name" ); + ContainerHelper::insertByName( rxDialogLib, aFormName, Any( xDialogSource ) ); + } + } + catch(const Exception& ) + { + } +} + +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/vbaexport.cxx b/oox/source/ole/vbaexport.cxx new file mode 100644 index 000000000..01eaff558 --- /dev/null +++ b/oox/source/ole/vbaexport.cxx @@ -0,0 +1,1193 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <cassert> +#include <random> +#include <string_view> + +#include <oox/ole/vbaexport.hxx> + +#include <tools/stream.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/vba/XVBAModuleInfo.hpp> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <ooo/vba/excel/XWorkbook.hpp> + +#include <oox/helper/propertyset.hxx> +#include <oox/token/properties.hxx> + +#include <sot/storage.hxx> + +#include <comphelper/xmltools.hxx> +#include <rtl/tencinfo.h> +#include <osl/thread.h> + +#define VBA_EXPORT_DEBUG 0 +#define VBA_USE_ORIGINAL_WM_STREAM 0 +#define VBA_USE_ORIGINAL_DIR_STREAM 0 +#define VBA_USE_ORIGINAL_PROJECT_STREAM 0 +#define VBA_USE_ORIGINAL_VBA_PROJECT 0 + +/* Enable to see VBA Encryption work. For now the input data and length values + * for encryption correspond to the case when the VBA macro is not protected. + */ +#define VBA_ENCRYPTION 1 + +namespace { + +void exportString(SvStream& rStrm, std::u16string_view rString, + const rtl_TextEncoding eTextEncoding) +{ + OString aStringCorrectCodepage = OUStringToOString(rString, eTextEncoding); + rStrm.WriteOString(aStringCorrectCodepage); +} + +void exportUTF16String(SvStream& rStrm, const OUString& rString) +{ + sal_Int32 n = rString.getLength(); + const sal_Unicode* pString = rString.getStr(); + for (sal_Int32 i = 0; i < n; ++i) + { + sal_Unicode character = pString[i]; + rStrm.WriteUnicode(character); + } +} + +bool isWorkbook(const css::uno::Reference<css::uno::XInterface>& xInterface) +{ + css::uno::Reference<ooo::vba::excel::XWorkbook> xWorkbook(xInterface, css::uno::UNO_QUERY); + return xWorkbook.is(); +} + +OUString createHexStringFromDigit(sal_uInt8 nDigit) +{ + OUString aString = OUString::number( nDigit, 16 ); + if(aString.getLength() == 1) + aString = OUString::number(0) + aString; + return aString.toAsciiUpperCase(); +} + +} + +VBACompressionChunk::VBACompressionChunk(SvStream& rCompressedStream, const sal_uInt8* pData, std::size_t nChunkSize) + : mrCompressedStream(rCompressedStream) + , mpUncompressedData(pData) + , mpCompressedChunkStream(nullptr) + , mnChunkSize(nChunkSize) + , mnCompressedCurrent(0) + , mnCompressedEnd(0) + , mnDecompressedCurrent(0) + , mnDecompressedEnd(0) +{ +} + +static void setUInt16(sal_uInt8* pBuffer, size_t nPos, sal_uInt16 nVal) +{ + pBuffer[nPos] = nVal & 0xFF; + pBuffer[nPos+1] = (nVal & 0xFF00) >> 8; +} + +sal_uInt16 VBACompressionChunk::handleHeader(bool bCompressed) +{ + // handle header bytes + size_t nSize = mnCompressedCurrent; + sal_uInt16 nHeader = 0; + PackCompressedChunkSize(nSize, nHeader); + PackCompressedChunkFlag(bCompressed, nHeader); + PackCompressedChunkSignature(nHeader); + + return nHeader; +} + +// section 2.4.1.3.7 +void VBACompressionChunk::write() +{ + + mnDecompressedCurrent = 0; + mnCompressedCurrent = 2; + mnCompressedEnd = 4098; + mnDecompressedEnd = std::min<sal_uInt64>(4096, mnChunkSize); + + // if that stream becomes larger than 4096 bytes then + // we use the uncompressed stream + sal_uInt8 pCompressedChunkStream[4098]; + mpCompressedChunkStream = pCompressedChunkStream; + + while (mnDecompressedCurrent < mnDecompressedEnd + && mnCompressedCurrent < mnCompressedEnd) + { + // compress token sequence + compressTokenSequence(); + } + + if (mnDecompressedCurrent < mnDecompressedEnd) + { + sal_uInt64 nChunkStart = mrCompressedStream.Tell(); + mrCompressedStream.WriteUInt16(0); + writeRawChunk(); + mrCompressedStream.Seek(nChunkStart); + sal_uInt16 nHeader = handleHeader(false); + mrCompressedStream.WriteUInt16(nHeader); + } + else + { + sal_uInt16 nHeader = handleHeader(true); + setUInt16(pCompressedChunkStream, 0, nHeader); + // copy the compressed stream to our output stream + mrCompressedStream.WriteBytes(pCompressedChunkStream, mnCompressedCurrent); + } +} + +// section 2.4.1.3.13 +void VBACompressionChunk::PackCompressedChunkSize(size_t nSize, sal_uInt16& rHeader) +{ + sal_uInt16 nTemp1 = rHeader & 0xF000; + sal_uInt16 nTemp2 = nSize - 3; + rHeader = nTemp1 | nTemp2; +} + +// section 2.4.1.3.16 +void VBACompressionChunk::PackCompressedChunkFlag(bool bCompressed, sal_uInt16& rHeader) +{ + sal_uInt16 nTemp1 = rHeader & 0x7FFF; + sal_uInt16 nTemp2 = static_cast<sal_uInt16>(bCompressed) << 15; + rHeader = nTemp1 | nTemp2; +} + +// section 2.4.1.3.14 +void VBACompressionChunk::PackCompressedChunkSignature(sal_uInt16& rHeader) +{ + sal_Int32 nTemp = rHeader & 0x8FFFF; + rHeader = nTemp | 0x3000; +} + +// section 2.4.1.3.8 +void VBACompressionChunk::compressTokenSequence() +{ + sal_uInt64 nFlagByteIndex = mnCompressedCurrent; + sal_uInt8 nFlagByte = 0; + ++mnCompressedCurrent; + for (size_t index = 0; index <= 7; ++index) + { + if (mnDecompressedCurrent < mnDecompressedEnd + && mnCompressedCurrent < mnCompressedEnd) + { + compressToken(index, nFlagByte); + } + } + mpCompressedChunkStream[nFlagByteIndex] = nFlagByte; +} + +// section 2.4.1.3.9 +void VBACompressionChunk::compressToken(size_t index, sal_uInt8& nFlagByte) +{ + size_t nLength = 0; + size_t nOffset = 0; + match(nLength, nOffset); + if (nOffset != 0) + { + if (mnCompressedCurrent + 1 < mnCompressedEnd) + { + sal_uInt16 nToken = CopyToken(nLength, nOffset); + setUInt16(mpCompressedChunkStream, mnCompressedCurrent, nToken); + SetFlagBit(index, true, nFlagByte); + mnCompressedCurrent += 2; + mnDecompressedCurrent += nLength; + } + else + { + mnCompressedCurrent = mnCompressedEnd; + } + } + else + { + if (mnCompressedCurrent + 1 < mnCompressedEnd) + { + mpCompressedChunkStream[mnCompressedCurrent] = mpUncompressedData[mnDecompressedCurrent]; + ++mnCompressedCurrent; + ++mnDecompressedCurrent; + } + else + { + mnCompressedCurrent = mnCompressedEnd; + } + } +} + +// section 2.4.1.3.18 +void VBACompressionChunk::SetFlagBit(size_t index, bool bVal, sal_uInt8& rFlag) +{ + size_t nTemp1 = static_cast<int>(bVal) << index; + sal_uInt8 nTemp2 = rFlag & (~nTemp1); + rFlag = nTemp2 | nTemp1; +} + +// section 2.4.1.3.19.3 +sal_uInt16 VBACompressionChunk::CopyToken(size_t nLength, size_t nOffset) +{ + sal_uInt16 nLengthMask = 0; + sal_uInt16 nOffsetMask = 0; + sal_uInt16 nBitCount = 0; + sal_uInt16 nMaxLength; + CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaxLength); + sal_uInt16 nTemp1 = nOffset -1; + sal_uInt16 nTemp2 = 16 - nBitCount; + sal_uInt16 nTemp3 = nLength - 3; + sal_uInt16 nToken = (nTemp1 << nTemp2) | nTemp3; + return nToken; +} + +// section 2.4.1.3.19.4 +void VBACompressionChunk::match(size_t& rLength, size_t& rOffset) +{ + size_t nBestLen = 0; + sal_Int32 nCandidate = mnDecompressedCurrent - 1; + sal_Int32 nBestCandidate = nCandidate; + while (nCandidate >= 0) + { + sal_Int32 nC = nCandidate; + sal_Int32 nD = mnDecompressedCurrent; + size_t nLen = 0; + while (nD < static_cast<sal_Int32>(mnChunkSize) // TODO: check if this needs to be including a minus -1 + && mpUncompressedData[nC] == mpUncompressedData[nD]) + { + ++nLen; + ++nC; + ++nD; + } + if (nLen > nBestLen) + { + nBestLen = nLen; + nBestCandidate = nCandidate; + } + --nCandidate; + } + + if (nBestLen >= 3) + { + sal_uInt16 nMaximumLength = 0; + sal_uInt16 nLengthMask, nOffsetMask, nBitCount; + CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaximumLength); + rLength = std::min<sal_uInt16>(nMaximumLength, nBestLen); + rOffset = mnDecompressedCurrent - nBestCandidate; + } + else + { + rLength = 0; + rOffset = 0; + } +} + +// section 2.4.1.3.19.1 +void VBACompressionChunk::CopyTokenHelp(sal_uInt16& rLengthMask, sal_uInt16& rOffsetMask, + sal_uInt16& rBitCount, sal_uInt16& rMaximumLength) +{ + sal_uInt16 nDifference = mnDecompressedCurrent; + assert(nDifference <= 4096); + assert(nDifference >= 1); + if (nDifference >= 2049) + rBitCount = 12; + else if (nDifference >= 1025) + rBitCount = 11; + else if (nDifference >= 513) + rBitCount = 10; + else if (nDifference >= 257) + rBitCount = 9; + else if (nDifference >= 129) + rBitCount = 8; + else if (nDifference >= 65) + rBitCount = 7; + else if (nDifference >= 33) + rBitCount = 6; + else if (nDifference >= 17) + rBitCount = 5; + else + rBitCount = 4; + rLengthMask = 0xffff >> rBitCount; + rOffsetMask = ~rLengthMask; + rMaximumLength = rLengthMask + 3; +} + +// section 2.4.1.3.10 +void VBACompressionChunk::writeRawChunk() +{ + // we need to use up to 4096 bytes of the original stream + // and fill the rest with padding + mrCompressedStream.WriteBytes(mpUncompressedData, mnChunkSize); + std::size_t nPadding = 4096 - mnChunkSize; + for (size_t i = 0; i < nPadding; ++i) + { + mrCompressedStream.WriteUInt8(0); + } +} + +VBACompression::VBACompression(SvStream& rCompressedStream, + SvMemoryStream& rUncompressedStream): + mrCompressedStream(rCompressedStream), + mrUncompressedStream(rUncompressedStream) +{ +} + +// section 2.4.1.3.6 +void VBACompression::write() +{ + // section 2.4.1.1.1 + mrCompressedStream.WriteUInt8(0x01); // signature byte of a compressed container + bool bStreamNotEnded = true; + const sal_uInt8* pData = static_cast<const sal_uInt8*>(mrUncompressedStream.GetData()); + std::size_t nSize = mrUncompressedStream.GetEndOfData(); + std::size_t nRemainingSize = nSize; + while(bStreamNotEnded) + { + std::size_t nChunkSize = std::min<size_t>(nRemainingSize, 4096); + VBACompressionChunk aChunk(mrCompressedStream, &pData[nSize - nRemainingSize], nChunkSize); + aChunk.write(); + + // update the uncompressed chunk start marker + nRemainingSize -= nChunkSize; + bStreamNotEnded = nRemainingSize != 0; + } +} + +// section 2.4.3 +#if VBA_ENCRYPTION + +VBAEncryption::VBAEncryption(const sal_uInt8* pData, const sal_uInt16 length, + SvStream& rEncryptedData, sal_uInt8 nProjKey, + const rtl_TextEncoding eTextEncoding) + :mpData(pData) + ,mnLength(length) + ,mrEncryptedData(rEncryptedData) + ,mnUnencryptedByte1(0) + ,mnEncryptedByte1(0) + ,mnEncryptedByte2(0) + ,mnProjKey(nProjKey) + ,mnIgnoredLength(0) + ,mnSeed(0x00) + ,mnVersionEnc(0) + ,meTextEncoding(eTextEncoding) +{ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 255); + mnSeed = dis(gen); +} + +void VBAEncryption::writeSeed() +{ + exportString(mrEncryptedData, createHexStringFromDigit(mnSeed), meTextEncoding); +} + +void VBAEncryption::writeVersionEnc() +{ + static const sal_uInt8 mnVersion = 2; // the encrypted version + mnVersionEnc = mnSeed ^ mnVersion; + exportString(mrEncryptedData, createHexStringFromDigit(mnVersionEnc), meTextEncoding); +} + +sal_uInt8 VBAEncryption::calculateProjKey(const OUString& rProjectKey) +{ + sal_uInt8 nProjKey = 0; + sal_Int32 n = rProjectKey.getLength(); + const sal_Unicode* pString = rProjectKey.getStr(); + for (sal_Int32 i = 0; i < n; ++i) + { + sal_Unicode character = pString[i]; + nProjKey += character; + } + + return nProjKey; +} + +void VBAEncryption::writeProjKeyEnc() +{ + sal_uInt8 nProjKeyEnc = mnSeed ^ mnProjKey; + exportString(mrEncryptedData, createHexStringFromDigit(nProjKeyEnc), meTextEncoding); + mnUnencryptedByte1 = mnProjKey; + mnEncryptedByte1 = nProjKeyEnc; // ProjKeyEnc + mnEncryptedByte2 = mnVersionEnc; // VersionEnc +} + +void VBAEncryption::writeIgnoredEnc() +{ + mnIgnoredLength = (mnSeed & 6) / 2; + for(sal_Int32 i = 1; i <= mnIgnoredLength; ++i) + { + sal_uInt8 nTempValue = 0xBE; // Any value can be assigned here + sal_uInt8 nByteEnc = nTempValue ^ (mnEncryptedByte2 + mnUnencryptedByte1); + exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc), meTextEncoding); + mnEncryptedByte2 = mnEncryptedByte1; + mnEncryptedByte1 = nByteEnc; + mnUnencryptedByte1 = nTempValue; + } +} + +void VBAEncryption::writeDataLengthEnc() +{ + sal_uInt16 temp = mnLength; + for(sal_Int8 i = 0; i < 4; ++i) + { + sal_uInt8 nByte = temp & 0xFF; + sal_uInt8 nByteEnc = nByte ^ (mnEncryptedByte2 + mnUnencryptedByte1); + exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc), meTextEncoding); + mnEncryptedByte2 = mnEncryptedByte1; + mnEncryptedByte1 = nByteEnc; + mnUnencryptedByte1 = nByte; + temp >>= 8; + } +} + +void VBAEncryption::writeDataEnc() +{ + for(sal_Int16 i = 0; i < mnLength; i++) + { + sal_uInt8 nByteEnc = mpData[i] ^ (mnEncryptedByte2 + mnUnencryptedByte1); + exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc), meTextEncoding); + mnEncryptedByte2 = mnEncryptedByte1; + mnEncryptedByte1 = nByteEnc; + mnUnencryptedByte1 = mpData[i]; + } +} + +void VBAEncryption::write() +{ + writeSeed(); + writeVersionEnc(); + writeProjKeyEnc(); + writeIgnoredEnc(); + writeDataLengthEnc(); + writeDataEnc(); +} + +#endif + +VbaExport::VbaExport(css::uno::Reference<css::frame::XModel> const & xModel): + mxModel(xModel) +{ +} + +namespace { + +// section 2.3.4.2.1.1 +void writePROJECTSYSKIND(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0001); // id + rStrm.WriteUInt32(0x00000004); // size + rStrm.WriteUInt32(0x00000001); // SysKind, hard coded to 32-bin windows for now +} + +// section 2.3.4.2.1.2 +void writePROJECTLCID(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0002); // id + rStrm.WriteUInt32(0x00000004); // size + rStrm.WriteUInt32(0x00000409); // Lcid +} + +// section 2.3.4.2.1.3 +void writePROJECTLCIDINVOKE(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0014); // id + rStrm.WriteUInt32(0x00000004); // size + rStrm.WriteUInt32(0x00000409); // LcidInvoke +} + +// section 2.3.4.2.1.4 +void writePROJECTCODEPAGE(SvStream& rStrm, const rtl_TextEncoding eTextEncoding) +{ + rStrm.WriteUInt16(0x0003); // id + rStrm.WriteUInt32(0x00000002); // size + rStrm.WriteUInt16(rtl_getWindowsCodePageFromTextEncoding(eTextEncoding)); // CodePage +} + +//section 2.3.4.2.1.5 +void writePROJECTNAME(SvStream& rStrm, const OUString& name, const rtl_TextEncoding eTextEncoding) +{ + rStrm.WriteUInt16(0x0004); // id + sal_uInt32 sizeOfProjectName = name.getLength(); + rStrm.WriteUInt32(sizeOfProjectName); // sizeOfProjectName + exportString(rStrm, name, eTextEncoding); // ProjectName +} + +//section 2.3.4.2.1.6 +void writePROJECTDOCSTRING(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0005); // id + rStrm.WriteUInt32(0x00000000); // sizeOfDocString + rStrm.WriteUInt16(0x0040); // Reserved + rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode, MUST be even +} + +//section 2.3.4.2.1.7 +void writePROJECTHELPFILEPATH(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0006); // id + rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile1 + rStrm.WriteUInt16(0x003D); // Reserved + rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile2 +} + +//section 2.3.4.2.1.8 +void writePROJECTHELPCONTEXT(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0007); // id + rStrm.WriteUInt32(0x00000004); // size + rStrm.WriteUInt32(0x00000000); // HelpContext +} + +//section 2.3.4.2.1.9 +void writePROJECTLIBFLAGS(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0008); // id + rStrm.WriteUInt32(0x00000004); // size + rStrm.WriteUInt32(0x00000000); // ProjectLibFlags +} + +//section 2.3.4.2.1.10 +void writePROJECTVERSION(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0009); // id + rStrm.WriteUInt32(0x00000004); // Reserved + rStrm.WriteUInt32(1467127224); // VersionMajor // TODO: where is this magic number coming from + rStrm.WriteUInt16(5); // VersionMinor // TODO: where is this magic number coming from +} + +//section 2.3.4.2.1.11 +void writePROJECTCONSTANTS(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x000C); // id + rStrm.WriteUInt32(0x00000000); // sizeOfConstants + rStrm.WriteUInt16(0x003C); // Reserved + rStrm.WriteUInt32(0x00000000); // sizeOfConstantsUnicode +} + +// section 2.3.4.2.1 +void writePROJECTINFORMATION(SvStream& rStrm, const OUString& projectName, + const rtl_TextEncoding eTextEncoding) +{ + writePROJECTSYSKIND(rStrm); + writePROJECTLCID(rStrm); + writePROJECTLCIDINVOKE(rStrm); + writePROJECTCODEPAGE(rStrm, eTextEncoding); + writePROJECTNAME(rStrm, projectName, eTextEncoding); + writePROJECTDOCSTRING(rStrm); + writePROJECTHELPFILEPATH(rStrm); + writePROJECTHELPCONTEXT(rStrm); + writePROJECTLIBFLAGS(rStrm); + writePROJECTVERSION(rStrm); + writePROJECTCONSTANTS(rStrm); +} + +// section 2.3.4.2.2.2 +void writeREFERENCENAME(SvStream& rStrm, const OUString& name, const rtl_TextEncoding eTextEncoding) +{ + rStrm.WriteUInt16(0x0016); // id + sal_Int32 size = name.getLength(); + rStrm.WriteUInt32(size); // sizeOfName + exportString(rStrm, name, eTextEncoding); // name + rStrm.WriteUInt16(0x003E); // reserved + sal_Int32 unicodesize = size * 2; + rStrm.WriteUInt32(unicodesize); // sizeOfNameUnicode + exportUTF16String(rStrm, name); // nameUnicode +} + +// section 2.3.4.2.2.5 +void writeREFERENCEREGISTERED(SvStream& rStrm, const OUString& libid, + const rtl_TextEncoding eTextEncoding) +{ + rStrm.WriteUInt16(0x000D); // id + sal_Int32 sizeOfLibid = libid.getLength(); + sal_Int32 size = sizeOfLibid + 10; // size of Libid, sizeOfLibid(4 bytes), reserved 1(4 bytes) and reserved 2(2 bytes) + rStrm.WriteUInt32(size); // size + rStrm.WriteUInt32(sizeOfLibid); // sizeOfLibid + exportString(rStrm, libid, eTextEncoding); // Libid + rStrm.WriteUInt32(0x00000000); // reserved 1 + rStrm.WriteUInt16(0x0000); // reserved 2 +} + +// section 2.3.4.2.2.1 +void writeREFERENCE(SvStream& rStrm, const OUString& name, const OUString& libid, + const rtl_TextEncoding eTextEncoding) +{ + writeREFERENCENAME(rStrm, name, eTextEncoding); + writeREFERENCEREGISTERED(rStrm, libid, eTextEncoding); +} + +// section 2.3.4.2.2 +void writePROJECTREFERENCES(SvStream& rStrm, const rtl_TextEncoding eTextEncoding) +{ + // TODO: find out where these references are coming from + writeREFERENCE(rStrm, "stdole", "*\\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\\Windows\\SysWOW64\\stdole2.tlb#OLE Automation", eTextEncoding); + writeREFERENCE(rStrm, "Office", "*\\G{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}#2.0#0#C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\OFFICE14\\MSO.DLL#Microsoft Office 14.0 Object Library", eTextEncoding); +} + +// section 2.3.4.2.3.1 +void writePROJECTCOOKIE(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0013); // id + rStrm.WriteUInt32(0x00000002); // size + rStrm.WriteUInt16(0xFFFF); // cookie +} + +// section 2.3.4.2.3.2.1 +void writeMODULENAME(SvStream& rStrm, const OUString& name, const rtl_TextEncoding eTextEncoding) +{ + rStrm.WriteUInt16(0x0019); // id + sal_Int32 n = name.getLength(); // sizeOfModuleName + rStrm.WriteUInt32(n); + exportString(rStrm, name, eTextEncoding); // ModuleName +} + +// section 2.3.4.2.3.2.2 +void writeMODULENAMEUNICODE(SvStream& rStrm, const OUString& name) +{ + rStrm.WriteUInt16(0x0047); // id + sal_Int32 n = name.getLength() * 2; // sizeOfModuleNameUnicode // TODO: better calculation for unicode string length + rStrm.WriteUInt32(n); + exportUTF16String(rStrm, name); // ModuleNameUnicode +} + +// section 2.3.4.2.3.2.3 +void writeMODULESTREAMNAME(SvStream& rStrm, const OUString& streamName, + const rtl_TextEncoding eTextEncoding) +{ + rStrm.WriteUInt16(0x001A); // id + sal_Int32 n = streamName.getLength(); // sizeOfStreamName + rStrm.WriteUInt32(n); + exportString(rStrm, streamName, eTextEncoding); // StreamName + rStrm.WriteUInt16(0x0032); // reserved + rStrm.WriteUInt32(n * 2); // sizeOfStreamNameUnicode // TODO: better calculation for unicode string length + exportUTF16String(rStrm, streamName); // StreamNameUnicode +} + +// section 2.3.4.2.3.2.4 +void writeMODULEDOCSTRING(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x001C); // id + rStrm.WriteUInt32(0x00000000); // sizeOfDocString + rStrm.WriteUInt16(0x0048); // reserved + rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode +} + +// section 2.3.4.2.3.2.5 +void writeMODULEOFFSET(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x0031); // id + rStrm.WriteUInt32(0x00000004); // sizeOfTextOffset + rStrm.WriteUInt32(0x00000000); // TextOffset +} + +// section 2.3.4.2.3.2.6 +void writeMODULEHELPCONTEXT(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x001E); // id + rStrm.WriteUInt32(0x00000004); // sizeOfHelpContext + rStrm.WriteUInt32(0x00000000); // HelpContext +} + +// section 2.3.4.2.3.2.7 +void writeMODULECOOKIE(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x002C); // id + rStrm.WriteUInt32(0x00000002); // sizeOfHelpContext + rStrm.WriteUInt16(0xFFFF); // HelpContext +} + +// section 2.3.4.2.3.2.8 +void writeMODULETYPE(SvStream& rStrm, const sal_uInt16 type) +{ + if(type == 1) + rStrm.WriteUInt16(0x0021); // id for a procedural module + else + rStrm.WriteUInt16(0x0022); // id for document, class or design module + rStrm.WriteUInt32(0x00000000); // reserved +} + +// section 2.3.4.2.3.2 +void writePROJECTMODULE(SvStream& rStrm, const OUString& name, const sal_uInt16 type, + const rtl_TextEncoding eTextEncoding) +{ + writeMODULENAME(rStrm, name, eTextEncoding); + writeMODULENAMEUNICODE(rStrm, name); + writeMODULESTREAMNAME(rStrm, name, eTextEncoding); + writeMODULEDOCSTRING(rStrm); + writeMODULEOFFSET(rStrm); + writeMODULEHELPCONTEXT(rStrm); + writeMODULECOOKIE(rStrm); + writeMODULETYPE(rStrm, type); + rStrm.WriteUInt16(0x002B); // terminator + rStrm.WriteUInt32(0x00000000); // reserved +} + +// section 2.3.4.2.3 +void writePROJECTMODULES(SvStream& rStrm, + const css::uno::Reference<css::container::XNameContainer>& xNameContainer, + const std::vector<sal_Int32>& rLibraryMap, + const rtl_TextEncoding eTextEncoding) +{ + const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames(); + sal_Int32 n = aElementNames.getLength(); + css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY); + assert(xModuleInfo.is()); + + // TODO: this whole part is document specific + rStrm.WriteUInt16(0x000F); // id + rStrm.WriteUInt32(0x00000002); // size of Count + sal_Int16 count = n; // Number of modules // TODO: this is dependent on the document + rStrm.WriteUInt16(count); // Count + writePROJECTCOOKIE(rStrm); + + for (sal_Int32 i = 0; i < n; ++i) + { + const OUString& rModuleName = aElementNames[rLibraryMap[i]]; + css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); + writePROJECTMODULE(rStrm, rModuleName, aModuleInfo.ModuleType, eTextEncoding); + } +} + +// section 2.3.4.2 +void exportDirStream(SvStream& rStrm, + const css::uno::Reference<css::container::XNameContainer>& xNameContainer, + const std::vector<sal_Int32>& rLibraryMap, const OUString& projectName, + const rtl_TextEncoding eTextEncoding) +{ + SvMemoryStream aDirStream(4096, 4096); + + writePROJECTINFORMATION(aDirStream, projectName, eTextEncoding); + writePROJECTREFERENCES(aDirStream, eTextEncoding); + writePROJECTMODULES(aDirStream, xNameContainer, rLibraryMap, eTextEncoding); + aDirStream.WriteUInt16(0x0010); // terminator + aDirStream.WriteUInt32(0x00000000); // reserved + +#if VBA_EXPORT_DEBUG + static const OUStringLiteral aDirFileName(u"/tmp/vba_dir_out.bin"); + SvFileStream aDirStreamDebug(aDirFileName, StreamMode::READWRITE); + aDirStream.Seek(0); + aDirStreamDebug.WriteStream(aDirStream); +#endif + + VBACompression aCompression(rStrm, aDirStream); + aCompression.write(); +} + +// section 2.3.4.3 Module Stream +void exportModuleStream(SvStream& rStrm, const OUString& rSourceCode, const OUString& aElementName, + css::script::ModuleInfo const& rInfo, const rtl_TextEncoding eTextEncoding) +{ + SvMemoryStream aModuleStream(4096, 4096); + + exportString(aModuleStream, OUStringConcatenation("Attribute VB_Name = \"" + aElementName + "\"\r\n"), eTextEncoding); + if (rInfo.ModuleType == 4) + { + if (isWorkbook(rInfo.ModuleObject)) + exportString(aModuleStream, u"Attribute VB_Base = \"0{00020819-0000-0000-C000-000000000046}\"\r\n", eTextEncoding); + else + exportString(aModuleStream, u"Attribute VB_Base = \"0{00020820-0000-0000-C000-000000000046}\"\r\n", eTextEncoding); + + exportString(aModuleStream, u"Attribute VB_GlobalNameSpace = False\r\n", eTextEncoding); + exportString(aModuleStream, u"Attribute VB_Creatable = False\r\n", eTextEncoding); + exportString(aModuleStream, u"Attribute VB_PredeclaredId = True\r\n", eTextEncoding); + exportString(aModuleStream, u"Attribute VB_Exposed = True\r\n", eTextEncoding); + exportString(aModuleStream, u"Attribute VB_TemplateDerived = False\r\n", eTextEncoding); + exportString(aModuleStream, u"Attribute VB_Customizable = True\r\n", eTextEncoding); + } + OUString aSourceCode = rSourceCode.replaceFirst("Option VBASupport 1\n", ""); + const sal_Int32 nPos = aSourceCode.indexOf("Rem Attribute VBA_ModuleType="); + const sal_Int32 nEndPos = nPos != -1 ? aSourceCode.indexOf("\n", nPos) : -1; + if (nPos != -1 && nEndPos != -1) + aSourceCode = aSourceCode.replaceAt(nPos, nEndPos - nPos+1, u""); + aSourceCode = aSourceCode.replaceAll("\n", "\r\n"); + exportString(aModuleStream, aSourceCode, eTextEncoding); + +#if VBA_EXPORT_DEBUG + OUString aModuleFileName("/tmp/vba_" + aElementName + "_out.bin"); + SvFileStream aModuleStreamDebug(aModuleFileName, StreamMode::READWRITE); + aModuleStream.Seek(0); + aModuleStreamDebug.WriteStream(aModuleStream); +#endif + + VBACompression aCompression(rStrm, aModuleStream); + aCompression.write(); +} + +// section 2.3.4.1 _VBA_PROJECT Stream +void exportVBAProjectStream(SvStream& rStrm) +{ + rStrm.WriteUInt16(0x61CC); // Reserved1 + rStrm.WriteUInt16(0xFFFF); // Version + rStrm.WriteUInt8(0x00); // Reserved2 + rStrm.WriteUInt16(0x0000); // Undefined +} + +// section 2.3.1 PROJECT Stream +void exportPROJECTStream(SvStream& rStrm, + const css::uno::Reference<css::container::XNameContainer>& xNameContainer, + const OUString& projectName, const std::vector<sal_Int32>& rLibraryMap, + const rtl_TextEncoding eTextEncoding) +{ + const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames(); + sal_Int32 n = aElementNames.getLength(); + css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY); + assert(xModuleInfo.is()); + + // section 2.3.1.1ProjectProperties + + // section 2.3.1.2 ProjectId + exportString(rStrm, u"ID=\"", eTextEncoding); + OUString aProjectID + = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8); + exportString(rStrm, aProjectID, eTextEncoding); + exportString(rStrm, u"\"\r\n", eTextEncoding); + + // section 2.3.1.3 ProjectModule + for (sal_Int32 i = 0; i < n; ++i) + { + const OUString& rModuleName = aElementNames[rLibraryMap[i]]; + css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); + if(aModuleInfo.ModuleType == 1) + { + exportString(rStrm, OUStringConcatenation("Module=" + rModuleName + "\r\n"), + eTextEncoding); + } + else if(aModuleInfo.ModuleType == 4) + { + exportString(rStrm, + OUStringConcatenation("Document=" + rModuleName + "/&H00000000\r\n"), + eTextEncoding); + } + } + + // section 2.3.1.11 ProjectName + exportString(rStrm, OUStringConcatenation("Name=\"" + projectName + "\"\r\n"), eTextEncoding); + + // section 2.3.1.12 ProjectHelpId + exportString(rStrm, u"HelpContextID=\"0\"\r\n", eTextEncoding); + + // section 2.3.1.14 ProjectVersionCompat32 + exportString(rStrm, u"VersionCompatible32=\"393222000\"\r\n", eTextEncoding); + + // section 2.3.1.15 ProjectProtectionState +#if VBA_ENCRYPTION + exportString(rStrm, u"CMG=\"", eTextEncoding); + SvMemoryStream aProtectedStream(4096, 4096); + aProtectedStream.WriteUInt32(0x00000000); + const sal_uInt8* pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData()); + sal_uInt8 nProjKey = VBAEncryption::calculateProjKey(aProjectID); + VBAEncryption aProtectionState(pData, 4, rStrm, nProjKey, eTextEncoding); + aProtectionState.write(); + exportString(rStrm, u"\"\r\n", eTextEncoding); +#else + exportString(rStrm, "CMG=\"BEBC9256EEAAA8AEA8AEA8AEA8AE\"\r\n", eTextEncoding); +#endif + + // section 2.3.1.16 ProjectPassword +#if VBA_ENCRYPTION + exportString(rStrm, u"DPB=\"", eTextEncoding); + aProtectedStream.Seek(0); + aProtectedStream.WriteUInt8(0x00); + pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData()); + VBAEncryption aProjectPassword(pData, 1, rStrm, nProjKey, eTextEncoding); + aProjectPassword.write(); + exportString(rStrm, u"\"\r\n", eTextEncoding); +#else + exportString(rStrm, "DPB=\"7C7E5014B0D3B1D3B1D3\"\r\n", eTextEncoding); +#endif + + // section 2.3.1.17 ProjectVisibilityState +#if VBA_ENCRYPTION + exportString(rStrm, u"GC=\"", eTextEncoding); + aProtectedStream.Seek(0); + aProtectedStream.WriteUInt8(0xFF); + pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData()); + VBAEncryption aVisibilityState(pData, 1, rStrm, nProjKey, eTextEncoding); + aVisibilityState.write(); + exportString(rStrm, u"\"\r\n\r\n", eTextEncoding); +#else + exportString(rStrm, "GC=\"3A3816DAD5DBD5DB2A\"\r\n\r\n", eTextEncoding); +#endif + + // section 2.3.1.18 HostExtenders + exportString(rStrm, + u"[Host Extender Info]\r\n" + "&H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000\r\n\r\n", + eTextEncoding); + + // section 2.3.1.19 ProjectWorkspace + exportString(rStrm, u"[Workspace]\r\n", eTextEncoding); + for (sal_Int32 i = 0; i < n; ++i) + { + const OUString& rModuleName = aElementNames[rLibraryMap[i]]; + css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); + if(aModuleInfo.ModuleType == 1) + { + exportString(rStrm, OUStringConcatenation(rModuleName + "=25, 25, 1439, 639, \r\n"), + eTextEncoding); + } + else + { + exportString(rStrm, OUStringConcatenation(rModuleName + "=0, 0, 0, 0, C\r\n"), + eTextEncoding); + } + } +} + +// section 2.3.3.1 NAMEMAP +void writeNAMEMAP(SvStream& rStrm, const css::uno::Sequence<OUString>& rElementNames, + const std::vector<sal_Int32>& rLibraryMap, const rtl_TextEncoding eTextEncoding) +{ + int n = rElementNames.getLength(); + for(sal_Int32 i = 0; i < n; ++i) + { + const OUString& rModuleName = rElementNames[rLibraryMap[i]]; + exportString(rStrm, rModuleName, eTextEncoding); + rStrm.WriteUInt8(0x00); // terminator + exportUTF16String(rStrm, rModuleName); + rStrm.WriteUInt16(0x0000); // terminator + } +} + +// section 2.3.3 PROJECTwm Stream +void exportPROJECTwmStream(SvStream& rStrm, const css::uno::Sequence<OUString>& rElementNames, + const std::vector<sal_Int32>& rLibraryMap, const rtl_TextEncoding eTextEncoding) +{ + writeNAMEMAP(rStrm, rElementNames, rLibraryMap, eTextEncoding); + rStrm.WriteUInt16(0x0000); // terminator +} + +void getCorrectExportOrder(const css::uno::Reference<css::container::XNameContainer>& xNameContainer, std::vector<sal_Int32>& rLibraryMap) +{ + const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames(); + sal_Int32 n = aElementNames.getLength(); + css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY); + + sal_Int32 nCurrentId = 0; + // first all the non-document modules + for (sal_Int32 i = 0; i < n; ++i) + { + css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]); + if (aModuleInfo.ModuleType != 4) + { + rLibraryMap[nCurrentId] = i; + ++nCurrentId; + } + } + + sal_Int32 nWorkbookIndex = -1; + // then possibly the workbook module + for (sal_Int32 i = 0; i < n; ++i) + { + css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]); + bool bWorkbook = isWorkbook(aModuleInfo.ModuleObject); + if (bWorkbook) + { + nWorkbookIndex = i; + rLibraryMap[nCurrentId] = i; + ++nCurrentId; + } + } + + // then the remaining modules + for (sal_Int32 i = 0; i < n; ++i) + { + if (i == nWorkbookIndex) + continue; + + css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]); + if (aModuleInfo.ModuleType == 4) + { + rLibraryMap[nCurrentId] = i; + ++nCurrentId; + } + } +} + +} + +#if VBA_USE_ORIGINAL_WM_STREAM || VBA_USE_ORIGINAL_DIR_STREAM \ + || VBA_USE_ORIGINAL_PROJECT_STREAM || VBA_USE_ORIGINAL_VBA_PROJECT \ + || VBA_USE_ORIGINAL_DIR_STREAM +void addFileStreamToSotStream(const OUString& rPath, SotStorageStream& rStream) +{ + SvFileStream aFileStream(rPath, StreamMode::READWRITE); + rStream.WriteStream(aFileStream); +} +#endif + +void VbaExport::exportVBA(SotStorage* pRootStorage) +{ + css::uno::Reference<css::container::XNameContainer> xNameContainer = getBasicLibrary(); + if (!xNameContainer.is()) { + return; + } + const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames(); + sal_Int32 n = aElementNames.getLength(); // get the number of modules + // export the elements in the order MSO expects them + // we store the index of the + std::vector<sal_Int32> aLibraryMap(n, 0); + getCorrectExportOrder(xNameContainer, aLibraryMap); + + // start here with the VBA export + tools::SvRef<SotStorage> xVBAStream = pRootStorage->OpenSotStorage("VBA", StreamMode::READWRITE); + tools::SvRef<SotStorageStream> pDirStream = xVBAStream->OpenSotStream("dir", StreamMode::READWRITE); + + tools::SvRef<SotStorageStream> pVBAProjectStream = xVBAStream->OpenSotStream("_VBA_PROJECT", StreamMode::READWRITE); + tools::SvRef<SotStorageStream> pPROJECTStream = pRootStorage->OpenSotStream("PROJECT", StreamMode::READWRITE); + tools::SvRef<SotStorageStream> pPROJECTwmStream = pRootStorage->OpenSotStream("PROJECTwm", StreamMode::READWRITE); + + const rtl_TextEncoding eTextEncoding = getVBATextEncoding(); + +#if VBA_USE_ORIGINAL_WM_STREAM + OUString aProjectwmPath = "/home/moggi/Documents/testfiles/vba/PROJECTwm"; + addFileStreamToSotStream(aProjectwmPath, *pPROJECTwmStream); +#else + exportPROJECTwmStream(*pPROJECTwmStream, aElementNames, aLibraryMap, eTextEncoding); +#endif + +#if VBA_USE_ORIGINAL_DIR_STREAM + OUString aDirPath = "/home/moggi/Documents/testfiles/vba/VBA/dir"; + addFileStreamToSotStream(aDirPath, *pDirStream); +#else + exportDirStream(*pDirStream, xNameContainer, aLibraryMap, getProjectName(), eTextEncoding); +#endif + +#if VBA_USE_ORIGINAL_PROJECT_STREAM + OUString aProjectPath = "/home/moggi/Documents/testfiles/vba/PROJECT"; + addFileStreamToSotStream(aProjectPath, *pPROJECTStream); +#else + exportPROJECTStream(*pPROJECTStream, xNameContainer, getProjectName(), aLibraryMap, + eTextEncoding); +#endif + +#if VBA_USE_ORIGINAL_VBA_PROJECT + OUString a_VBA_ProjectPath = "/home/moggi/Documents/testfiles/vba/VBA/_VBA_PROJECT"; + addFileStreamToSotStream(a_VBA_ProjectPath, *pVBAProjectStream); +#else + exportVBAProjectStream(*pVBAProjectStream); +#endif + +#if VBA_USE_ORIGINAL_DIR_STREAM + OUString aModule1Path = "/home/moggi/Documents/testfiles/vba/VBA/Module1"; + OUString aSheet1Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet1"; + OUString aSheet2Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet2"; + OUString aSheet3Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet3"; + OUString aWorkbookPath = "/home/moggi/Documents/testfiles/vba/VBA/ThisWorkbook"; + tools::SvRef<SotStorageStream> pModule1Stream = xVBAStream->OpenSotStream("Module1", StreamMode::READWRITE); + tools::SvRef<SotStorageStream> pSheet1Stream = xVBAStream->OpenSotStream("Sheet1", StreamMode::READWRITE); + tools::SvRef<SotStorageStream> pSheet2Stream = xVBAStream->OpenSotStream("Sheet2", StreamMode::READWRITE); + tools::SvRef<SotStorageStream> pSheet3Stream = xVBAStream->OpenSotStream("Sheet3", StreamMode::READWRITE); + tools::SvRef<SotStorageStream> pWorkbookStream = xVBAStream->OpenSotStream("ThisWorkbook", StreamMode::READWRITE); + addFileStreamToSotStream(aModule1Path, *pModule1Stream); + addFileStreamToSotStream(aSheet1Path, *pSheet1Stream); + addFileStreamToSotStream(aSheet2Path, *pSheet2Stream); + addFileStreamToSotStream(aSheet3Path, *pSheet3Stream); + addFileStreamToSotStream(aWorkbookPath, *pWorkbookStream); + + pModule1Stream->Commit(); + pSheet1Stream->Commit(); + pSheet2Stream->Commit(); + pSheet3Stream->Commit(); + pWorkbookStream->Commit(); +#else + + css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY); + for (sal_Int32 i = 0; i < n; ++i) + { + const OUString& rModuleName = aElementNames[aLibraryMap[i]]; + tools::SvRef<SotStorageStream> pModuleStream = xVBAStream->OpenSotStream(rModuleName, StreamMode::READWRITE); + css::uno::Any aCode = xNameContainer->getByName(rModuleName); + css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); + OUString aSourceCode; + aCode >>= aSourceCode; + exportModuleStream(*pModuleStream, aSourceCode, rModuleName, aModuleInfo, eTextEncoding); + pModuleStream->Commit(); + } + +#endif + + pVBAProjectStream->Commit(); + + pDirStream->Commit(); + xVBAStream->Commit(); + pPROJECTStream->Commit(); + pPROJECTwmStream->Commit(); + pRootStorage->Commit(); +} + +css::uno::Reference<css::script::XLibraryContainer> VbaExport::getLibraryContainer() const +{ + oox::PropertySet aDocProp(mxModel); + css::uno::Reference<css::script::XLibraryContainer> xLibContainer(aDocProp.getAnyProperty(oox::PROP_BasicLibraries), css::uno::UNO_QUERY); + + return xLibContainer; +} + +css::uno::Reference<css::container::XNameContainer> VbaExport::getBasicLibrary() const +{ + css::uno::Reference<css::container::XNameContainer> xLibrary; + try + { + css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer(); + OUString aProjectName = getProjectName(); + xLibrary.set( xLibContainer->getByName(aProjectName), css::uno::UNO_QUERY_THROW ); + } + catch(...) + { + } + + return xLibrary; +} + +bool VbaExport::containsVBAProject() +{ + css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer(); + if (!xLibContainer.is()) + return false; + + css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility (xLibContainer, css::uno::UNO_QUERY); + if (!xVbaCompatibility.is()) + return false; + + bool bVBACompatibility = xVbaCompatibility->getVBACompatibilityMode(); + + return bVBACompatibility; +} + +OUString VbaExport::getProjectName() const +{ + css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility(getLibraryContainer(), css::uno::UNO_QUERY); + if (xVbaCompatibility.is()) + return xVbaCompatibility->getProjectName(); + + return OUString(); +} + +rtl_TextEncoding VbaExport::getVBATextEncoding() const +{ + rtl_TextEncoding aTextEncoding = osl_getThreadTextEncoding(); + css::uno::Reference<css::beans::XPropertySet> xProps(getLibraryContainer(), + css::uno::UNO_QUERY); + if (xProps.is()) + try + { + xProps->getPropertyValue("VBATextEncoding") >>= aTextEncoding; + } + catch (const css::uno::Exception&) + { + } + + return aTextEncoding; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/vbahelper.cxx b/oox/source/ole/vbahelper.cxx new file mode 100644 index 000000000..e5377568a --- /dev/null +++ b/oox/source/ole/vbahelper.cxx @@ -0,0 +1,59 @@ +/* -*- 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 <oox/ole/vbahelper.hxx> +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> +#include <oox/helper/binaryinputstream.hxx> + +namespace oox::ole { + +using namespace ::com::sun::star::uno; + +bool VbaHelper::readDirRecord( sal_uInt16& rnRecId, StreamDataSequence& rRecData, BinaryInputStream& rInStrm ) +{ + // read the record header + sal_Int32 nRecSize; + rnRecId = rInStrm.readuInt16(); + nRecSize = rInStrm.readInt32(); + // for no obvious reason, PROJECTVERSION record contains size field of 4, but is 6 bytes long + if( rnRecId == VBA_ID_PROJECTVERSION ) + { + OSL_ENSURE( nRecSize == 4, "VbaHelper::readDirRecord - unexpected record size for PROJECTVERSION" ); + nRecSize = 6; + } + // read the record contents into the passed sequence + return !rInStrm.isEof() && (rInStrm.readData( rRecData, nRecSize ) == nRecSize); +} + +bool VbaHelper::extractKeyValue( OUString& rKey, OUString& rValue, std::u16string_view rKeyValue ) +{ + size_t nEqSignPos = rKeyValue.find( '=' ); + if( nEqSignPos > 0 && nEqSignPos != std::u16string_view::npos ) + { + rKey = o3tl::trim(rKeyValue.substr( 0, nEqSignPos )); + rValue = o3tl::trim(rKeyValue.substr( nEqSignPos + 1 )); + return !rKey.isEmpty() && !rValue.isEmpty(); + } + return false; +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/vbainputstream.cxx b/oox/source/ole/vbainputstream.cxx new file mode 100644 index 000000000..fc795d30d --- /dev/null +++ b/oox/source/ole/vbainputstream.cxx @@ -0,0 +1,208 @@ +/* -*- 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 <oox/ole/vbainputstream.hxx> +#include <osl/diagnose.h> + +namespace oox::ole { + +namespace { + +const sal_uInt8 VBASTREAM_SIGNATURE = 1; + +const sal_uInt16 VBACHUNK_SIGMASK = 0x7000; +const sal_uInt16 VBACHUNK_SIG = 0x3000; +const sal_uInt16 VBACHUNK_COMPRESSED = 0x8000; +const sal_uInt16 VBACHUNK_LENMASK = 0x0FFF; + +} // namespace + +VbaInputStream::VbaInputStream( BinaryInputStream& rInStrm ) : + BinaryStreamBase( false ), + mpInStrm( &rInStrm ), + mnChunkPos( 0 ) +{ + maChunk.reserve( 4096 ); + + sal_uInt8 nSig = rInStrm.readuInt8(); + OSL_ENSURE( nSig == VBASTREAM_SIGNATURE, "VbaInputStream::VbaInputStream - wrong signature" ); + mbEof = mbEof || rInStrm.isEof() || (nSig != VBASTREAM_SIGNATURE); +} + +sal_Int64 VbaInputStream::size() const +{ + return -1; +} + +sal_Int64 VbaInputStream::tell() const +{ + return -1; +} + +void VbaInputStream::seek( sal_Int64 ) +{ +} + +void VbaInputStream::close() +{ + mpInStrm = nullptr; + mbEof = true; +} + +sal_Int32 VbaInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize ) +{ + sal_Int32 nRet = 0; + if( !mbEof ) + { + orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) ); + if( nBytes > 0 ) + { + nRet = readMemory( orData.getArray(), nBytes, nAtomSize ); + if( nRet < nBytes ) + orData.realloc( nRet ); + } + } + return nRet; +} + +sal_Int32 VbaInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t /*nAtomSize*/ ) +{ + sal_Int32 nRet = 0; + sal_uInt8* opnMem = static_cast< sal_uInt8* >( opMem ); + while( (nBytes > 0) && updateChunk() ) + { + sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); + sal_Int32 nReadBytes = ::std::min( nBytes, nChunkLeft ); + memcpy( opnMem, &*(maChunk.begin() + mnChunkPos), nReadBytes ); + opnMem += nReadBytes; + mnChunkPos += static_cast< size_t >( nReadBytes ); + nBytes -= nReadBytes; + nRet += nReadBytes; + } + return nRet; +} + +void VbaInputStream::skip( sal_Int32 nBytes, size_t /*nAtomSize*/ ) +{ + while( (nBytes > 0) && updateChunk() ) + { + sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); + sal_Int32 nSkipBytes = ::std::min( nBytes, nChunkLeft ); + mnChunkPos += static_cast< size_t >( nSkipBytes ); + nBytes -= nSkipBytes; + } +} + +// private -------------------------------------------------------------------- + +bool VbaInputStream::updateChunk() +{ + if( mbEof || (mnChunkPos < maChunk.size()) ) return !mbEof; + // try to read next chunk header, this may trigger EOF + sal_uInt16 nHeader = mpInStrm->readuInt16(); + + mbEof = mpInStrm->isEof(); + if( mbEof ) return false; + + // check header signature + bool bIgnoreBrokenSig = ( (nHeader & VBACHUNK_SIGMASK) != VBACHUNK_SIG ); + + // decode length of chunk data and compression flag + bool bCompressed = getFlag( nHeader, VBACHUNK_COMPRESSED ); + sal_uInt16 nChunkLen = (nHeader & VBACHUNK_LENMASK) + 1; + OSL_ENSURE( bCompressed || (nChunkLen == 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" ); + + // From the amazing bit detective work of Valek Filippov<frob@gnome.org> + // this tweak and the one at the bottom of the method to seek to the + // start of the next chunk we can read those strange broken + // ( I guess from a MSO bug ) compressed streams > 4k + + if ( bIgnoreBrokenSig ) + { + bCompressed = true; + nChunkLen = 4094; + } + + sal_Int64 target = mpInStrm->tell() + nChunkLen; + if( bCompressed ) + { + maChunk.clear(); + sal_uInt8 nBitCount = 4; + sal_uInt16 nChunkPos = 0; + while( !mbEof && !mpInStrm->isEof() && (nChunkPos < nChunkLen) ) + { + sal_uInt8 nTokenFlags = mpInStrm->readuInt8(); + ++nChunkPos; + for( int nBit = 0; !mbEof && !mpInStrm->isEof() && (nBit < 8) && (nChunkPos < nChunkLen); ++nBit, nTokenFlags >>= 1 ) + { + if( nTokenFlags & 1 ) + { + sal_uInt16 nCopyToken = mpInStrm->readuInt16(); + nChunkPos = nChunkPos + 2; + // update bit count used for offset/length in the token + while( ( static_cast<size_t>(1) << nBitCount ) < maChunk.size() ) ++nBitCount; + // extract length from lower (16-nBitCount) bits, plus 3 + sal_uInt16 nLength = extractValue< sal_uInt16 >( nCopyToken, 0, 16 - nBitCount ) + 3; + // extract offset from high nBitCount bits, plus 1 + sal_uInt16 nOffset = extractValue< sal_uInt16 >( nCopyToken, 16 - nBitCount, nBitCount ) + 1; + mbEof = (nOffset > maChunk.size()) || (maChunk.size() + nLength > 4096); + OSL_ENSURE( !mbEof, "VbaInputStream::updateChunk - invalid offset or size in copy token" ); + if( !mbEof ) + { + // append data to buffer + maChunk.resize( maChunk.size() + nLength ); + sal_uInt8* pnTo = &*(maChunk.end() - nLength); + const sal_uInt8* pnEnd = pnTo + nLength; + const sal_uInt8* pnFrom = pnTo - nOffset; + // offset may be less than length, effectively duplicating source data several times + size_t nRunLen = ::std::min< size_t >( nLength, nOffset ); + while( pnTo < pnEnd ) + { + size_t nStepLen = ::std::min< size_t >( nRunLen, pnEnd - pnTo ); + memcpy( pnTo, pnFrom, nStepLen ); + pnTo += nStepLen; + } + } + } + // we suspect this will never be called + else + { + maChunk.emplace_back(); + maChunk.back() = mpInStrm->readuChar(); + ++nChunkPos; + } + } + } + } + else + { + maChunk.resize( nChunkLen ); + mpInStrm->readMemory(maChunk.data(), nChunkLen); + } + // decompression sometimes leaves the stream pos offset 1 place ( at + // least ) past or before the expected stream pos. + // here we make sure we are on the chunk boundary + mpInStrm->seek( target ); + mnChunkPos = 0; + return !mbEof; +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/vbamodule.cxx b/oox/source/ole/vbamodule.cxx new file mode 100644 index 000000000..207a76e83 --- /dev/null +++ b/oox/source/ole/vbamodule.cxx @@ -0,0 +1,347 @@ +/* -*- 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 <oox/ole/vbamodule.hxx> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/script/ModuleInfo.hpp> +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/script/vba/XVBAModuleInfo.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <osl/diagnose.h> +#include <rtl/character.hxx> +#include <o3tl/string_view.hxx> +#include <filter/msfilter/msvbahelper.hxx> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/helper/storagebase.hxx> +#include <oox/helper/textinputstream.hxx> +#include <oox/ole/vbahelper.hxx> +#include <oox/ole/vbainputstream.hxx> + +namespace oox::ole { + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::script::vba; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +using ::com::sun::star::awt::KeyEvent; + +VbaModule::VbaModule( const Reference< XComponentContext >& rxContext, + const Reference< frame::XModel >& rxDocModel, + const OUString& rName, rtl_TextEncoding eTextEnc, bool bExecutable ) : + mxContext( rxContext ), + mxDocModel( rxDocModel ), + maName( rName ), + meTextEnc( eTextEnc ), + mnType( script::ModuleType::UNKNOWN ), + mnOffset( SAL_MAX_UINT32 ), + mbReadOnly( false ), + mbPrivate( false ), + mbExecutable( bExecutable ) +{ +} + +void VbaModule::importDirRecords( BinaryInputStream& rDirStrm ) +{ + sal_uInt16 nRecId = 0; + StreamDataSequence aRecData; + while( VbaHelper::readDirRecord( nRecId, aRecData, rDirStrm ) && (nRecId != VBA_ID_MODULEEND) ) + { + SequenceInputStream aRecStrm( aRecData ); + sal_Int32 nRecSize = aRecData.getLength(); + switch( nRecId ) + { +#define OOX_ENSURE_RECORDSIZE( cond ) OSL_ENSURE( cond, "VbaModule::importDirRecords - invalid record size" ) + case VBA_ID_MODULENAME: + OSL_FAIL( "VbaModule::importDirRecords - unexpected MODULENAME record" ); + maName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc ); + break; + case VBA_ID_MODULENAMEUNICODE: + break; + case VBA_ID_MODULESTREAMNAME: + maStreamName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc ); + // Actually the stream name seems the best name to use + // the VBA_ID_MODULENAME name can sometimes be the wrong case + maName = maStreamName; + break; + case VBA_ID_MODULESTREAMNAMEUNICODE: + break; + case VBA_ID_MODULEDOCSTRING: + maDocString = aRecStrm.readCharArrayUC( nRecSize, meTextEnc ); + break; + case VBA_ID_MODULEDOCSTRINGUNICODE: + break; + case VBA_ID_MODULEOFFSET: + OOX_ENSURE_RECORDSIZE( nRecSize == 4 ); + mnOffset = aRecStrm.readuInt32(); + break; + case VBA_ID_MODULEHELPCONTEXT: + OOX_ENSURE_RECORDSIZE( nRecSize == 4 ); + break; + case VBA_ID_MODULECOOKIE: + OOX_ENSURE_RECORDSIZE( nRecSize == 2 ); + break; + case VBA_ID_MODULETYPEPROCEDURAL: + OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); + OSL_ENSURE( mnType == script::ModuleType::UNKNOWN, "VbaModule::importDirRecords - multiple module type records" ); + mnType = script::ModuleType::NORMAL; + break; + case VBA_ID_MODULETYPEDOCUMENT: + OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); + OSL_ENSURE( mnType == script::ModuleType::UNKNOWN, "VbaModule::importDirRecords - multiple module type records" ); + mnType = script::ModuleType::DOCUMENT; + break; + case VBA_ID_MODULEREADONLY: + OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); + mbReadOnly = true; + break; + case VBA_ID_MODULEPRIVATE: + OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); + mbPrivate = true; + break; + default: + OSL_FAIL( "VbaModule::importDirRecords - unknown module record" ); +#undef OOX_ENSURE_RECORDSIZE + } + } + OSL_ENSURE( !maName.isEmpty(), "VbaModule::importDirRecords - missing module name" ); + OSL_ENSURE( !maStreamName.isEmpty(), "VbaModule::importDirRecords - missing module stream name" ); + OSL_ENSURE( mnType != script::ModuleType::UNKNOWN, "VbaModule::importDirRecords - missing module type" ); + OSL_ENSURE( mnOffset < SAL_MAX_UINT32, "VbaModule::importDirRecords - missing module stream offset" ); +} + +void VbaModule::createAndImportModule( StorageBase& rVbaStrg, + const Reference< container::XNameContainer >& rxBasicLib, + const Reference< container::XNameAccess >& rxDocObjectNA ) +{ + OUString aVBASourceCode = readSourceCode( rVbaStrg ); + createModule( aVBASourceCode, rxBasicLib, rxDocObjectNA ); + registerShortcutKeys(); +} + +void VbaModule::registerShortcutKeys() +{ + for (VbaMacroKeyAndMethodBinding const& rKeyBinding : maKeyBindings) + { + try + { + KeyEvent aKeyEvent = ooo::vba::parseKeyEvent(rKeyBinding.msApiKey); + ooo::vba::applyShortCutKeyBinding(mxDocModel, aKeyEvent, rKeyBinding.msMethodName); + } + catch (const Exception&) + { + } + } +} + +void VbaModule::createEmptyModule( const Reference< container::XNameContainer >& rxBasicLib, + const Reference< container::XNameAccess >& rxDocObjectNA ) const +{ + createModule( u"", rxBasicLib, rxDocObjectNA ); +} + +OUString VbaModule::readSourceCode( StorageBase& rVbaStrg ) +{ + OUStringBuffer aSourceCode(512); + static const char sUnmatchedRemovedTag[] = "Rem removed unmatched Sub/End: "; + if( !maStreamName.isEmpty() && (mnOffset != SAL_MAX_UINT32) ) + { + BinaryXInputStream aInStrm( rVbaStrg.openInputStream( maStreamName ), true ); + OSL_ENSURE( !aInStrm.isEof(), "VbaModule::readSourceCode - cannot open module stream" ); + // skip the 'performance cache' stored before the actual source code + aInStrm.seek( mnOffset ); + // if stream is still valid, load the source code + if( !aInStrm.isEof() ) + { + // decompression starts at current stream position of aInStrm + VbaInputStream aVbaStrm( aInStrm ); + // load the source code line-by-line, with some more processing + TextInputStream aVbaTextStrm( mxContext, aVbaStrm, meTextEnc ); + + struct ProcedurePair + { + bool bInProcedure; + sal_uInt32 nPos; + ProcedurePair() : bInProcedure( false ), nPos( 0 ) {}; + } procInfo; + + while( !aVbaTextStrm.isEof() ) + { + OUString aCodeLine = aVbaTextStrm.readLine(); + if( aCodeLine.match( "Attribute " ) ) + { + // attribute + int index = aCodeLine.indexOf( ".VB_ProcData.VB_Invoke_Func = " ); + if ( index != -1 ) + { + // format is + // 'Attribute Procedure.VB_ProcData.VB_Invoke_Func = "*\n14"' + // where 'Procedure' is the procedure name and '*' is the shortcut key + // note: his is only relevant for Excel, seems that + // word doesn't store the shortcut in the module + // attributes + int nSpaceIndex = aCodeLine.indexOf(' '); + OUString sProc = aCodeLine.copy( nSpaceIndex + 1, index - nSpaceIndex - 1); + // for Excel short cut key seems limited to cntrl+'a-z, A-Z' + std::u16string_view sKey = aCodeLine.subView( aCodeLine.lastIndexOf("= ") + 3, 1 ); + // only alpha key valid for key shortcut, however the api will accept other keys + if ( rtl::isAsciiAlpha( sKey[ 0 ] ) ) + { + // cntrl modifier is explicit ( but could be cntrl+shift ), parseKeyEvent + // will handle and uppercase letter appropriately + OUString sApiKey = OUString::Concat("^") + sKey; + maKeyBindings.push_back({sApiKey, sProc}); + } + } + } + else + { + // Hack here to weed out any unmatched End Sub / Sub Foo statements. + // The behaviour of the vba ide practically guarantees the case and + // spacing of Sub statement(s). However, indentation can be arbitrary hence + // the trim. + OUString trimLine( aCodeLine.trim() ); + if ( mbExecutable && ( + trimLine.match("Sub ") || + trimLine.match("Public Sub ") || + trimLine.match("Private Sub ") || + trimLine.match("Static Sub ") ) ) + { + // this should never happen, basic doesn't support nested procedures + // first Sub Foo must be bogus + if ( procInfo.bInProcedure ) + { + // comment out the line + aSourceCode.insert( procInfo.nPos, sUnmatchedRemovedTag ); + // mark location of this Sub + procInfo.nPos = aSourceCode.getLength(); + } + else + { + procInfo.bInProcedure = true; + procInfo.nPos = aSourceCode.getLength(); + } + } + else if ( mbExecutable && o3tl::starts_with(o3tl::trim(aCodeLine), u"End Sub") ) + { + // un-matched End Sub + if ( !procInfo.bInProcedure ) + { + aSourceCode.append( sUnmatchedRemovedTag ); + } + else + { + procInfo.bInProcedure = false; + procInfo.nPos = 0; + } + } + // normal source code line + if( !mbExecutable ) + aSourceCode.append( "Rem " ); + aSourceCode.append( aCodeLine ).append( '\n' ); + } + } + } + } + return aSourceCode.makeStringAndClear(); +} + +void VbaModule::createModule( std::u16string_view rVBASourceCode, + const Reference< container::XNameContainer >& rxBasicLib, + const Reference< container::XNameAccess >& rxDocObjectNA ) const +{ + if( maName.isEmpty() ) + return; + + // prepare the Basic module + script::ModuleInfo aModuleInfo; + aModuleInfo.ModuleType = mnType; + OUStringBuffer aSourceCode(512); + aSourceCode.append( "Rem Attribute VBA_ModuleType=" ); + switch( mnType ) + { + case script::ModuleType::NORMAL: + aSourceCode.append( "VBAModule" ); + break; + case script::ModuleType::CLASS: + aSourceCode.append( "VBAClassModule" ); + break; + case script::ModuleType::FORM: + aSourceCode.append( "VBAFormModule" ); + // hack from old filter, document Basic should know the XModel, but it doesn't + aModuleInfo.ModuleObject.set( mxDocModel, UNO_QUERY ); + break; + case script::ModuleType::DOCUMENT: + aSourceCode.append( "VBADocumentModule" ); + // get the VBA implementation object associated to the document module + if( rxDocObjectNA.is() ) try + { + aModuleInfo.ModuleObject.set( rxDocObjectNA->getByName( maName ), UNO_QUERY ); + } + catch (const Exception&) + { + } + break; + default: + aSourceCode.append( "VBAUnknown" ); + } + aSourceCode.append( '\n' ); + if( mbExecutable ) + { + aSourceCode.append( "Option VBASupport 1\n" ); + if( mnType == script::ModuleType::CLASS ) + aSourceCode.append( "Option ClassModule\n" ); + } + else + { + // add a subroutine named after the module itself + aSourceCode.append( "Sub " + maName.replace( ' ', '_' ) + "\n" ); + } + + // append passed VBA source code + aSourceCode.append( rVBASourceCode ); + + // close the subroutine named after the module + if( !mbExecutable ) + aSourceCode.append( "End Sub\n" ); + + // insert extended module info + try + { + Reference< XVBAModuleInfo > xVBAModuleInfo( rxBasicLib, UNO_QUERY_THROW ); + xVBAModuleInfo->insertModuleInfo( maName, aModuleInfo ); + } + catch (const Exception&) + { + } + + // insert the module into the passed Basic library + try + { + rxBasicLib->insertByName( maName, Any( aSourceCode.makeStringAndClear() ) ); + } + catch (const Exception&) + { + OSL_FAIL( "VbaModule::createModule - cannot insert module into library" ); + } +} + +} // namespace oox::ole + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/ole/vbaproject.cxx b/oox/source/ole/vbaproject.cxx new file mode 100644 index 000000000..c868d83b6 --- /dev/null +++ b/oox/source/ole/vbaproject.cxx @@ -0,0 +1,564 @@ +/* -*- 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 <oox/ole/vbaproject.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <com/sun/star/script/vba/XVBAMacroResolver.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/configurationhelper.hxx> +#include <comphelper/documentinfo.hxx> +#include <comphelper/storagehelper.hxx> +#include <osl/diagnose.h> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/helper/propertyset.hxx> +#include <oox/helper/textinputstream.hxx> +#include <oox/ole/olestorage.hxx> +#include <oox/ole/vbacontrol.hxx> +#include <oox/ole/vbahelper.hxx> +#include <oox/ole/vbainputstream.hxx> +#include <oox/ole/vbamodule.hxx> +#include <oox/token/properties.hxx> + +namespace oox::ole { + +using namespace ::com::sun::star; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::script::vba; +using namespace ::com::sun::star::uno; + +using ::comphelper::ConfigurationHelper; + +namespace { + +bool lclReadConfigItem( const Reference< XInterface >& rxConfigAccess, const OUString& rItemName ) +{ + // some applications do not support all configuration items, assume 'false' in this case + try + { + Any aItem = ConfigurationHelper::readRelativeKey( rxConfigAccess, "Filter/Import/VBA", rItemName ); + return aItem.has< bool >() && aItem.get< bool >(); + } + catch(const Exception& ) + { + } + return false; +} + +} // namespace + +VbaFilterConfig::VbaFilterConfig( const Reference< XComponentContext >& rxContext, std::u16string_view rConfigCompName ) +{ + OSL_ENSURE( rxContext.is(), "VbaFilterConfig::VbaFilterConfig - missing component context" ); + if( rxContext.is() ) try + { + OSL_ENSURE( !rConfigCompName.empty(), "VbaFilterConfig::VbaFilterConfig - invalid configuration component name" ); + OUString aConfigPackage = OUString::Concat("org.openoffice.Office.") + rConfigCompName; + mxConfigAccess = ConfigurationHelper::openConfig( rxContext, aConfigPackage, comphelper::EConfigurationModes::ReadOnly ); + } + catch(const Exception& ) + { + } + OSL_ENSURE( mxConfigAccess.is(), "VbaFilterConfig::VbaFilterConfig - cannot open configuration" ); +} + +VbaFilterConfig::~VbaFilterConfig() +{ +} + +bool VbaFilterConfig::isImportVba() const +{ + return lclReadConfigItem( mxConfigAccess, "Load" ); +} + +bool VbaFilterConfig::isImportVbaExecutable() const +{ + return lclReadConfigItem( mxConfigAccess, "Executable" ); +} + +bool VbaFilterConfig::isExportVba() const +{ + return lclReadConfigItem( mxConfigAccess, "Save" ); +} + +VbaMacroAttacherBase::VbaMacroAttacherBase( const OUString& rMacroName ) : + maMacroName( rMacroName ) +{ + OSL_ENSURE( !maMacroName.isEmpty(), "VbaMacroAttacherBase::VbaMacroAttacherBase - empty macro name" ); +} + +VbaMacroAttacherBase::~VbaMacroAttacherBase() +{ +} + +void VbaMacroAttacherBase::resolveAndAttachMacro( const Reference< XVBAMacroResolver >& rxResolver ) +{ + try + { + attachMacro( rxResolver->resolveVBAMacroToScriptURL( maMacroName ) ); + } + catch(const Exception& ) + { + } +} + +VbaProject::VbaProject( const Reference< XComponentContext >& rxContext, + const Reference< XModel >& rxDocModel, std::u16string_view rConfigCompName ) : + VbaFilterConfig( rxContext, rConfigCompName ), + mxContext( rxContext ), + mxDocModel( rxDocModel ), + maPrjName( "Standard" ) +{ + OSL_ENSURE( mxContext.is(), "VbaProject::VbaProject - missing component context" ); + OSL_ENSURE( mxDocModel.is(), "VbaProject::VbaProject - missing document model" ); +} + +VbaProject::~VbaProject() +{ +} + +bool VbaProject::importVbaProject( StorageBase& rVbaPrjStrg ) +{ + // create GraphicHelper + Reference< css::frame::XFrame > xFrame; + if ( mxDocModel.is() ) + { + Reference< css::frame::XController > xController = mxDocModel->getCurrentController(); + xFrame = xController.is() ? xController->getFrame() : nullptr; + } + StorageRef noStorage; + // if the GraphicHelper tries to use noStorage it will of course crash + // but... this shouldn't happen as there is no reason for GraphicHelper + // to do that when importing VBA projects + GraphicHelper grfHlp( mxContext, xFrame, noStorage ); + importVbaProject( rVbaPrjStrg, grfHlp ); + // return true if something has been imported + return (mxBasicLib.is() && mxBasicLib->hasElements()) || + (mxDialogLib.is() && mxDialogLib->hasElements()); +} + +void VbaProject::importVbaProject( StorageBase& rVbaPrjStrg, const GraphicHelper& rGraphicHelper ) +{ + if( rVbaPrjStrg.isStorage() ) + { + // load the code modules and forms + if( isImportVba() ) + importVba( rVbaPrjStrg, rGraphicHelper ); + // copy entire storage into model + if( isExportVba() ) + copyStorage( rVbaPrjStrg ); + } +} + +void VbaProject::importVbaData(const uno::Reference<io::XInputStream>& xInputStream) +{ + uno::Reference<document::XStorageBasedDocument> xStorageBasedDoc(mxDocModel, uno::UNO_QUERY); + uno::Reference<embed::XStorage> xDocStorage = xStorageBasedDoc->getDocumentStorage(); + { + const sal_Int32 nOpenMode = ElementModes::SEEKABLE | ElementModes::WRITE | ElementModes::TRUNCATE; + uno::Reference<io::XOutputStream> xDocStream(xDocStorage->openStreamElement("_MS_VBA_Macros_XML", nOpenMode), uno::UNO_QUERY); + comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xDocStream); + } + uno::Reference<embed::XTransactedObject>(xDocStorage, uno::UNO_QUERY_THROW)->commit(); +} + +void VbaProject::registerMacroAttacher( const VbaMacroAttacherRef& rxAttacher ) +{ + OSL_ENSURE( rxAttacher, "VbaProject::registerMacroAttacher - unexpected empty reference" ); + maMacroAttachers.push_back( rxAttacher ); +} + +// protected ------------------------------------------------------------------ + +void VbaProject::addDummyModule( const OUString& rName, sal_Int32 nType ) +{ + OSL_ENSURE( !rName.isEmpty(), "VbaProject::addDummyModule - missing module name" ); + maDummyModules[ rName ] = nType; +} + +void VbaProject::prepareImport() +{ +} + +// private -------------------------------------------------------------------- + +Reference< XLibraryContainer > VbaProject::getLibraryContainer( sal_Int32 nPropId ) +{ + PropertySet aDocProp( mxDocModel ); + Reference< XLibraryContainer > xLibContainer( aDocProp.getAnyProperty( nPropId ), UNO_QUERY ); + return xLibContainer; +} + +Reference< XNameContainer > VbaProject::openLibrary( sal_Int32 nPropId ) +{ + Reference< XNameContainer > xLibrary; + try + { + Reference< XLibraryContainer > xLibContainer( getLibraryContainer( nPropId ), UNO_SET_THROW ); + if( !xLibContainer->hasByName( maPrjName ) ) + xLibContainer->createLibrary( maPrjName ); + xLibrary.set( xLibContainer->getByName( maPrjName ), UNO_QUERY_THROW ); + } + catch(const Exception& ) + { + } + OSL_ENSURE( xLibrary.is(), "VbaProject::openLibrary - cannot create library" ); + return xLibrary; +} + +Reference< XNameContainer > const & VbaProject::createBasicLibrary() +{ + if( !mxBasicLib.is() ) + mxBasicLib = openLibrary( PROP_BasicLibraries ); + return mxBasicLib; +} + +Reference< XNameContainer > const & VbaProject::createDialogLibrary() +{ + if( !mxDialogLib.is() ) + mxDialogLib = openLibrary( PROP_DialogLibraries ); + return mxDialogLib; +} + +void VbaProject::importVba( StorageBase& rVbaPrjStrg, const GraphicHelper& rGraphicHelper ) +{ + readVbaModules( rVbaPrjStrg ); + importModulesAndForms(rVbaPrjStrg, rGraphicHelper ); + // attach macros to registered objects + attachMacros(); +} + +void VbaProject::readVbaModules( StorageBase& rVbaPrjStrg ) +{ + StorageRef xVbaStrg = rVbaPrjStrg.openSubStorage( "VBA", false ); + OSL_ENSURE( xVbaStrg, "VbaProject::readVbaModules - cannot open 'VBA' substorage" ); + if( !xVbaStrg ) + return; + + /* Read the 'VBA/dir' stream which contains general settings of the VBA + project such as the text encoding used throughout several streams, and + a list of all code modules. + */ + BinaryXInputStream aInStrm( xVbaStrg->openInputStream( "dir" ), true ); + // VbaInputStream implements decompression + VbaInputStream aDirStrm( aInStrm ); + OSL_ENSURE( !aDirStrm.isEof(), "VbaProject::importVba - cannot open 'dir' stream" ); + if( aDirStrm.isEof() ) + return; + + // virtual call, derived classes may do some preparations + prepareImport(); + + // read all records of the directory + rtl_TextEncoding eTextEnc = RTL_TEXTENCODING_MS_1252; + sal_uInt16 nModuleCount = 0; + bool bExecutable = isImportVbaExecutable(); + + sal_uInt16 nRecId = 0; + StreamDataSequence aRecData; + while( VbaHelper::readDirRecord( nRecId, aRecData, aDirStrm ) && (nRecId != VBA_ID_PROJECTEND) ) + { + // create record stream object from imported record data + SequenceInputStream aRecStrm( aRecData ); + sal_Int32 nRecSize = aRecData.getLength(); + switch( nRecId ) + { + case VBA_ID_PROJECTCODEPAGE: + { + OSL_ENSURE( nRecSize == 2, "VbaProject::importVba - invalid record size" ); + OSL_ENSURE( maModules.empty(), "VbaProject::importVba - unexpected PROJECTCODEPAGE record" ); + rtl_TextEncoding eNewTextEnc = rtl_getTextEncodingFromWindowsCodePage( aRecStrm.readuInt16() ); + OSL_ENSURE( eNewTextEnc != RTL_TEXTENCODING_DONTKNOW, "VbaProject::importVba - unknown text encoding" ); + if( eNewTextEnc != RTL_TEXTENCODING_DONTKNOW ) + eTextEnc = eNewTextEnc; + } + break; + case VBA_ID_PROJECTNAME: + { + OUString aPrjName = aRecStrm.readCharArrayUC( nRecSize, eTextEnc ); + OSL_ENSURE( !aPrjName.isEmpty(), "VbaProject::importVba - invalid project name" ); + if( !aPrjName.isEmpty() ) + maPrjName = aPrjName; + } + break; + case VBA_ID_PROJECTMODULES: + OSL_ENSURE( nRecSize == 2, "VbaProject::importVba - invalid record size" ); + OSL_ENSURE( maModules.empty(), "VbaProject::importVba - unexpected PROJECTMODULES record" ); + nModuleCount = aRecStrm.readuInt16(); + break; + case VBA_ID_MODULENAME: + { + OUString aName = aRecStrm.readCharArrayUC( nRecSize, eTextEnc ); + OSL_ENSURE( !aName.isEmpty(), "VbaProject::importVba - invalid module name" ); + OSL_ENSURE( !maModules.has( aName ), "VbaProject::importVba - multiple modules with the same name" ); + VbaModuleMap::mapped_type& rxModule = maModules[ aName ]; + rxModule = std::make_shared<VbaModule>( mxContext, mxDocModel, aName, eTextEnc, bExecutable ); + // read all remaining records until the MODULEEND record + rxModule->importDirRecords( aDirStrm ); + OSL_ENSURE( !maModulesByStrm.has( rxModule->getStreamName() ), "VbaProject::importVba - multiple modules with the same stream name" ); + maModulesByStrm[ rxModule->getStreamName() ] = rxModule; + } + break; + } + } + SAL_WARN_IF( nModuleCount != maModules.size(), "oox", "VbaProject::importVba - invalid module count" ); + + /* The directory does not contain the real type of the modules, it + distinguishes only between 'procedural' and 'document' (the latter + includes class and form modules). Now, the exact type of all modules + will be read from the 'PROJECT' stream. It consists of text lines in + 'key=value' format which list the code modules by type. + + - The line 'document=<modulename>/&HXXXXXXXX' declares document + modules. These are attached to the Word document (usually called + 'ThisDocument'), the Excel workbook (usually called + 'ThisWorkbook'), or single Excel worksheets or chartsheets (usually + called 'SheetX' or 'ChartX', X being a decimal number). Of course, + users may rename all these modules. The slash character separates + an automation server version number (hexadecimal 'XXXXXXXX') from + the module name. + - The line 'Module=<modulename>' declares common procedural code + modules. + - The line 'Class=<modulename>' declares a class module. + - The line 'BaseClass=<modulename>' declares a code module attached + to a user form with the same name. + */ + BinaryXInputStream aPrjStrm( rVbaPrjStrg.openInputStream( "PROJECT" ), true ); + OSL_ENSURE( !aPrjStrm.isEof(), "VbaProject::importVba - cannot open 'PROJECT' stream" ); + // do not exit if this stream does not exist, but proceed to load the modules below + if( !aPrjStrm.isEof() ) + { + TextInputStream aPrjTextStrm( mxContext, aPrjStrm, eTextEnc ); + OUString aKey, aValue; + bool bExitLoop = false; + while( !bExitLoop && !aPrjTextStrm.isEof() ) + { + // read a text line from the stream + OUString aLine = aPrjTextStrm.readLine().trim(); + sal_Int32 nLineLen = aLine.getLength(); + // exit if a subsection starts (section name is given in brackets) + bExitLoop = (nLineLen >= 2) && (aLine[ 0 ] == '[') && (aLine[ nLineLen - 1 ] == ']'); + if( !bExitLoop && VbaHelper::extractKeyValue( aKey, aValue, aLine ) ) + { + sal_Int32 nType = ModuleType::UNKNOWN; + if( aKey.equalsIgnoreAsciiCase( "Document" ) ) + { + nType = ModuleType::DOCUMENT; + // strip automation server version from module names + sal_Int32 nSlashPos = aValue.indexOf( '/' ); + if( nSlashPos >= 0 ) + aValue = aValue.copy( 0, nSlashPos ); + } + else if( aKey.equalsIgnoreAsciiCase( "Module" ) ) + nType = ModuleType::NORMAL; + else if( aKey.equalsIgnoreAsciiCase( "Class" ) ) + nType = ModuleType::CLASS; + else if( aKey.equalsIgnoreAsciiCase( "BaseClass" ) ) + nType = ModuleType::FORM; + + if( (nType != ModuleType::UNKNOWN) && !aValue.isEmpty() ) + { + OSL_ENSURE( maModules.has( aValue ), "VbaProject::importVba - module not found" ); + if( VbaModule* pModule = maModules.get( aValue ).get() ) + pModule->setType( nType ); + } + } + } + } + + if( maModules.empty() ) + return; + + try + { + /* Set library container to VBA compatibility mode. This will create + the VBA Globals object and store it in the Basic manager of the + document. */ + try + { + Reference< XVBACompatibility > xVBACompat( getLibraryContainer( PROP_BasicLibraries ), UNO_QUERY_THROW ); + xVBACompat->setVBACompatibilityMode( true ); + xVBACompat->setProjectName( maPrjName ); + + uno::Reference<beans::XPropertySet> xProps(xVBACompat, uno::UNO_QUERY_THROW); + xProps->setPropertyValue("VBATextEncoding", uno::Any(eTextEnc)); + } + catch(const Exception& ) + { + } + } + catch(const Exception& ) + { + } +} + +void VbaProject::importModulesAndForms( StorageBase& rVbaPrjStrg, const GraphicHelper& rGraphicHelper ) +{ + StorageRef xVbaStrg = rVbaPrjStrg.openSubStorage( "VBA", false ); + OSL_ENSURE( xVbaStrg, "VbaProject::importModulesAndForms - cannot open 'VBA' substorage" ); + if( !xVbaStrg ) + return; + rtl_TextEncoding eTextEnc = RTL_TEXTENCODING_MS_1252; + bool bExecutable = isImportVbaExecutable(); + + // create empty dummy modules + VbaModuleMap aDummyModules; + for (auto const& dummyModule : maDummyModules) + { + OSL_ENSURE( !maModules.has( dummyModule.first ) && !aDummyModules.has( dummyModule.first ), "VbaProject::importVba - multiple modules with the same name" ); + VbaModuleMap::mapped_type& rxModule = aDummyModules[ dummyModule.first ]; + rxModule = std::make_shared<VbaModule>( mxContext, mxDocModel, dummyModule.first, eTextEnc, bExecutable ); + rxModule->setType( dummyModule.second ); + } + + /* Now it is time to load the source code. All modules will be inserted + into the Basic library of the document specified by the 'maPrjName' + member. Do not create the Basic library, if there are no modules + specified. */ + if( !maModules.empty() || !aDummyModules.empty() ) try + { + // get the model factory and the basic library + Reference< XMultiServiceFactory > xModelFactory( mxDocModel, UNO_QUERY_THROW ); + Reference< XNameContainer > xBasicLib( createBasicLibrary(), UNO_SET_THROW ); + + // try to get access to document objects related to code modules + Reference< XNameAccess > xDocObjectNA; + try + { + xDocObjectNA.set( xModelFactory->createInstance( "ooo.vba.VBAObjectModuleObjectProvider" ), UNO_QUERY ); + } + catch(const Exception& ) + { + // not all documents support this + } + + if( xBasicLib.is() ) + { + // #TODO cater for mxOleOverridesSink, like I used to before + // call Basic source code import for each module, std::[c]ref enforces pass-by-ref + maModules.forEachMem( &VbaModule::createAndImportModule, + ::std::ref( *xVbaStrg ), ::std::cref( xBasicLib ), + ::std::cref( xDocObjectNA ) ); + + // create empty dummy modules + aDummyModules.forEachMem( &VbaModule::createEmptyModule, + ::std::cref( xBasicLib ), ::std::cref( xDocObjectNA ) ); + } + } + catch(const Exception& ) + { + } + + /* Load the forms. The file format specification requires that a module + must exist for every form. We are a bit more tolerant and scan the + project storage for all form substorages. This may 'repair' broken VBA + storages that misses to mention a module for an existing form. */ + ::std::vector< OUString > aElements; + rVbaPrjStrg.getElementNames( aElements ); + for (auto const& elem : aElements) + { + // try to open the element as storage + if( elem != "VBA" ) + { + StorageRef xSubStrg = rVbaPrjStrg.openSubStorage( elem, false ); + if( xSubStrg ) try + { + // resolve module name from storage name (which equals the module stream name) + VbaModule* pModule = maModulesByStrm.get( elem ).get(); + OSL_ENSURE( pModule && (pModule->getType() == ModuleType::FORM), + "VbaProject::importVba - form substorage without form module" ); + OUString aModuleName; + if( pModule ) + aModuleName = pModule->getName(); + + // create and import the form + Reference< XNameContainer > xDialogLib( createDialogLibrary(), UNO_SET_THROW ); + VbaUserForm aForm( mxContext, mxDocModel, rGraphicHelper, true/*bDefaultColorBgr*/ ); + aForm.importForm( xDialogLib, *xSubStrg, aModuleName, eTextEnc ); + } + catch(const Exception& ) + { + } + } + } +} + +void VbaProject::attachMacros() +{ + if( maMacroAttachers.empty() || !mxContext.is() ) + return; + + try + { + comphelper::DocumentInfo::notifyMacroEventRead(mxDocModel); + + Reference< XMultiComponentFactory > xFactory( mxContext->getServiceManager(), UNO_SET_THROW ); + Sequence< Any > aArgs{ Any(mxDocModel), Any(maPrjName) }; + Reference< XVBAMacroResolver > xResolver( xFactory->createInstanceWithArgumentsAndContext( + "com.sun.star.script.vba.VBAMacroResolver", aArgs, mxContext ), UNO_QUERY_THROW ); + maMacroAttachers.forEachMem( &VbaMacroAttacherBase::resolveAndAttachMacro, ::std::cref( xResolver ) ); + + } + catch(const Exception& ) + { + } +} + +void VbaProject::copyStorage( StorageBase& rVbaPrjStrg ) +{ + if( !mxContext.is() ) + return; + + try + { + Reference< XStorageBasedDocument > xStorageBasedDoc( mxDocModel, UNO_QUERY_THROW ); + Reference< XStorage > xDocStorage( xStorageBasedDoc->getDocumentStorage(), UNO_SET_THROW ); + { + const sal_Int32 nOpenMode = ElementModes::SEEKABLE | ElementModes::WRITE | ElementModes::TRUNCATE; + Reference< XStream > xDocStream( xDocStorage->openStreamElement( "_MS_VBA_Macros", nOpenMode ), UNO_SET_THROW ); + OleStorage aDestStorage( mxContext, xDocStream, false ); + rVbaPrjStrg.copyStorageToStorage( aDestStorage ); + aDestStorage.commit(); + } + Reference< XTransactedObject >( xDocStorage, UNO_QUERY_THROW )->commit(); + } + catch(const Exception& ) + { + } +} + +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |