LCOV - code coverage report
Current view: top level - oox/source/ole - vbacontrol.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 260 379 68.6 %
Date: 2015-06-13 12:38:46 Functions: 35 43 81.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include "oox/ole/vbacontrol.hxx"
      21             : 
      22             : #include <algorithm>
      23             : #include <set>
      24             : #include <com/sun/star/awt/XControlModel.hpp>
      25             : #include <com/sun/star/container/XNameContainer.hpp>
      26             : #include <com/sun/star/io/XInputStreamProvider.hpp>
      27             : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
      28             : #include <com/sun/star/uno/XComponentContext.hpp>
      29             : #include <osl/diagnose.h>
      30             : #include <rtl/ustrbuf.hxx>
      31             : #include <xmlscript/xmldlg_imexp.hxx>
      32             : #include "oox/helper/attributelist.hxx"
      33             : #include "oox/helper/binaryinputstream.hxx"
      34             : #include "oox/helper/containerhelper.hxx"
      35             : #include "oox/helper/propertymap.hxx"
      36             : #include "oox/helper/propertyset.hxx"
      37             : #include "oox/helper/storagebase.hxx"
      38             : #include "oox/helper/textinputstream.hxx"
      39             : #include "oox/ole/vbahelper.hxx"
      40             : #include <unordered_map>
      41             : 
      42             : namespace oox {
      43             : namespace ole {
      44             : 
      45             : using namespace ::com::sun::star::awt;
      46             : using namespace ::com::sun::star::container;
      47             : using namespace ::com::sun::star::frame;
      48             : using namespace ::com::sun::star::io;
      49             : using namespace ::com::sun::star::lang;
      50             : using namespace ::com::sun::star::uno;
      51             : 
      52             : namespace {
      53             : 
      54             : const sal_uInt16 VBA_SITE_CLASSIDINDEX          = 0x8000;
      55             : const sal_uInt16 VBA_SITE_INDEXMASK             = 0x7FFF;
      56             : const sal_uInt16 VBA_SITE_FORM                  = 7;
      57             : const sal_uInt16 VBA_SITE_IMAGE                 = 12;
      58             : const sal_uInt16 VBA_SITE_FRAME                 = 14;
      59             : const sal_uInt16 VBA_SITE_SPINBUTTON            = 16;
      60             : const sal_uInt16 VBA_SITE_COMMANDBUTTON         = 17;
      61             : const sal_uInt16 VBA_SITE_TABSTRIP              = 18;
      62             : const sal_uInt16 VBA_SITE_LABEL                 = 21;
      63             : const sal_uInt16 VBA_SITE_TEXTBOX               = 23;
      64             : const sal_uInt16 VBA_SITE_LISTBOX               = 24;
      65             : const sal_uInt16 VBA_SITE_COMBOBOX              = 25;
      66             : const sal_uInt16 VBA_SITE_CHECKBOX              = 26;
      67             : const sal_uInt16 VBA_SITE_OPTIONBUTTON          = 27;
      68             : const sal_uInt16 VBA_SITE_TOGGLEBUTTON          = 28;
      69             : const sal_uInt16 VBA_SITE_SCROLLBAR             = 47;
      70             : const sal_uInt16 VBA_SITE_MULTIPAGE             = 57;
      71             : const sal_uInt16 VBA_SITE_UNKNOWN               = 0x7FFF;
      72             : 
      73             : const sal_uInt32 VBA_SITE_TABSTOP               = 0x00000001;
      74             : const sal_uInt32 VBA_SITE_VISIBLE               = 0x00000002;
      75             : const sal_uInt32 VBA_SITE_OSTREAM               = 0x00000010;
      76             : const sal_uInt32 VBA_SITE_DEFFLAGS              = 0x00000033;
      77             : 
      78             : const sal_uInt8 VBA_SITEINFO_COUNT              = 0x80;
      79             : const sal_uInt8 VBA_SITEINFO_MASK               = 0x7F;
      80             : 
      81             : /** Collects names of all controls in a user form or container control. Allows
      82             :     to generate unused names for dummy controls separating option groups.
      83             :  */
      84           2 : class VbaControlNamesSet
      85             : {
      86             : public:
      87             :     explicit            VbaControlNamesSet();
      88             : 
      89             :     /** Inserts the name of the passed control. */
      90             :     void                insertName( const VbaFormControl& rControl );
      91             :     /** Returns a name that is not contained in this set. */
      92             :     OUString            generateDummyName();
      93             : 
      94             : private:
      95             :     typedef ::std::set< OUString > OUStringSet;
      96             :     OUStringSet         maCtrlNames;
      97             :     const OUString      maDummyBaseName;
      98             :     sal_Int32           mnIndex;
      99             : };
     100             : 
     101           2 : VbaControlNamesSet::VbaControlNamesSet() :
     102             :     maDummyBaseName( "DummyGroupSep" ),
     103           2 :     mnIndex( 0 )
     104             : {
     105           2 : }
     106             : 
     107           6 : void VbaControlNamesSet::insertName( const VbaFormControl& rControl )
     108             : {
     109           6 :     OUString aName = rControl.getControlName();
     110           6 :     if( !aName.isEmpty() )
     111           6 :         maCtrlNames.insert( aName );
     112           6 : }
     113             : 
     114           0 : OUString VbaControlNamesSet::generateDummyName()
     115             : {
     116           0 :     OUString aCtrlName;
     117           0 :     do
     118             :     {
     119           0 :         aCtrlName = OUStringBuffer( maDummyBaseName ).append( ++mnIndex ).makeStringAndClear();
     120             :     }
     121           0 :     while( maCtrlNames.count( aCtrlName ) > 0 );
     122           0 :     maCtrlNames.insert( aCtrlName );
     123           0 :     return aCtrlName;
     124             : }
     125             : 
     126             : /** Functor that inserts the name of a control into a VbaControlNamesSet. */
     127             : struct VbaControlNameInserter
     128             : {
     129             : public:
     130             :     VbaControlNamesSet& mrCtrlNames;
     131           2 :     inline explicit     VbaControlNameInserter( VbaControlNamesSet& rCtrlNames ) : mrCtrlNames( rCtrlNames ) {}
     132           6 :     inline void         operator()( const VbaFormControl& rControl ) { mrCtrlNames.insertName( rControl ); }
     133             : };
     134             : 
     135             : /** A dummy invisible form control (fixed label without text) that is used to
     136             :     separate two groups of option buttons.
     137             :  */
     138           0 : class VbaDummyFormControl : public VbaFormControl
     139             : {
     140             : public:
     141             :     explicit            VbaDummyFormControl( const OUString& rName );
     142             : };
     143             : 
     144           0 : VbaDummyFormControl::VbaDummyFormControl( const OUString& rName )
     145             : {
     146           0 :     mxSiteModel.reset( new VbaSiteModel );
     147           0 :     mxSiteModel->importProperty( XML_Name, rName );
     148           0 :     mxSiteModel->importProperty( XML_VariousPropertyBits, OUString( '0' ) );
     149             : 
     150           0 :     mxCtrlModel.reset( new AxLabelModel );
     151           0 :     mxCtrlModel->setAwtModelMode();
     152           0 :     mxCtrlModel->importProperty( XML_Size, "10;10" );
     153           0 : }
     154             : 
     155             : } // namespace
     156             : 
     157           8 : VbaSiteModel::VbaSiteModel() :
     158             :     maPos( 0, 0 ),
     159             :     mnId( 0 ),
     160             :     mnHelpContextId( 0 ),
     161             :     mnFlags( VBA_SITE_DEFFLAGS ),
     162             :     mnStreamLen( 0 ),
     163             :     mnTabIndex( -1 ),
     164             :     mnClassIdOrCache( VBA_SITE_UNKNOWN ),
     165           8 :     mnGroupId( 0 )
     166             : {
     167           8 : }
     168             : 
     169          16 : VbaSiteModel::~VbaSiteModel()
     170             : {
     171          16 : }
     172             : 
     173           2 : void VbaSiteModel::importProperty( sal_Int32 nPropId, const OUString& rValue )
     174             : {
     175           2 :     switch( nPropId )
     176             :     {
     177           2 :         case XML_Name:                  maName = rValue;                                            break;
     178           0 :         case XML_Tag:                   maTag = rValue;                                             break;
     179           0 :         case XML_VariousPropertyBits:   mnFlags = AttributeConversion::decodeUnsigned( rValue );    break;
     180             :     }
     181           2 : }
     182             : 
     183           6 : bool VbaSiteModel::importBinaryModel( BinaryInputStream& rInStrm )
     184             : {
     185           6 :     AxBinaryPropertyReader aReader( rInStrm );
     186           6 :     aReader.readStringProperty( maName );
     187           6 :     aReader.readStringProperty( maTag );
     188           6 :     aReader.readIntProperty< sal_Int32 >( mnId );
     189           6 :     aReader.readIntProperty< sal_Int32 >( mnHelpContextId );
     190           6 :     aReader.readIntProperty< sal_uInt32 >( mnFlags );
     191           6 :     aReader.readIntProperty< sal_uInt32 >( mnStreamLen );
     192           6 :     aReader.readIntProperty< sal_Int16 >( mnTabIndex );
     193           6 :     aReader.readIntProperty< sal_uInt16 >( mnClassIdOrCache );
     194           6 :     aReader.readPairProperty( maPos );
     195           6 :     aReader.readIntProperty< sal_uInt16 >( mnGroupId );
     196           6 :     aReader.skipUndefinedProperty();
     197           6 :     aReader.readStringProperty( maToolTip );
     198           6 :     aReader.skipStringProperty();   // license key
     199           6 :     aReader.readStringProperty( maControlSource );
     200           6 :     aReader.readStringProperty( maRowSource );
     201           6 :     return aReader.finalizeImport();
     202             : }
     203             : 
     204           0 : void VbaSiteModel::moveRelative( const AxPairData& rDistance )
     205             : {
     206           0 :     maPos.first += rDistance.first;
     207           0 :     maPos.second += rDistance.second;
     208           0 : }
     209             : 
     210          18 : bool VbaSiteModel::isContainer() const
     211             : {
     212          18 :     return !getFlag( mnFlags, VBA_SITE_OSTREAM );
     213             : }
     214             : 
     215           6 : sal_uInt32 VbaSiteModel::getStreamLength() const
     216             : {
     217           6 :     return isContainer() ? 0 : mnStreamLen;
     218             : }
     219             : 
     220           0 : OUString VbaSiteModel::getSubStorageName() const
     221             : {
     222           0 :     if( mnId >= 0 )
     223             :     {
     224           0 :         OUStringBuffer aBuffer;
     225           0 :         aBuffer.append( 'i' );
     226           0 :         if( mnId < 10 )
     227           0 :             aBuffer.append( '0' );
     228           0 :         aBuffer.append( mnId );
     229           0 :         return aBuffer.makeStringAndClear();
     230             :     }
     231           0 :     return OUString();
     232             : }
     233             : 
     234           6 : ControlModelRef VbaSiteModel::createControlModel( const AxClassTable& rClassTable ) const
     235             : {
     236           6 :     ControlModelRef xCtrlModel;
     237             : 
     238           6 :     sal_Int32 nTypeIndex = static_cast< sal_Int32 >( mnClassIdOrCache & VBA_SITE_INDEXMASK );
     239           6 :     if( !getFlag( mnClassIdOrCache, VBA_SITE_CLASSIDINDEX ) )
     240             :     {
     241           6 :         switch( nTypeIndex )
     242             :         {
     243           0 :             case VBA_SITE_COMMANDBUTTON:    xCtrlModel.reset( new AxCommandButtonModel );   break;
     244           0 :             case VBA_SITE_LABEL:            xCtrlModel.reset( new AxLabelModel );           break;
     245           0 :             case VBA_SITE_IMAGE:            xCtrlModel.reset( new AxImageModel );           break;
     246           1 :             case VBA_SITE_TOGGLEBUTTON:     xCtrlModel.reset( new AxToggleButtonModel );    break;
     247           1 :             case VBA_SITE_CHECKBOX:         xCtrlModel.reset( new AxCheckBoxModel );        break;
     248           1 :             case VBA_SITE_OPTIONBUTTON:     xCtrlModel.reset( new AxOptionButtonModel );    break;
     249           1 :             case VBA_SITE_TEXTBOX:          xCtrlModel.reset( new AxTextBoxModel );         break;
     250           1 :             case VBA_SITE_LISTBOX:          xCtrlModel.reset( new AxListBoxModel );         break;
     251           1 :             case VBA_SITE_COMBOBOX:         xCtrlModel.reset( new AxComboBoxModel );        break;
     252           0 :             case VBA_SITE_SPINBUTTON:       xCtrlModel.reset( new AxSpinButtonModel );      break;
     253           0 :             case VBA_SITE_SCROLLBAR:        xCtrlModel.reset( new AxScrollBarModel );       break;
     254           0 :             case VBA_SITE_TABSTRIP:         xCtrlModel.reset( new AxTabStripModel );
     255           0 :             break;
     256           0 :             case VBA_SITE_FRAME:            xCtrlModel.reset( new AxFrameModel );           break;
     257           0 :             case VBA_SITE_MULTIPAGE:        xCtrlModel.reset( new AxMultiPageModel );
     258           0 :             break;
     259           0 :             case VBA_SITE_FORM:             xCtrlModel.reset( new AxPageModel );
     260           0 :             break;
     261             :             default:    OSL_FAIL( "VbaSiteModel::createControlModel - unknown type index" );
     262             :         }
     263             :     }
     264             :     else
     265             :     {
     266           0 :         const OUString* pGuid = ContainerHelper::getVectorElement( rClassTable, nTypeIndex );
     267             :         OSL_ENSURE( pGuid, "VbaSiteModel::createControlModel - invalid class table index" );
     268           0 :         if( pGuid )
     269             :         {
     270           0 :             if( *pGuid == COMCTL_GUID_SCROLLBAR_60 )
     271           0 :                 xCtrlModel.reset( new ComCtlScrollBarModel( 6 ) );
     272           0 :             else if( *pGuid == COMCTL_GUID_PROGRESSBAR_50 )
     273           0 :                 xCtrlModel.reset( new ComCtlProgressBarModel( 5 ) );
     274           0 :             else if( *pGuid == COMCTL_GUID_PROGRESSBAR_60 )
     275           0 :                 xCtrlModel.reset( new ComCtlProgressBarModel( 6 ) );
     276             :         }
     277             :     }
     278             : 
     279           6 :     if( xCtrlModel.get() )
     280             :     {
     281             :         // user form controls are AWT models
     282           6 :         xCtrlModel->setAwtModelMode();
     283             : 
     284             :         // check that container model matches container flag in site data
     285           6 :         bool bModelIsContainer = dynamic_cast< const AxContainerModelBase* >( xCtrlModel.get() ) != 0;
     286           6 :         bool bTypeMatch = bModelIsContainer == isContainer();
     287             :         OSL_ENSURE( bTypeMatch, "VbaSiteModel::createControlModel - container type does not match container flag" );
     288           6 :         if( !bTypeMatch )
     289           0 :             xCtrlModel.reset();
     290             :     }
     291           6 :     return xCtrlModel;
     292             : }
     293             : 
     294           8 : void VbaSiteModel::convertProperties( PropertyMap& rPropMap,
     295             :         const ControlConverter& rConv, ApiControlType eCtrlType, sal_Int32 nCtrlIndex ) const
     296             : {
     297           8 :     rPropMap.setProperty( PROP_Name, maName );
     298           8 :     rPropMap.setProperty( PROP_Tag, maTag );
     299             : 
     300           8 :     if( eCtrlType != API_CONTROL_DIALOG )
     301             :     {
     302           6 :         rPropMap.setProperty( PROP_HelpText, maToolTip );
     303           6 :         rPropMap.setProperty( PROP_EnableVisible, getFlag( mnFlags, VBA_SITE_VISIBLE ) );
     304             :         // we need to set the passed control index to make option button groups work
     305           6 :         if( (0 <= nCtrlIndex) && (nCtrlIndex <= SAL_MAX_INT16) )
     306           6 :             rPropMap.setProperty( PROP_TabIndex, static_cast< sal_Int16 >( nCtrlIndex ) );
     307             :         // progress bar and group box support TabIndex, but not Tabstop...
     308           6 :         if( (eCtrlType != API_CONTROL_PROGRESSBAR) && (eCtrlType != API_CONTROL_GROUPBOX) && (eCtrlType != API_CONTROL_FRAME) && (eCtrlType != API_CONTROL_PAGE) )
     309           6 :             rPropMap.setProperty( PROP_Tabstop, getFlag( mnFlags, VBA_SITE_TABSTOP ) );
     310           6 :         rConv.convertPosition( rPropMap, maPos );
     311             :     }
     312           8 : }
     313             : 
     314           8 : VbaFormControl::VbaFormControl()
     315             : {
     316           8 : }
     317             : 
     318          14 : VbaFormControl::~VbaFormControl()
     319             : {
     320          14 : }
     321             : 
     322           6 : void VbaFormControl::importModelOrStorage( BinaryInputStream& rInStrm, StorageBase& rStrg, const AxClassTable& rClassTable )
     323             : {
     324           6 :     if( mxSiteModel.get() )
     325             :     {
     326           6 :         if( mxSiteModel->isContainer() )
     327             :         {
     328           0 :             StorageRef xSubStrg = rStrg.openSubStorage( mxSiteModel->getSubStorageName(), false );
     329             :             OSL_ENSURE( xSubStrg.get(), "VbaFormControl::importModelOrStorage - cannot find storage for embedded control" );
     330           0 :             if( xSubStrg.get() )
     331           0 :                 importStorage( *xSubStrg, rClassTable );
     332             :         }
     333           6 :         else if( !rInStrm.isEof() )
     334             :         {
     335           6 :             sal_Int64 nNextStrmPos = rInStrm.tell() + mxSiteModel->getStreamLength();
     336           6 :             importControlModel( rInStrm, rClassTable );
     337           6 :             rInStrm.seek( nNextStrmPos );
     338             :         }
     339             :     }
     340           6 : }
     341             : 
     342           6 : OUString VbaFormControl::getControlName() const
     343             : {
     344           6 :     return mxSiteModel.get() ? mxSiteModel->getName() : OUString();
     345             : }
     346             : 
     347           6 : void VbaFormControl::createAndConvert( sal_Int32 nCtrlIndex,
     348             :         const Reference< XNameContainer >& rxParentNC, const ControlConverter& rConv ) const
     349             : {
     350           6 :     if( rxParentNC.is() && mxSiteModel.get() && mxCtrlModel.get() ) try
     351             :     {
     352             :         // create the control model
     353           6 :         OUString aServiceName = mxCtrlModel->getServiceName();
     354          12 :         Reference< XMultiServiceFactory > xModelFactory( rxParentNC, UNO_QUERY_THROW );
     355          12 :         Reference< XControlModel > xCtrlModel( xModelFactory->createInstance( aServiceName ), UNO_QUERY_THROW );
     356             : 
     357             :         // convert all properties and embedded controls
     358           6 :         if( convertProperties( xCtrlModel, rConv, nCtrlIndex ) )
     359             :         {
     360             :             // insert into parent container
     361           6 :             const OUString& rCtrlName = mxSiteModel->getName();
     362             :             OSL_ENSURE( !rxParentNC->hasByName( rCtrlName ), "VbaFormControl::createAndConvert - multiple controls with equal name" );
     363           6 :             ContainerHelper::insertByName( rxParentNC, rCtrlName, Any( xCtrlModel ) );
     364           6 :         }
     365             :     }
     366           0 :     catch(const Exception& )
     367             :     {
     368             :     }
     369           6 : }
     370             : 
     371             : // protected ------------------------------------------------------------------
     372             : 
     373           6 : void VbaFormControl::importControlModel( BinaryInputStream& rInStrm, const AxClassTable& rClassTable )
     374             : {
     375           6 :     createControlModel( rClassTable );
     376           6 :     if( mxCtrlModel.get() )
     377           6 :         mxCtrlModel->importBinaryModel( rInStrm );
     378           6 : }
     379             : 
     380           2 : void VbaFormControl::importStorage( StorageBase& rStrg, const AxClassTable& rClassTable )
     381             : {
     382           2 :     createControlModel( rClassTable );
     383           2 :     AxContainerModelBase* pContainerModel = dynamic_cast< AxContainerModelBase* >( mxCtrlModel.get() );
     384             :     OSL_ENSURE( pContainerModel, "VbaFormControl::importStorage - missing container control model" );
     385           2 :     if( pContainerModel )
     386             :     {
     387             :         /*  Open the 'f' stream containing the model of this control and a list
     388             :             of site models for all child controls. */
     389           2 :         BinaryXInputStream aFStrm( rStrg.openInputStream( "f" ), true );
     390             :         OSL_ENSURE( !aFStrm.isEof(), "VbaFormControl::importStorage - missing 'f' stream" );
     391             : 
     392             :         /*  Read the properties of this container control and the class table
     393             :             (into the maClassTable vector) containing a list of GUIDs for
     394             :             exotic embedded controls. */
     395           2 :         if( !aFStrm.isEof() && pContainerModel->importBinaryModel( aFStrm ) && pContainerModel->importClassTable( aFStrm, maClassTable ) )
     396             :         {
     397             :             /*  Read the site models of all embedded controls (this fills the
     398             :                 maControls vector). Ignore failure of importSiteModels() but
     399             :                 try to import as much controls as possible. */
     400           2 :             importEmbeddedSiteModels( aFStrm );
     401             :             /*  Open the 'o' stream containing models of embedded simple
     402             :                 controls. Stream may be empty or missing, if this control
     403             :                 contains no controls or only container controls. */
     404           2 :             BinaryXInputStream aOStrm( rStrg.openInputStream( "o" ), true );
     405             : 
     406             :             /*  Iterate over all embedded controls, import model from 'o'
     407             :                 stream (for embedded simple controls) or from the substorage
     408             :                 (for embedded container controls). */
     409             :             maControls.forEachMem( &VbaFormControl::importModelOrStorage,
     410           2 :                 ::boost::ref( aOStrm ), ::boost::ref( rStrg ), ::boost::cref( maClassTable ) );
     411             : 
     412             :             // Special handling for multi-page which has non-standard
     413             :             // containment and additionally needs to re-order Page children
     414           2 :             if ( pContainerModel->getControlType() == API_CONTROL_MULTIPAGE )
     415             :             {
     416           0 :                 AxMultiPageModel* pMultiPage = dynamic_cast< AxMultiPageModel* >( pContainerModel );
     417           0 :                 if ( pMultiPage )
     418             :                 {
     419           0 :                     BinaryXInputStream aXStrm( rStrg.openInputStream( "x" ), true );
     420           0 :                     pMultiPage->importPageAndMultiPageProperties( aXStrm, maControls.size() );
     421             :                 }
     422             :                 typedef std::unordered_map< sal_uInt32, std::shared_ptr< VbaFormControl > > IdToPageMap;
     423           0 :                 IdToPageMap idToPage;
     424           0 :                 VbaFormControlVector::iterator it = maControls.begin();
     425           0 :                 VbaFormControlVector::iterator it_end = maControls.end();
     426             :                 typedef std::vector< sal_uInt32 > UInt32Array;
     427           0 :                 AxArrayString sCaptions;
     428             : 
     429           0 :                 for ( ; it != it_end; ++it )
     430             :                 {
     431           0 :                     if ( (*it)->mxCtrlModel->getControlType() == API_CONTROL_PAGE )
     432             :                     {
     433           0 :                         VbaSiteModelRef xPageSiteRef = (*it)->mxSiteModel;
     434           0 :                         if ( xPageSiteRef.get() )
     435           0 :                             idToPage[ xPageSiteRef->getId() ] = (*it);
     436             :                     }
     437             :                     else
     438             :                     {
     439           0 :                         AxTabStripModel* pTabStrip = static_cast<AxTabStripModel*> ( (*it)->mxCtrlModel.get() );
     440           0 :                         sCaptions = pTabStrip->maItems;
     441           0 :                         pMultiPage->mnActiveTab = pTabStrip->mnListIndex;
     442           0 :                         pMultiPage->mnTabStyle = pTabStrip->mnTabStyle;
     443             :                     }
     444             :                 }
     445             :                 // apply caption/titles to pages
     446           0 :                 UInt32Array::iterator itCtrlId = pMultiPage->mnIDs.begin();
     447           0 :                 UInt32Array::iterator itCtrlId_end = pMultiPage->mnIDs.end();
     448           0 :                 AxArrayString::iterator itCaption = sCaptions.begin();
     449             : 
     450           0 :                 maControls.clear();
     451             :                 // need to sort the controls according to the order of the ids
     452           0 :                 for ( sal_Int32 index = 1 ; ( sCaptions.size() == idToPage.size() ) && itCtrlId != itCtrlId_end; ++itCtrlId, ++itCaption, ++index )
     453             :                 {
     454           0 :                     IdToPageMap::iterator iter = idToPage.find( *itCtrlId );
     455           0 :                     if ( iter != idToPage.end() )
     456             :                     {
     457           0 :                         AxPageModel* pPage = static_cast<AxPageModel*> ( iter->second->mxCtrlModel.get() );
     458             : 
     459           0 :                         pPage->importProperty( XML_Caption, *itCaption );
     460           0 :                         maControls.push_back( iter->second );
     461             :                     }
     462           0 :                 }
     463             :             }
     464             :             /*  Reorder the controls (sorts all option buttons of an option
     465             :                 group together), and move all children of all embedded frames
     466             :                 (group boxes) to this control (UNO group boxes cannot contain
     467             :                 other controls). */
     468           2 :             finalizeEmbeddedControls();
     469           2 :         }
     470             :     }
     471           2 : }
     472             : 
     473           8 : bool VbaFormControl::convertProperties( const Reference< XControlModel >& rxCtrlModel,
     474             :         const ControlConverter& rConv, sal_Int32 nCtrlIndex ) const
     475             : {
     476           8 :     if( rxCtrlModel.is() && mxSiteModel.get() && mxCtrlModel.get() )
     477             :     {
     478           8 :         const OUString& rCtrlName = mxSiteModel->getName();
     479             :         OSL_ENSURE( !rCtrlName.isEmpty(), "VbaFormControl::convertProperties - control without name" );
     480           8 :         if( !rCtrlName.isEmpty() )
     481             :         {
     482             :             // convert all properties
     483           8 :             PropertyMap aPropMap;
     484           8 :             mxSiteModel->convertProperties( aPropMap, rConv, mxCtrlModel->getControlType(), nCtrlIndex );
     485           8 :             rConv.bindToSources( rxCtrlModel, mxSiteModel->getControlSource(), mxSiteModel->getRowSource() );
     486           8 :             mxCtrlModel->convertProperties( aPropMap, rConv );
     487           8 :             mxCtrlModel->convertSize( aPropMap, rConv );
     488          16 :             PropertySet aPropSet( rxCtrlModel );
     489           8 :             aPropSet.setProperties( aPropMap );
     490             : 
     491             :             // create and convert all embedded controls
     492           8 :             if( !maControls.empty() ) try
     493             :             {
     494           2 :                 Reference< XNameContainer > xCtrlModelNC( rxCtrlModel, UNO_QUERY_THROW );
     495             :                 /*  Call conversion for all controls. Pass vector index as new
     496             :                     tab order to make option button groups work correctly. */
     497             :                 maControls.forEachMemWithIndex( &VbaFormControl::createAndConvert,
     498           2 :                     ::boost::cref( xCtrlModelNC ), ::boost::cref( rConv ) );
     499             :             }
     500           0 :             catch(const Exception& )
     501             :             {
     502             :                 OSL_FAIL( "VbaFormControl::convertProperties - cannot get control container interface" );
     503             :             }
     504             : 
     505          16 :             return true;
     506             :         }
     507             :     }
     508           0 :     return false;
     509             : }
     510             : 
     511             : // private --------------------------------------------------------------------
     512             : 
     513           8 : void VbaFormControl::createControlModel( const AxClassTable& rClassTable )
     514             : {
     515             :     // derived classes may have created their own control model
     516           8 :     if( !mxCtrlModel && mxSiteModel.get() )
     517           6 :         mxCtrlModel = mxSiteModel->createControlModel( rClassTable );
     518           8 : }
     519             : 
     520           6 : bool VbaFormControl::importSiteModel( BinaryInputStream& rInStrm )
     521             : {
     522           6 :     mxSiteModel.reset( new VbaSiteModel );
     523           6 :     return mxSiteModel->importBinaryModel( rInStrm );
     524             : }
     525             : 
     526           2 : bool VbaFormControl::importEmbeddedSiteModels( BinaryInputStream& rInStrm )
     527             : {
     528           2 :     sal_uInt64 nAnchorPos = rInStrm.tell();
     529             :     sal_uInt32 nSiteCount, nSiteDataSize;
     530           2 :     nSiteCount = rInStrm.readuInt32();
     531           2 :     nSiteDataSize = rInStrm.readuInt32();
     532           2 :     sal_Int64 nSiteEndPos = rInStrm.tell() + nSiteDataSize;
     533             : 
     534             :     // skip the site info structure
     535           2 :     sal_uInt32 nSiteIndex = 0;
     536           6 :     while( !rInStrm.isEof() && (nSiteIndex < nSiteCount) )
     537             :     {
     538           2 :         rInStrm.skip( 1 ); // site depth
     539           2 :         sal_uInt8 nTypeCount = rInStrm.readuInt8(); // 'type-or-count' byte
     540           2 :         if( getFlag( nTypeCount, VBA_SITEINFO_COUNT ) )
     541             :         {
     542             :             /*  Count flag is set: the 'type-or-count' byte contains the number
     543             :                 of controls in the lower bits, the type specifier follows in
     544             :                 the next byte. The type specifier should always be 1 according
     545             :                 to the specification. */
     546           2 :             rInStrm.skip( 1 );
     547           2 :             nSiteIndex += (nTypeCount & VBA_SITEINFO_MASK);
     548             :         }
     549             :         else
     550             :         {
     551             :             /*  Count flag is not set: the 'type-or-count' byte contains the
     552             :                 type specifier of *one* control in the lower bits (this type
     553             :                 should be 1, see above). */
     554           0 :             ++nSiteIndex;
     555             :         }
     556             :     }
     557             :     // align the stream to 32bit, relative to start of entire site info
     558           2 :     rInStrm.alignToBlock( 4, nAnchorPos );
     559             : 
     560             :     // import the site models for all embedded controls
     561           2 :     maControls.clear();
     562           2 :     bool bValid = !rInStrm.isEof();
     563           8 :     for( nSiteIndex = 0; bValid && (nSiteIndex < nSiteCount); ++nSiteIndex )
     564             :     {
     565           6 :         VbaFormControlRef xControl( new VbaFormControl );
     566           6 :         maControls.push_back( xControl );
     567           6 :         bValid = xControl->importSiteModel( rInStrm );
     568           6 :     }
     569             : 
     570           2 :     rInStrm.seek( nSiteEndPos );
     571           2 :     return bValid;
     572             : }
     573             : 
     574           2 : void VbaFormControl::finalizeEmbeddedControls()
     575             : {
     576             :     /*  This function performs two tasks:
     577             : 
     578             :         1)  Reorder the controls appropriately (sort all option buttons of an
     579             :             option group together to make grouping work).
     580             :         2)  Move all children of all embedded frames (group boxes) to this
     581             :             control (UNO group boxes cannot contain other controls).
     582             :      */
     583             : 
     584             :     // first, sort all controls by original tab index
     585           2 :     ::std::sort( maControls.begin(), maControls.end(), &compareByTabIndex );
     586             : 
     587             :     /*  Collect the programmatical names of all embedded controls (needed to be
     588             :         able to set unused names to new dummy controls created below). Also
     589             :         collect the names of all children of embedded frames (group boxes).
     590             :         Luckily, names of controls must be unique in the entire form, not just
     591             :         in the current container. */
     592           2 :     VbaControlNamesSet aControlNames;
     593           2 :     VbaControlNameInserter aInserter( aControlNames );
     594           2 :     maControls.forEach( aInserter );
     595           8 :     for( VbaFormControlVector::iterator aIt = maControls.begin(), aEnd = maControls.end(); aIt != aEnd; ++aIt )
     596           6 :         if( (*aIt)->mxCtrlModel.get() && ((*aIt)->mxCtrlModel->getControlType() == API_CONTROL_GROUPBOX) )
     597           0 :             (*aIt)->maControls.forEach( aInserter );
     598             : 
     599             :     /*  Reprocess the sorted list and collect all option button controls that
     600             :         are part of the same option group (determined by group name). All
     601             :         controls will be stored in a vector of vectors, that collects every
     602             :         option button group in one vector element, and other controls between
     603             :         these option groups (or leading or trailing controls) in other vector
     604             :         elements. If an option button group follows another group, a dummy
     605             :         separator control has to be inserted. */
     606             :     typedef RefVector< VbaFormControlVector > VbaFormControlVectorVector;
     607           4 :     VbaFormControlVectorVector aControlGroups;
     608             : 
     609             :     typedef RefMap< OUString, VbaFormControlVector > VbaFormControlVectorMap;
     610           4 :     VbaFormControlVectorMap aOptionGroups;
     611             : 
     612             :     typedef VbaFormControlVectorMap::mapped_type VbaFormControlVectorRef;
     613           2 :     bool bLastWasOptionButton = false;
     614           8 :     for( VbaFormControlVector::iterator aIt = maControls.begin(), aEnd = maControls.end(); aIt != aEnd; ++aIt )
     615             :     {
     616           6 :         VbaFormControlRef xControl = *aIt;
     617           6 :         const ControlModelBase* pCtrlModel = xControl->mxCtrlModel.get();
     618             : 
     619           6 :         if( const AxOptionButtonModel* pOptButtonModel = dynamic_cast< const AxOptionButtonModel* >( pCtrlModel ) )
     620             :         {
     621             :             // check if a new option group needs to be created
     622           1 :             const OUString& rGroupName = pOptButtonModel->getGroupName();
     623           1 :             VbaFormControlVectorRef& rxOptionGroup = aOptionGroups[ rGroupName ];
     624           1 :             if( !rxOptionGroup )
     625             :             {
     626             :                 /*  If last control was an option button too, we have two
     627             :                     option groups following each other, so a dummy separator
     628             :                     control is needed. */
     629           1 :                 if( bLastWasOptionButton )
     630             :                 {
     631           0 :                     VbaFormControlVectorRef xDummyGroup( new VbaFormControlVector );
     632           0 :                     aControlGroups.push_back( xDummyGroup );
     633           0 :                     OUString aName = aControlNames.generateDummyName();
     634           0 :                     VbaFormControlRef xDummyControl( new VbaDummyFormControl( aName ) );
     635           0 :                     xDummyGroup->push_back( xDummyControl );
     636             :                 }
     637           1 :                 rxOptionGroup.reset( new VbaFormControlVector );
     638           1 :                 aControlGroups.push_back( rxOptionGroup );
     639             :             }
     640             :             /*  Append the option button to the control group (which is now
     641             :                 referred by the vector aControlGroups and by the map
     642             :                 aOptionGroups). */
     643           1 :             rxOptionGroup->push_back( xControl );
     644           1 :             bLastWasOptionButton = true;
     645             :         }
     646             :         else
     647             :         {
     648             :             // open a new control group, if the last group is an option group
     649           5 :             if( bLastWasOptionButton || aControlGroups.empty() )
     650             :             {
     651           2 :                 VbaFormControlVectorRef xControlGroup( new VbaFormControlVector );
     652           2 :                 aControlGroups.push_back( xControlGroup );
     653             :             }
     654             :             // append the control to the last control group
     655           5 :             VbaFormControlVector& rLastGroup = *aControlGroups.back();
     656           5 :             rLastGroup.push_back( xControl );
     657           5 :             bLastWasOptionButton = false;
     658             : 
     659             :             // if control is a group box, move all its children to this control
     660           5 :             if( pCtrlModel && (pCtrlModel->getControlType() == API_CONTROL_GROUPBOX) )
     661             :             {
     662             :                 /*  Move all embedded controls of the group box relative to the
     663             :                     position of the group box. */
     664           0 :                 xControl->moveEmbeddedToAbsoluteParent();
     665             :                 /*  Insert all children of the group box into the last control
     666             :                     group (following the group box). */
     667           0 :                 rLastGroup.insert( rLastGroup.end(), xControl->maControls.begin(), xControl->maControls.end() );
     668           0 :                 xControl->maControls.clear();
     669             :                 // check if last control of the group box is an option button
     670           0 :                 bLastWasOptionButton = dynamic_cast< const AxOptionButtonModel* >( rLastGroup.back()->mxCtrlModel.get() ) != 0;
     671             :             }
     672             :         }
     673           6 :     }
     674             : 
     675             :     // flatten the vector of vectors of form controls to a single vector
     676           2 :     maControls.clear();
     677           5 :     for( VbaFormControlVectorVector::iterator aIt = aControlGroups.begin(), aEnd = aControlGroups.end(); aIt != aEnd; ++aIt )
     678           5 :         maControls.insert( maControls.end(), (*aIt)->begin(), (*aIt)->end() );
     679           2 : }
     680             : 
     681           0 : void VbaFormControl::moveRelative( const AxPairData& rDistance )
     682             : {
     683           0 :     if( mxSiteModel.get() )
     684           0 :         mxSiteModel->moveRelative( rDistance );
     685           0 : }
     686             : 
     687           0 : void VbaFormControl::moveEmbeddedToAbsoluteParent()
     688             : {
     689           0 :     if( mxSiteModel.get() && !maControls.empty() )
     690             :     {
     691             :         // distance to move is equal to position of this control in its parent
     692           0 :         AxPairData aDistance = mxSiteModel->getPosition();
     693             : 
     694             :         /*  For group boxes: add half of the font height to Y position (VBA
     695             :             positions relative to frame border line, not to 'top' of frame). */
     696           0 :         const AxFontDataModel* pFontModel = dynamic_cast< const AxFontDataModel* >( mxCtrlModel.get() );
     697           0 :         if( pFontModel && (pFontModel->getControlType() == API_CONTROL_GROUPBOX) )
     698             :         {
     699             :             // convert points to 1/100 mm (1 pt = 1/72 inch = 2.54/72 cm = 2540/72 1/100 mm)
     700           0 :             sal_Int32 nFontHeight = static_cast< sal_Int32 >( pFontModel->getFontHeight() * 2540 / 72 );
     701           0 :             aDistance.second += nFontHeight / 2;
     702             :         }
     703             : 
     704             :         // move the embedded controls
     705           0 :         maControls.forEachMem( &VbaFormControl::moveRelative, ::boost::cref( aDistance ) );
     706             :     }
     707           0 : }
     708             : 
     709           9 : bool VbaFormControl::compareByTabIndex( const VbaFormControlRef& rxLeft, const VbaFormControlRef& rxRight )
     710             : {
     711             :     // sort controls without model to the end
     712           9 :     sal_Int32 nLeftTabIndex = rxLeft->mxSiteModel.get() ? rxLeft->mxSiteModel->getTabIndex() : SAL_MAX_INT32;
     713           9 :     sal_Int32 nRightTabIndex = rxRight->mxSiteModel.get() ? rxRight->mxSiteModel->getTabIndex() : SAL_MAX_INT32;
     714           9 :     return nLeftTabIndex < nRightTabIndex;
     715             : }
     716             : 
     717             : namespace {
     718             : 
     719           2 : OUString lclGetQuotedString( const OUString& rCodeLine )
     720             : {
     721           2 :     OUStringBuffer aBuffer;
     722           2 :     sal_Int32 nLen = rCodeLine.getLength();
     723           2 :     if( (nLen > 0) && (rCodeLine[ 0 ] == '"') )
     724             :     {
     725           2 :         bool bExitLoop = false;
     726          22 :         for( sal_Int32 nIndex = 1; !bExitLoop && (nIndex < nLen); ++nIndex )
     727             :         {
     728          20 :             sal_Unicode cChar = rCodeLine[ nIndex ];
     729             :             // exit on closing quote char (but check on double quote chars)
     730          20 :             bExitLoop = (cChar == '"') && ((nIndex + 1 == nLen) || (rCodeLine[ nIndex + 1 ] != '"'));
     731          20 :             if( !bExitLoop )
     732             :             {
     733          18 :                 aBuffer.append( cChar );
     734             :                 // skip second quote char
     735          18 :                 if( cChar == '"' )
     736           0 :                     ++nIndex;
     737             :             }
     738             :         }
     739             :     }
     740           2 :     return aBuffer.makeStringAndClear();
     741             : }
     742             : 
     743           4 : bool lclEatWhitespace( OUString& rCodeLine )
     744             : {
     745           4 :     sal_Int32 nIndex = 0;
     746          12 :     while( (nIndex < rCodeLine.getLength()) && ((rCodeLine[ nIndex ] == ' ') || (rCodeLine[ nIndex ] == '\t')) )
     747           4 :         ++nIndex;
     748           4 :     if( nIndex > 0 )
     749             :     {
     750           4 :         rCodeLine = rCodeLine.copy( nIndex );
     751           4 :         return true;
     752             :     }
     753           0 :     return false;
     754             : }
     755             : 
     756           6 : bool lclEatKeyword( OUString& rCodeLine, const OUString& rKeyword )
     757             : {
     758           6 :     if( rCodeLine.matchIgnoreAsciiCase( rKeyword ) )
     759             :     {
     760           4 :         rCodeLine = rCodeLine.copy( rKeyword.getLength() );
     761             :         // success, if code line ends after keyword, or if whitespace follows
     762           4 :         return rCodeLine.isEmpty() || lclEatWhitespace( rCodeLine );
     763             :     }
     764           2 :     return false;
     765             : }
     766             : 
     767             : } // namespace
     768             : 
     769           2 : VbaUserForm::VbaUserForm( const Reference< XComponentContext >& rxContext,
     770             :         const Reference< XModel >& rxDocModel, const GraphicHelper& rGraphicHelper, bool bDefaultColorBgr ) :
     771             :     mxContext( rxContext ),
     772             :     mxDocModel( rxDocModel ),
     773           2 :     maConverter( rxDocModel, rGraphicHelper, bDefaultColorBgr )
     774             : {
     775             :     OSL_ENSURE( mxContext.is(), "VbaUserForm::VbaUserForm - missing component context" );
     776             :     OSL_ENSURE( mxDocModel.is(), "VbaUserForm::VbaUserForm - missing document model" );
     777           2 : }
     778             : 
     779           2 : void VbaUserForm::importForm( const Reference< XNameContainer >& rxDialogLib,
     780             :                            StorageBase& rVbaFormStrg, const OUString& rModuleName, rtl_TextEncoding eTextEnc )
     781             : {
     782             :     OSL_ENSURE( rxDialogLib.is(), "VbaUserForm::importForm - missing dialog library" );
     783           2 :     if( !mxContext.is() || !mxDocModel.is() || !rxDialogLib.is() )
     784           0 :         return;
     785             : 
     786             :     // check that the '03VBFrame' stream exists, this is required for forms
     787           2 :     BinaryXInputStream aInStrm( rVbaFormStrg.openInputStream( "\003VBFrame" ), true );
     788             :     OSL_ENSURE( !aInStrm.isEof(), "VbaUserForm::importForm - missing \\003VBFrame stream" );
     789           2 :     if( aInStrm.isEof() )
     790           0 :         return;
     791             : 
     792             :     // scan for the line 'Begin {GUID} <FormName>'
     793           4 :     TextInputStream aFrameTextStrm( mxContext, aInStrm, eTextEnc );
     794           4 :     const OUString aBegin = "Begin";
     795           4 :     OUString aLine;
     796           2 :     bool bBeginFound = false;
     797           8 :     while( !bBeginFound && !aFrameTextStrm.isEof() )
     798             :     {
     799           4 :         aLine = aFrameTextStrm.readLine().trim();
     800           4 :         bBeginFound = lclEatKeyword( aLine, aBegin );
     801             :     }
     802             :     // check for the specific GUID that represents VBA forms
     803           2 :     if( !bBeginFound || !lclEatKeyword( aLine, "{C62A69F0-16DC-11CE-9E98-00AA00574A4F}" ) )
     804           0 :         return;
     805             : 
     806             :     // remaining line is the form name
     807           4 :     OUString aFormName = aLine.trim();
     808             :     OSL_ENSURE( !aFormName.isEmpty(), "VbaUserForm::importForm - missing form name" );
     809             :     OSL_ENSURE( rModuleName.equalsIgnoreAsciiCase( aFormName ), "VbaUserForm::importFrameStream - form and module name mismatch" );
     810           2 :     if( aFormName.isEmpty() )
     811           0 :         aFormName = rModuleName;
     812           2 :     if( aFormName.isEmpty() )
     813           0 :         return;
     814           2 :     mxSiteModel.reset( new VbaSiteModel );
     815           2 :     mxSiteModel->importProperty( XML_Name, aFormName );
     816             : 
     817             :     // read the form properties (caption is contained in this '03VBFrame' stream, not in the 'f' stream)
     818           2 :     mxCtrlModel.reset( new AxUserFormModel );
     819           4 :     OUString aKey, aValue;
     820           2 :     bool bExitLoop = false;
     821          20 :     while( !bExitLoop && !aFrameTextStrm.isEof() )
     822             :     {
     823          16 :         aLine = aFrameTextStrm.readLine().trim();
     824          16 :         bExitLoop = aLine.equalsIgnoreAsciiCase( "End" );
     825          16 :         if( !bExitLoop && VbaHelper::extractKeyValue( aKey, aValue, aLine ) )
     826             :         {
     827          14 :             if( aKey.equalsIgnoreAsciiCase( "Caption" ) )
     828           2 :                 mxCtrlModel->importProperty( XML_Caption, lclGetQuotedString( aValue ) );
     829          12 :             else if( aKey.equalsIgnoreAsciiCase( "Tag" ) )
     830           0 :                 mxSiteModel->importProperty( XML_Tag, lclGetQuotedString( aValue ) );
     831             :         }
     832             :     }
     833             : 
     834             :     // use generic container control functionality to import the embedded controls
     835           2 :     importStorage( rVbaFormStrg, AxClassTable() );
     836             : 
     837             :     try
     838             :     {
     839             :         // create the dialog model
     840           2 :         OUString aServiceName = mxCtrlModel->getServiceName();
     841           4 :         Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
     842           4 :         Reference< XControlModel > xDialogModel( xFactory->createInstance( aServiceName ), UNO_QUERY_THROW );
     843           4 :         Reference< XNameContainer > xDialogNC( xDialogModel, UNO_QUERY_THROW );
     844             : 
     845             :         // convert properties and embedded controls
     846           2 :         if( convertProperties( xDialogModel, maConverter, 0 ) )
     847             :         {
     848             :             // export the dialog to XML and insert it into the dialog library
     849           2 :             Reference< XInputStreamProvider > xDialogSource( ::xmlscript::exportDialogModel( xDialogNC, mxContext, mxDocModel ), UNO_SET_THROW );
     850             :             OSL_ENSURE( !rxDialogLib->hasByName( aFormName ), "VbaUserForm::importForm - multiple dialogs with equal name" );
     851           2 :             ContainerHelper::insertByName( rxDialogLib, aFormName, Any( xDialogSource ) );
     852           2 :         }
     853             :     }
     854           0 :     catch(const Exception& )
     855             :     {
     856           2 :     }
     857             : }
     858             : 
     859             : } // namespace ole
     860         246 : } // namespace oox
     861             : 
     862             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11