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: */
|