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