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