/* -*- 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 ?
            u"com.sun.star.table.CellRangeAddressConversion"_ustr :
            u"com.sun.star.table.CellAddressConversion"_ustr;
        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(u"BoundCell"_ustr, Any(aAddress))) };

        // create the CellValueBinding instance and set at the control model
        Reference< XMultiServiceFactory > xModelFactory( mxDocModel, UNO_QUERY_THROW );
        Reference< XValueBinding > xBinding( xModelFactory->createInstanceWithArguments( u"com.sun.star.table.CellValueBinding"_ustr, 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(u"CellRange"_ustr, Any(aRangeAddr))) };

        // create the EntrySource instance and set at the control model
        Reference< XMultiServiceFactory > xModelFactory( mxDocModel, UNO_QUERY_THROW );
        Reference< XListEntrySource > xEntrySource( xModelFactory->createInstanceWithArguments(u"com.sun.star.table.CellRangeListSource"_ustr, 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 u"com.sun.star.awt.UnoControlButtonModel"_ustr;
        case API_CONTROL_FIXEDTEXT:     return u"com.sun.star.awt.UnoControlFixedTextModel"_ustr;
        case API_CONTROL_IMAGE:         return u"com.sun.star.awt.UnoControlImageControlModel"_ustr;
        case API_CONTROL_CHECKBOX:      return u"com.sun.star.awt.UnoControlCheckBoxModel"_ustr;
        case API_CONTROL_RADIOBUTTON:   return u"com.sun.star.form.component.RadioButton"_ustr;
        case API_CONTROL_EDIT:          return u"com.sun.star.awt.UnoControlEditModel"_ustr;
        case API_CONTROL_NUMERIC:       return u"com.sun.star.awt.UnoControlNumericFieldModel"_ustr;
        case API_CONTROL_LISTBOX:       return u"com.sun.star.form.component.ListBox"_ustr;
        case API_CONTROL_COMBOBOX:      return u"com.sun.star.form.component.ComboBox"_ustr;
        case API_CONTROL_SPINBUTTON:    return u"com.sun.star.form.component.SpinButton"_ustr;
        case API_CONTROL_SCROLLBAR:     return u"com.sun.star.form.component.ScrollBar"_ustr;
        case API_CONTROL_PROGRESSBAR:   return u"com.sun.star.awt.UnoControlProgressBarModel"_ustr;
        case API_CONTROL_GROUPBOX:      return u"com.sun.star.form.component.GroupBox"_ustr;
        case API_CONTROL_FRAME:         return u"com.sun.star.awt.UnoFrameModel"_ustr;
        case API_CONTROL_PAGE:          return u"com.sun.star.awt.UnoPageModel"_ustr;
        case API_CONTROL_MULTIPAGE:     return u"com.sun.star.awt.UnoMultiPageModel"_ustr;
        case API_CONTROL_DIALOG:        return u"com.sun.star.awt.UnoControlDialogModel"_ustr;
        default:    OSL_FAIL( "ControlModelBase::getServiceName - no AWT model service supported" );
    }
    else switch( eCtrlType )
    {
        case API_CONTROL_BUTTON:        return u"com.sun.star.form.component.CommandButton"_ustr;
        case API_CONTROL_FIXEDTEXT:     return u"com.sun.star.form.component.FixedText"_ustr;
        case API_CONTROL_IMAGE:         return u"com.sun.star.form.component.DatabaseImageControl"_ustr;
        case API_CONTROL_CHECKBOX:      return u"com.sun.star.form.component.CheckBox"_ustr;
        case API_CONTROL_RADIOBUTTON:   return u"com.sun.star.form.component.RadioButton"_ustr;
        case API_CONTROL_EDIT:          return u"com.sun.star.form.component.TextField"_ustr;
        case API_CONTROL_NUMERIC:       return u"com.sun.star.form.component.NumericField"_ustr;
        case API_CONTROL_LISTBOX:       return u"com.sun.star.form.component.ListBox"_ustr;
        case API_CONTROL_COMBOBOX:      return u"com.sun.star.form.component.ComboBox"_ustr;
        case API_CONTROL_SPINBUTTON:    return u"com.sun.star.form.component.SpinButton"_ustr;
        case API_CONTROL_SCROLLBAR:     return u"com.sun.star.form.component.ScrollBar"_ustr;
        case API_CONTROL_GROUPBOX:      return u"com.sun.star.form.component.GroupBox"_ustr;
        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( u"Tahoma"_ustr, 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 : u"autoGroup_"_ustr;
        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 )
{
#if OSL_DEBUG_LEVEL >= 2
    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 = std::move(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 = u"Standard"_ustr;
            if( xFormsNC->hasByName( aFormName ) )
            {
                mxFormIC.set( xFormsNC->getByName( aFormName ), UNO_QUERY_THROW );
            }
            else if( mxModelFactory.is() )
            {
                Reference< XForm > xForm( mxModelFactory->createInstance( u"com.sun.star.form.component.Form"_ustr ), 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: */
