/** Collects names of all controls in a user form or container control. Allows togenerateunusednamesfordummycontrolsseparatingoptiongroups.
*/ class VbaControlNamesSet
{ public: explicit VbaControlNamesSet();
/** Inserts the name of the passed control. */ void insertName( const VbaFormControl& rControl ); /** Returns a name that is not contained in this set. */
OUString generateDummyName();
/** Functor that inserts the name of a control into a VbaControlNamesSet. */ struct VbaControlNameInserter
{ public:
VbaControlNamesSet& mrCtrlNames; explicit VbaControlNameInserter( VbaControlNamesSet& rCtrlNames ) : mrCtrlNames( rCtrlNames ) {} voidoperator()( const VbaFormControl& rControl ) { mrCtrlNames.insertName( rControl ); }
};
/** A dummy invisible form control (fixed label without text) that is used to separatetwogroupsofoptionbuttons.
*/ class VbaDummyFormControl : public VbaFormControl
{ public: explicit VbaDummyFormControl( const OUString& rName );
};
sal_Int32 nTypeIndex = static_cast< sal_Int32 >( mnClassIdOrCache & VBA_SITE_INDEXMASK ); if( !getFlag( mnClassIdOrCache, VBA_SITE_CLASSIDINDEX ) )
{ switch( nTypeIndex )
{ case VBA_SITE_COMMANDBUTTON: xCtrlModel= std::make_shared<AxCommandButtonModel>(); break; case VBA_SITE_LABEL: xCtrlModel= std::make_shared<AxLabelModel>(); break; case VBA_SITE_IMAGE: xCtrlModel= std::make_shared<AxImageModel>(); break; case VBA_SITE_TOGGLEBUTTON: xCtrlModel= std::make_shared<AxToggleButtonModel>(); break; case VBA_SITE_CHECKBOX: xCtrlModel= std::make_shared<AxCheckBoxModel>(); break; case VBA_SITE_OPTIONBUTTON: xCtrlModel= std::make_shared<AxOptionButtonModel>(); break; case VBA_SITE_TEXTBOX: xCtrlModel= std::make_shared<AxTextBoxModel>(); break; case VBA_SITE_LISTBOX: xCtrlModel= std::make_shared<AxListBoxModel>(); break; case VBA_SITE_COMBOBOX: xCtrlModel= std::make_shared<AxComboBoxModel>(); break; case VBA_SITE_SPINBUTTON: xCtrlModel= std::make_shared<AxSpinButtonModel>(); break; case VBA_SITE_SCROLLBAR: xCtrlModel= std::make_shared<AxScrollBarModel>(); break; case VBA_SITE_TABSTRIP: xCtrlModel= std::make_shared<AxTabStripModel>(); break; case VBA_SITE_FRAME: xCtrlModel= std::make_shared<AxFrameModel>(); break; case VBA_SITE_MULTIPAGE: xCtrlModel= std::make_shared<AxMultiPageModel>(); break; case VBA_SITE_FORM: xCtrlModel= std::make_shared<AxPageModel>(); break; default: OSL_FAIL( "VbaSiteModel::createControlModel - unknown type index" );
}
} else
{ const OUString* pGuid = ContainerHelper::getVectorElement( rClassTable, nTypeIndex );
OSL_ENSURE( pGuid, "VbaSiteModel::createControlModel - invalid class table index" ); if( pGuid )
{ if( *pGuid == COMCTL_GUID_SCROLLBAR_60 )
xCtrlModel = std::make_shared<ComCtlScrollBarModel>( 6 ); elseif( *pGuid == COMCTL_GUID_PROGRESSBAR_50 )
xCtrlModel = std::make_shared<ComCtlProgressBarModel>( 5 ); elseif( *pGuid == COMCTL_GUID_PROGRESSBAR_60 )
xCtrlModel = std::make_shared<ComCtlProgressBarModel>( 6 );
}
}
if( xCtrlModel )
{ // user form controls are AWT models
xCtrlModel->setAwtModelMode();
// check that container model matches container flag in site data bool bModelIsContainer = dynamic_cast< const AxContainerModelBase* >( xCtrlModel.get() ) != nullptr; bool bTypeMatch = bModelIsContainer == isContainer();
OSL_ENSURE( bTypeMatch, "VbaSiteModel::createControlModel - container type does not match container flag" ); if( !bTypeMatch )
xCtrlModel.reset();
} return xCtrlModel;
}
/* Open the 'f' stream containing the model of this control and a list
of site models for all child controls. */
BinaryXInputStream aFStrm( rStrg.openInputStream( u"f"_ustr ), true );
OSL_ENSURE( !aFStrm.isEof(), "VbaFormControl::importStorage - missing 'f' stream" );
/* Read the properties of this container control and the class table (intothemaClassTablevector)containingalistofGUIDsfor
exotic embedded controls. */ if( !(!aFStrm.isEof() && pContainerModel->importBinaryModel( aFStrm ) && pContainerModel->importClassTable( aFStrm, maClassTable )) ) return;
/* Read the site models of all embedded controls (this fills the maControlsvector).IgnorefailureofimportSiteModels()but
try to import as much controls as possible. */
importEmbeddedSiteModels( aFStrm ); /* Open the 'o' stream containing models of embedded simple controls.Streammaybeemptyormissing,ifthiscontrol
contains no controls or only container controls. */
BinaryXInputStream aOStrm( rStrg.openInputStream( u"o"_ustr ), true );
/* Iterate over all embedded controls, import model from 'o' stream(forembeddedsimplecontrols)orfromthesubstorage
(for embedded container controls). */
maControls.forEachMem( &VbaFormControl::importModelOrStorage,
::std::ref( aOStrm ), ::std::ref( rStrg ), ::std::cref( maClassTable ) );
// Special handling for multi-page which has non-standard // containment and additionally needs to re-order Page children if ( pContainerModel->getControlType() == API_CONTROL_MULTIPAGE )
{
AxMultiPageModel* pMultiPage = dynamic_cast< AxMultiPageModel* >( pContainerModel );
assert(pMultiPage);
{
BinaryXInputStream aXStrm( rStrg.openInputStream( u"x"_ustr ), true );
pMultiPage->importPageAndMultiPageProperties( aXStrm, maControls.size() );
} typedef std::unordered_map< sal_uInt32, std::shared_ptr< VbaFormControl > > IdToPageMap;
IdToPageMap idToPage;
AxArrayString sCaptions;
for (autoconst& control : maControls)
{ auto& elem = control->mxCtrlModel; if (!elem)
{
SAL_WARN("oox", "empty control model"); continue;
} if (elem->getControlType() == API_CONTROL_PAGE)
{
VbaSiteModelRef xPageSiteRef = control->mxSiteModel; if ( xPageSiteRef )
idToPage[ xPageSiteRef->getId() ] = control;
} elseif (elem->getControlType() == API_CONTROL_TABSTRIP)
{
AxTabStripModel* pTabStrip = static_cast<AxTabStripModel*>(elem.get());
sCaptions = pTabStrip->maItems;
pMultiPage->mnActiveTab = pTabStrip->mnListIndex;
pMultiPage->mnTabStyle = pTabStrip->mnTabStyle;
} else
{
SAL_WARN("oox", "unexpected control type " << elem->getControlType());
}
} // apply caption/titles to pages
maControls.clear(); // need to sort the controls according to the order of the ids if ( sCaptions.size() == idToPage.size() )
{
AxArrayString::iterator itCaption = sCaptions.begin(); for ( constauto& rCtrlId : pMultiPage->mnIDs )
{
IdToPageMap::iterator iter = idToPage.find( rCtrlId ); if ( iter != idToPage.end() )
{
AxPageModel* pPage = static_cast<AxPageModel*> ( iter->second->mxCtrlModel.get() );
pPage->importProperty( XML_Caption, *itCaption );
maControls.push_back( iter->second );
}
++itCaption;
}
}
} /* Reorder the controls (sorts all option buttons of an option grouptogether),andmoveallchildrenofallembeddedframes (groupboxes)tothiscontrol(UNOgroupboxescannotcontain
other controls). */
finalizeEmbeddedControls();
}
// create and convert all embedded controls if( !maControls.empty() ) try
{
Reference< XNameContainer > xCtrlModelNC( rxCtrlModel, UNO_QUERY_THROW ); /* Call conversion for all controls. Pass vector index as new
tab order to make option button groups work correctly. */
maControls.forEachMemWithIndex( &VbaFormControl::createAndConvert,
::std::cref( xCtrlModelNC ), ::std::cref( rConv ) );
} catch(const Exception& )
{
OSL_FAIL( "VbaFormControl::convertProperties - cannot get control container interface" );
}
void VbaFormControl::createControlModel( const AxClassTable& rClassTable )
{ // derived classes may have created their own control model if( !mxCtrlModel && mxSiteModel )
mxCtrlModel = mxSiteModel->createControlModel( rClassTable );
}
// skip the site info structure
sal_uInt32 nSiteIndex = 0; while( !rInStrm.isEof() && (nSiteIndex < nSiteCount) )
{
rInStrm.skip( 1 ); // site depth
sal_uInt8 nTypeCount = rInStrm.readuInt8(); // 'type-or-count' byte if( getFlag( nTypeCount, VBA_SITEINFO_COUNT ) )
{ /* Count flag is set: the 'type-or-count' byte contains the number ofcontrolsinthelowerbits,thetypespecifierfollowsin thenextbyte.Thetypespecifiershouldalwaysbe1according
to the specification. */
rInStrm.skip( 1 );
nSiteIndex += (nTypeCount & VBA_SITEINFO_MASK);
} else
{ /* Count flag is not set: the 'type-or-count' byte contains the typespecifierof*one*controlinthelowerbits(thistype
should be 1, see above). */
++nSiteIndex;
}
} // align the stream to 32bit, relative to start of entire site info
rInStrm.alignToBlock( 4, nAnchorPos );
// import the site models for all embedded controls
maControls.clear(); bool bValid = !rInStrm.isEof(); for( nSiteIndex = 0; bValid && (nSiteIndex < nSiteCount); ++nSiteIndex )
{
VbaFormControlRef xControl = std::make_shared<VbaFormControl>();
maControls.push_back( xControl );
bValid = xControl->importSiteModel( rInStrm );
}
rInStrm.seek( nSiteEndPos );
}
void VbaFormControl::finalizeEmbeddedControls()
{ /* This function performs two tasks:
// first, sort all controls by original tab index
::std::sort( maControls.begin(), maControls.end(), &compareByTabIndex );
/* Collect the programmatical names of all embedded controls (needed to be abletosetunusednamestonewdummycontrolscreatedbelow).Also collectthenamesofallchildrenofembeddedframes(groupboxes). Luckily,namesofcontrolsmustbeuniqueintheentireform,notjust
in the current container. */
VbaControlNamesSet aControlNames;
VbaControlNameInserter aInserter( aControlNames );
maControls.forEach( aInserter ); for (autoconst& control : maControls) if( control->mxCtrlModel && (control->mxCtrlModel->getControlType() == API_CONTROL_GROUPBOX) )
control->maControls.forEach( aInserter );
/* Reprocess the sorted list and collect all option button controls that arepartofthesameoptiongroup(determinedbygroupname).All controlswillbestoredinavectorofvectors,thatcollectsevery optionbuttongroupinonevectorelement,andothercontrolsbetween theseoptiongroups(orleadingortrailingcontrols)inothervector elements.Ifanoptionbuttongroupfollowsanothergroup,adummy
separator control has to be inserted. */ typedef RefVector< VbaFormControlVector > VbaFormControlVectorVector;
VbaFormControlVectorVector aControlGroups;
typedef VbaFormControlVectorMap::mapped_type VbaFormControlVectorRef; bool bLastWasOptionButton = false; for (autoconst& control : maControls)
{ const ControlModelBase* pCtrlModel = control->mxCtrlModel.get();
if( const AxOptionButtonModel* pOptButtonModel = dynamic_cast< const AxOptionButtonModel* >( pCtrlModel ) )
{ // check if a new option group needs to be created const OUString& rGroupName = pOptButtonModel->getGroupName();
VbaFormControlVectorRef& rxOptionGroup = aOptionGroups[ rGroupName ]; if( !rxOptionGroup )
{ /* If last control was an option button too, we have two optiongroupsfollowingeachother,soadummyseparator
control is needed. */ if( bLastWasOptionButton )
{
VbaFormControlVectorRef xDummyGroup = std::make_shared<VbaFormControlVector>();
aControlGroups.push_back( xDummyGroup );
OUString aName = aControlNames.generateDummyName();
VbaFormControlRef xDummyControl = std::make_shared<VbaDummyFormControl>( aName );
xDummyGroup->push_back( xDummyControl );
}
rxOptionGroup = std::make_shared<VbaFormControlVector>();
aControlGroups.push_back( rxOptionGroup );
} /* Append the option button to the control group (which is now referredbythevectoraControlGroupsandbythemap
aOptionGroups). */
rxOptionGroup->push_back(control);
bLastWasOptionButton = true;
} else
{ // open a new control group, if the last group is an option group if( bLastWasOptionButton || aControlGroups.empty() )
{
VbaFormControlVectorRef xControlGroup = std::make_shared<VbaFormControlVector>();
aControlGroups.push_back( xControlGroup );
} // append the control to the last control group
VbaFormControlVector& rLastGroup = *aControlGroups.back();
rLastGroup.push_back(control);
bLastWasOptionButton = false;
// if control is a group box, move all its children to this control if( pCtrlModel && (pCtrlModel->getControlType() == API_CONTROL_GROUPBOX) )
{ /* Move all embedded controls of the group box relative to the
position of the group box. */
control->moveEmbeddedToAbsoluteParent(); /* Insert all children of the group box into the last control
group (following the group box). */
rLastGroup.insert( rLastGroup.end(), control->maControls.begin(), control->maControls.end() );
control->maControls.clear(); // check if last control of the group box is an option button
bLastWasOptionButton = dynamic_cast< const AxOptionButtonModel* >( rLastGroup.back()->mxCtrlModel.get() ) != nullptr;
}
}
}
// flatten the vector of vectors of form controls to a single vector
maControls.clear(); for (autoconst& controlGroup : aControlGroups)
maControls.insert( maControls.end(), controlGroup->begin(), controlGroup->end() );
}
// distance to move is equal to position of this control in its parent
AxPairData aDistance = mxSiteModel->getPosition();
/* For group boxes: add half of the font height to Y position (VBA
positions relative to frame border line, not to 'top' of frame). */ const AxFontDataModel* pFontModel = dynamic_cast< const AxFontDataModel* >( mxCtrlModel.get() ); if( pFontModel && (pFontModel->getControlType() == API_CONTROL_GROUPBOX) )
{
sal_Int32 nFontHeight = convertPointToMm100(pFontModel->getFontHeight());
aDistance.second += nFontHeight / 2;
}
// check that the '03VBFrame' stream exists, this is required for forms
BinaryXInputStream aInStrm( rVbaFormStrg.openInputStream( u"\003VBFrame"_ustr ), true);
OSL_ENSURE( !aInStrm.isEof(), "VbaUserForm::importForm - missing \\003VBFrame stream" ); if( aInStrm.isEof() ) return;
// scan for the line 'Begin {GUID} <FormName>'
TextInputStream aFrameTextStrm( mxContext, aInStrm, eTextEnc ); static constexpr OUStringLiteral aBegin = u"Begin";
OUString aLine; bool bBeginFound = false; while( !bBeginFound && !aFrameTextStrm.isEof() )
{
aLine = aFrameTextStrm.readLine().trim();
bBeginFound = lclEatKeyword( aLine, aBegin );
} // check for the specific GUID that represents VBA forms if( !bBeginFound || !lclEatKeyword( aLine, u"{C62A69F0-16DC-11CE-9E98-00AA00574A4F}" ) ) return;
// remaining line is the form name
OUString aFormName = aLine.trim();
OSL_ENSURE( !aFormName.isEmpty(), "VbaUserForm::importForm - missing form name" );
OSL_ENSURE( rModuleName.equalsIgnoreAsciiCase( aFormName ), "VbaUserForm::importFrameStream - form and module name mismatch" ); if( aFormName.isEmpty() )
aFormName = rModuleName; if( aFormName.isEmpty() ) return;
mxSiteModel = std::make_shared<VbaSiteModel>();
mxSiteModel->importProperty( XML_Name, aFormName );
// read the form properties (caption is contained in this '03VBFrame' stream, not in the 'f' stream)
mxCtrlModel = std::make_shared<AxUserFormModel>();
OUString aKey, aValue; bool bExitLoop = false; while( !bExitLoop && !aFrameTextStrm.isEof() )
{
aLine = aFrameTextStrm.readLine().trim();
bExitLoop = aLine.equalsIgnoreAsciiCase( "End" ); if( !bExitLoop && VbaHelper::extractKeyValue( aKey, aValue, aLine ) )
{ if( aKey.equalsIgnoreAsciiCase( "Caption" ) )
mxCtrlModel->importProperty( XML_Caption, lclGetQuotedString( aValue ) ); elseif( aKey.equalsIgnoreAsciiCase( "Tag" ) )
mxSiteModel->importProperty( XML_Tag, lclGetQuotedString( aValue ) );
}
}
// use generic container control functionality to import the embedded controls
importStorage( rVbaFormStrg, AxClassTable() );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.