summaryrefslogtreecommitdiffstats
path: root/oox/source/ole
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /oox/source/ole
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'oox/source/ole')
-rw-r--r--oox/source/ole/axbinaryreader.cxx291
-rw-r--r--oox/source/ole/axbinarywriter.cxx201
-rw-r--r--oox/source/ole/axcontrol.cxx2787
-rw-r--r--oox/source/ole/axcontrolfragment.cxx167
-rw-r--r--oox/source/ole/axfontdata.cxx117
-rw-r--r--oox/source/ole/olehelper.cxx572
-rw-r--r--oox/source/ole/oleobjecthelper.cxx178
-rw-r--r--oox/source/ole/olestorage.cxx377
-rw-r--r--oox/source/ole/vbacontrol.cxx873
-rw-r--r--oox/source/ole/vbaexport.cxx1194
-rw-r--r--oox/source/ole/vbahelper.cxx59
-rw-r--r--oox/source/ole/vbainputstream.cxx208
-rw-r--r--oox/source/ole/vbamodule.cxx348
-rw-r--r--oox/source/ole/vbaproject.cxx572
14 files changed, 7944 insertions, 0 deletions
diff --git a/oox/source/ole/axbinaryreader.cxx b/oox/source/ole/axbinaryreader.cxx
new file mode 100644
index 0000000000..6467e51bc0
--- /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 0000000000..b9c4b66bec
--- /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 0000000000..1c45988b2b
--- /dev/null
+++ b/oox/source/ole/axcontrol.cxx
@@ -0,0 +1,2787 @@
+/* -*- 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 <utility>
+#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 <comphelper/diagnose_ex.hxx>
+#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,
+ std::u16string_view 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.size() == 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( "&lt;", "<" );
+ displayValue = displayValue.replaceAll( "&gt;", ">" );
+ displayValue = displayValue.replaceAll( "&quot;", "\"" );
+ displayValue = displayValue.replaceAll( "&amp;", "&" );
+ 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( OUString aName ) :
+ maName(std::move( aName ))
+{
+}
+
+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 0000000000..5091af6607
--- /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.getStringDefaulted( AX_TOKEN( value )) );
+ }
+ }
+ break;
+
+ case AX_TOKEN( ocxPr ):
+ if( nElement == AX_TOKEN( picture ) )
+ {
+ OUString aPicturePath = getFragmentPathFromRelId( rAttribs.getStringDefaulted( R_TOKEN( id )) );
+ 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.getStringDefaulted( AX_TOKEN( classid ));
+ 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.getStringDefaulted( R_TOKEN( id )) );
+ 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.getStringDefaulted( R_TOKEN( id )) );
+ 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 0000000000..2505168bcf
--- /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 0000000000..d953cf17f9
--- /dev/null
+++ b/oox/source/ole/olehelper.cxx
@@ -0,0 +1,572 @@
+/* -*- 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>
+#include <utility>
+
+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 = std::size( 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( OUString aName, sal_uInt32 nHeight ) :
+ maName(std::move( aName )),
+ 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 0000000000..f99e4a897e
--- /dev/null
+++ b/oox/source/ole/oleobjecthelper.cxx
@@ -0,0 +1,178 @@
+/* -*- 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>
+#include <utility>
+
+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> xModel)
+ : m_xModel(std::move(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 OUString sEmbeddingsPropName = u"EmbeddedObjects"_ustr;
+
+ // 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;
+ auto grabIt = aGrabBag.find(sEmbeddingsPropName);
+ if (grabIt != aGrabBag.end())
+ objectsList << grabIt->second;
+
+ 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 0000000000..5f2ca4d414
--- /dev/null
+++ b/oox/source/ole/olestorage.cxx
@@ -0,0 +1,377 @@
+/* -*- 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>
+#include <utility>
+
+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,
+ OUString aElementName );
+
+ 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, OUString aElementName ) :
+ mxStorage( rxStorage ),
+ maElementName(std::move( aElementName ))
+{
+ 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 0000000000..dd9de50a25
--- /dev/null
+++ b/oox/source/ole/vbacontrol.cxx
@@ -0,0 +1,873 @@
+/* -*- 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( "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( std::u16string_view rCodeLine )
+{
+ OUStringBuffer aBuffer;
+ size_t nLen = rCodeLine.size();
+ if( (nLen > 0) && (rCodeLine[ 0 ] == '"') )
+ {
+ bool bExitLoop = false;
+ for( size_t 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, std::u16string_view rKeyword )
+{
+ if( rCodeLine.matchIgnoreAsciiCase( rKeyword ) )
+ {
+ rCodeLine = rCodeLine.copy( rKeyword.size() );
+ // 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 constexpr 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, u"{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 0000000000..c59895246a
--- /dev/null
+++ b/oox/source/ole/vbaexport.cxx
@@ -0,0 +1,1194 @@
+/* -*- 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 <utility>
+#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> xModel):
+ mxModel(std::move(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, std::u16string_view name, const rtl_TextEncoding eTextEncoding)
+{
+ rStrm.WriteUInt16(0x0004); // id
+ size_t sizeOfProjectName = name.size();
+ 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, std::u16string_view 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, std::u16string_view libid,
+ const rtl_TextEncoding eTextEncoding)
+{
+ rStrm.WriteUInt16(0x000D); // id
+ size_t sizeOfLibid = libid.size();
+ 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, std::u16string_view 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", u"*\\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\\Windows\\SysWOW64\\stdole2.tlb#OLE Automation", eTextEncoding);
+ writeREFERENCE(rStrm, "Office", u"*\\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, std::u16string_view name, const rtl_TextEncoding eTextEncoding)
+{
+ rStrm.WriteUInt16(0x0019); // id
+ sal_Int32 n = name.size(); // 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 constexpr 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, Concat2View("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, Concat2View("Module=" + rModuleName + "\r\n"),
+ eTextEncoding);
+ }
+ else if(aModuleInfo.ModuleType == 4)
+ {
+ exportString(rStrm,
+ Concat2View("Document=" + rModuleName + "/&H00000000\r\n"),
+ eTextEncoding);
+ }
+ }
+
+ // section 2.3.1.11 ProjectName
+ exportString(rStrm, Concat2View("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, Concat2View(rModuleName + "=25, 25, 1439, 639, \r\n"),
+ eTextEncoding);
+ }
+ else
+ {
+ exportString(rStrm, Concat2View(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 0000000000..e5377568a5
--- /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 0000000000..fc795d30de
--- /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 0000000000..4bd93cec13
--- /dev/null
+++ b/oox/source/ole/vbamodule.cxx
@@ -0,0 +1,348 @@
+/* -*- 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>
+#include <utility>
+
+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,
+ OUString aName, rtl_TextEncoding eTextEnc, bool bExecutable ) :
+ mxContext( rxContext ),
+ mxDocModel( rxDocModel ),
+ maName(std::move( aName )),
+ 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 + "\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 0000000000..d87c689f8e
--- /dev/null
+++ b/oox/source/ole/vbaproject.cxx
@@ -0,0 +1,572 @@
+/* -*- 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 <comphelper/diagnose_ex.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>
+#include <utility>
+
+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& )
+ {
+ TOOLS_WARN_EXCEPTION("oox", "");
+ }
+ 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( OUString aMacroName ) :
+ maMacroName(std::move( aMacroName ))
+{
+ 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& )
+ {
+ TOOLS_WARN_EXCEPTION("oox", "");
+ }
+}
+
+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& )
+ {
+ TOOLS_WARN_EXCEPTION("oox", "");
+ }
+ 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& )
+ {
+ TOOLS_WARN_EXCEPTION("oox", "");
+ }
+}
+
+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& )
+ {
+ TOOLS_WARN_EXCEPTION("oox", "");
+ }
+ }
+ }
+}
+
+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& )
+ {
+ TOOLS_WARN_EXCEPTION("oox", "");
+ }
+}
+
+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: */