1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include <com/sun/star/awt/XControl.hpp> 25 #include <com/sun/star/awt/XControlContainer.hpp> 26 #include <com/sun/star/awt/FontWeight.hpp> 27 #include <com/sun/star/awt/FontSlant.hpp> 28 #include <com/sun/star/awt/FontStrikeout.hpp> 29 #include <com/sun/star/awt/FontUnderline.hpp> 30 #include <com/sun/star/container/XNameContainer.hpp> 31 #include <com/sun/star/script/XInvocation.hpp> 32 #include <com/sun/star/lang/WrappedTargetException.hpp> 33 34 #include "vbacontrols.hxx" 35 #include "vbacontrol.hxx" 36 #include <cppuhelper/implbase2.hxx> 37 #include <ooo/vba/XControlProvider.hpp> 38 #include <hash_map> 39 40 using namespace com::sun::star; 41 using namespace ooo::vba; 42 43 44 typedef ::cppu::WeakImplHelper2< container::XNameAccess, container::XIndexAccess > ArrayWrapImpl; 45 46 typedef std::hash_map< rtl::OUString, sal_Int32, ::rtl::OUStringHash, 47 ::std::equal_to< ::rtl::OUString > > ControlIndexMap; 48 typedef std::vector< uno::Reference< awt::XControl > > ControlVec; 49 50 class ControlArrayWrapper : public ArrayWrapImpl 51 { 52 uno::Reference< awt::XControlContainer > mxDialog; 53 uno::Sequence< ::rtl::OUString > msNames; 54 ControlVec mControls; 55 ControlIndexMap mIndices; 56 57 private: 58 void SetArrayElementTo( const uno::Reference< awt::XControl >& xCtrl, sal_Int32 nIndex = -1 ) 59 { 60 // initialize the element with specified index to the control 61 if ( xCtrl.is() ) 62 { 63 if ( nIndex == -1 ) 64 nIndex = msNames.getLength(); 65 66 if ( nIndex >= msNames.getLength() ) 67 msNames.realloc( nIndex ); 68 69 msNames[ nIndex ] = getControlName( xCtrl ); 70 mControls.push_back( xCtrl ); 71 mIndices[ msNames[ nIndex ] ] = nIndex; 72 } 73 } 74 75 public: 76 ControlArrayWrapper( const uno::Reference< awt::XControl >& xDialog ) 77 { 78 try 79 { 80 mxDialog.set( xDialog, uno::UNO_QUERY_THROW ); 81 uno::Sequence< uno::Reference< awt::XControl > > sXControls = mxDialog->getControls(); 82 83 msNames.realloc( sXControls.getLength() ); 84 for ( sal_Int32 i = 0; i < sXControls.getLength(); ++i ) 85 SetArrayElementTo( sXControls[ i ], i ); 86 } 87 catch( uno::Exception& ) 88 { 89 // accept the case when the dialog already does not exist 90 // in this case the wrapper should work in dummy mode 91 } 92 } 93 94 static rtl::OUString getControlName( const uno::Reference< awt::XControl >& xCtrl ) 95 { 96 if ( !xCtrl.is() ) 97 throw uno::RuntimeException(); 98 99 uno::Reference< beans::XPropertySet > xProp( xCtrl->getModel(), uno::UNO_QUERY_THROW ); 100 rtl::OUString sName; 101 xProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Name" ) ) ) >>= sName; 102 return sName; 103 } 104 105 106 // XElementAccess 107 virtual uno::Type SAL_CALL getElementType( ) 108 { 109 return awt::XControl::static_type(0); 110 } 111 112 virtual ::sal_Bool SAL_CALL hasElements( ) 113 { 114 return ( mControls.size() > 0 ); 115 } 116 117 // XNameAcess 118 virtual uno::Any SAL_CALL getByName( const ::rtl::OUString& aName ) 119 { 120 if ( !hasByName( aName ) ) 121 throw container::NoSuchElementException(); 122 return getByIndex( mIndices[ aName ] ); 123 } 124 125 virtual uno::Sequence< ::rtl::OUString > SAL_CALL getElementNames( ) 126 { 127 return msNames; 128 } 129 130 virtual ::sal_Bool SAL_CALL hasByName( const ::rtl::OUString& aName ) 131 { 132 ControlIndexMap::iterator it = mIndices.find( aName ); 133 return it != mIndices.end(); 134 } 135 136 // XElementAccess 137 virtual ::sal_Int32 SAL_CALL getCount( ) 138 { 139 return mControls.size(); 140 } 141 142 virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) 143 { 144 if ( Index < 0 || Index >= static_cast< sal_Int32 >( mControls.size() ) ) 145 throw lang::IndexOutOfBoundsException(); 146 return uno::makeAny( mControls[ Index ] ); 147 } 148 }; 149 150 151 class ControlsEnumWrapper : public EnumerationHelper_BASE 152 { 153 uno::Reference<XHelperInterface > m_xParent; 154 uno::Reference<uno::XComponentContext > m_xContext; 155 uno::Reference<container::XIndexAccess > m_xIndexAccess; 156 uno::Reference<awt::XControl > m_xDlg; 157 uno::Reference< frame::XModel > m_xModel; 158 double mfOffsetX; 159 double mfOffsetY; 160 sal_Int32 nIndex; 161 162 public: 163 164 ControlsEnumWrapper( 165 const uno::Reference< XHelperInterface >& xParent, 166 const uno::Reference< uno::XComponentContext >& xContext, 167 const uno::Reference< container::XIndexAccess >& xIndexAccess, 168 const uno::Reference< awt::XControl >& xDlg, 169 const uno::Reference< frame::XModel >& xModel, 170 double fOffsetX, double fOffsetY ) : 171 m_xParent( xParent ), 172 m_xContext( xContext), 173 m_xIndexAccess( xIndexAccess ), 174 m_xDlg( xDlg ), 175 m_xModel( xModel ), 176 mfOffsetX( fOffsetX ), 177 mfOffsetY( fOffsetY ), 178 nIndex( 0 ) {} 179 180 virtual ::sal_Bool SAL_CALL hasMoreElements( ) 181 { 182 return ( nIndex < m_xIndexAccess->getCount() ); 183 } 184 185 virtual uno::Any SAL_CALL nextElement( ) 186 { 187 if ( nIndex < m_xIndexAccess->getCount() ) 188 { 189 uno::Reference< awt::XControl > xControl; 190 m_xIndexAccess->getByIndex( nIndex++ ) >>= xControl; 191 192 uno::Reference< msforms::XControl > xVBAControl; 193 if ( xControl.is() && m_xDlg.is() ) 194 xVBAControl = ScVbaControlFactory::createUserformControl( m_xContext, xControl, m_xDlg, m_xModel, mfOffsetX, mfOffsetY ); 195 return uno::makeAny( xVBAControl ); 196 } 197 throw container::NoSuchElementException(); 198 } 199 200 }; 201 202 203 uno::Reference<container::XIndexAccess > 204 lcl_controlsWrapper( const uno::Reference< awt::XControl >& xDlg ) 205 { 206 return new ControlArrayWrapper( xDlg ); 207 } 208 209 ScVbaControls::ScVbaControls( 210 const uno::Reference< XHelperInterface >& xParent, 211 const uno::Reference< uno::XComponentContext >& xContext, 212 const css::uno::Reference< awt::XControl >& xDialog, 213 const uno::Reference< frame::XModel >& xModel, 214 double fOffsetX, double fOffsetY ) : 215 ControlsImpl_BASE( xParent, xContext, lcl_controlsWrapper( xDialog ) ), 216 mxDialog( xDialog ), 217 mxModel( xModel ), 218 mfOffsetX( fOffsetX ), 219 mfOffsetY( fOffsetY ) 220 { 221 } 222 223 uno::Reference< container::XEnumeration > 224 ScVbaControls::createEnumeration() 225 { 226 uno::Reference< container::XEnumeration > xEnum( new ControlsEnumWrapper( mxParent, mxContext, m_xIndexAccess, mxDialog, mxModel, mfOffsetX, mfOffsetY ) ); 227 if ( !xEnum.is() ) 228 throw uno::RuntimeException(); 229 return xEnum; 230 } 231 232 uno::Any 233 ScVbaControls::createCollectionObject( const css::uno::Any& aSource ) 234 { 235 // Create control from awt::XControl 236 uno::Reference< awt::XControl > xControl( aSource, uno::UNO_QUERY_THROW ); 237 uno::Reference< msforms::XControl > xVBAControl = ScVbaControlFactory::createUserformControl( mxContext, xControl, mxDialog, mxModel, mfOffsetX, mfOffsetY ); 238 return uno::Any( xVBAControl ); 239 } 240 241 void SAL_CALL 242 ScVbaControls::Move( double cx, double cy ) 243 { 244 uno::Reference< container::XEnumeration > xEnum( createEnumeration() ); 245 while ( xEnum->hasMoreElements() ) 246 { 247 uno::Reference< msforms::XControl > xControl( xEnum->nextElement(), uno::UNO_QUERY_THROW ); 248 xControl->setLeft( xControl->getLeft() + cx ); 249 xControl->setTop( xControl->getTop() + cy ); 250 } 251 } 252 253 uno::Any SAL_CALL ScVbaControls::Add( const uno::Any& Object, const uno::Any& StringKey, const uno::Any& /*Before*/, const uno::Any& /*After*/ ) 254 { 255 uno::Any aResult; 256 ::rtl::OUString aComServiceName; 257 258 try 259 { 260 if ( !mxDialog.is() ) 261 throw uno::RuntimeException(); 262 263 uno::Reference< awt::XControl > xNewControl; 264 uno::Reference< lang::XMultiServiceFactory > xModelFactory( mxDialog->getModel(), uno::UNO_QUERY_THROW ); 265 266 uno::Reference< container::XNameContainer > xDialogContainer( xModelFactory, uno::UNO_QUERY_THROW ); 267 268 Object >>= aComServiceName; 269 270 // TODO: Support Before and After? 271 ::rtl::OUString aNewName; 272 StringKey >>= aNewName; 273 if ( !aNewName.getLength() ) 274 { 275 aNewName = aComServiceName; 276 if ( !aNewName.getLength() ) 277 aNewName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Control" ) ); 278 279 sal_Int32 nInd = 0; 280 while( xDialogContainer->hasByName( aNewName ) && (nInd < SAL_MAX_INT32) ) 281 { 282 aNewName = aComServiceName; 283 aNewName += ::rtl::OUString::valueOf( nInd++ ); 284 } 285 } 286 287 double fDefWidth = 72.0, fDefHeight = 18.0; 288 if ( aComServiceName.getLength() ) 289 { 290 // create a UNO control model based on the passed control type 291 uno::Reference< awt::XControlModel > xNewModel; 292 bool bFontSupport = false; 293 bool bNativeAX = false; 294 if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.CommandButton.1" ) ) ) 295 { 296 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlButtonModel" ) ) ), uno::UNO_QUERY_THROW ); 297 fDefWidth = 72.0; fDefHeight = 24.0; 298 bFontSupport = true; 299 } 300 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.Label.1" ) ) ) 301 { 302 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlFixedTextModel" ) ) ), uno::UNO_QUERY_THROW ); 303 fDefWidth = 72.0; fDefHeight = 18.0; 304 bFontSupport = true; 305 } 306 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.Image.1" ) ) ) 307 { 308 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlImageControlModel" ) ) ), uno::UNO_QUERY_THROW ); 309 fDefWidth = 72.0; fDefHeight = 72.0; 310 } 311 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.CheckBox.1" ) ) ) 312 { 313 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlCheckBoxModel" ) ) ), uno::UNO_QUERY_THROW ); 314 fDefWidth = 108.0; fDefHeight = 18.0; 315 bFontSupport = true; 316 } 317 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.OptionButton.1" ) ) ) 318 { 319 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlRadioButtonModel" ) ) ), uno::UNO_QUERY_THROW ); 320 fDefWidth = 108.0; fDefHeight = 18.0; 321 bFontSupport = true; 322 } 323 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.TextBox.1" ) ) ) 324 { 325 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlEditModel" ) ) ), uno::UNO_QUERY_THROW ); 326 fDefWidth = 72.0; fDefHeight = 18.0; 327 bFontSupport = true; 328 } 329 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ListBox.1" ) ) ) 330 { 331 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlListBoxModel" ) ) ), uno::UNO_QUERY_THROW ); 332 fDefWidth = 72.0; fDefHeight = 18.0; 333 bFontSupport = true; 334 } 335 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ComboBox.1" ) ) ) 336 { 337 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlComboBoxModel" ) ) ), uno::UNO_QUERY_THROW ); 338 uno::Reference< beans::XPropertySet > xProps( xNewModel, uno::UNO_QUERY_THROW ); 339 xProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Dropdown" ) ), uno::Any( true ) ); 340 fDefWidth = 72.0; fDefHeight = 18.0; 341 bFontSupport = true; 342 } 343 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ToggleButton.1" ) ) ) 344 { 345 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlButtonModel" ) ) ), uno::UNO_QUERY_THROW ); 346 uno::Reference< beans::XPropertySet > xProps( xNewModel, uno::UNO_QUERY_THROW ); 347 xProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Toggle" ) ), uno::Any( true ) ); 348 fDefWidth = 72.0; fDefHeight = 18.0; 349 bFontSupport = true; 350 } 351 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.Frame.1" ) ) ) 352 { 353 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlGroupBoxModel" ) ) ), uno::UNO_QUERY_THROW ); 354 fDefWidth = 216.0; fDefHeight = 144.0; 355 bFontSupport = true; 356 } 357 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.SpinButton.1" ) ) ) 358 { 359 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlSpinButtonModel" ) ) ), uno::UNO_QUERY_THROW ); 360 fDefWidth = 12.75; fDefHeight = 25.5; 361 } 362 else if( aComServiceName.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ScrollBar.1" ) ) ) 363 { 364 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.UnoControlScrollBarModel" ) ) ), uno::UNO_QUERY_THROW ); 365 fDefWidth = 12.75; fDefHeight = 63.8; 366 } 367 else 368 { 369 xNewModel.set( xModelFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.custom.awt.UnoControlSystemAXContainerModel" ) ) ), uno::UNO_QUERY_THROW ); 370 fDefWidth = 72.0; fDefHeight = 18.0; 371 bNativeAX = true; 372 } 373 374 // need to set a few font properties to get rid of the default DONT_KNOW values 375 if( bFontSupport ) 376 { 377 uno::Reference< beans::XPropertySet > xModelProps( xNewModel, uno::UNO_QUERY_THROW ); 378 xModelProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FontName" ) ), uno::Any( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Tahoma" ) ) ) ); 379 xModelProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FontHeight" ) ), uno::Any( float( 8.0 ) ) ); 380 xModelProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FontWeight" ) ), uno::Any( awt::FontWeight::NORMAL ) ); 381 xModelProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FontSlant" ) ), uno::Any( awt::FontSlant_NONE ) ); 382 xModelProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FontUnderline" ) ), uno::Any( awt::FontUnderline::NONE ) ); 383 xModelProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FontStrikeout" ) ), uno::Any( awt::FontStrikeout::NONE ) ); 384 } 385 386 xDialogContainer->insertByName( aNewName, uno::makeAny( xNewModel ) ); 387 uno::Reference< awt::XControlContainer > xControlContainer( mxDialog, uno::UNO_QUERY_THROW ); 388 xNewControl = xControlContainer->getControl( aNewName ); 389 390 if( bNativeAX ) try 391 { 392 uno::Reference< script::XInvocation > xControlInvoke( xNewControl, uno::UNO_QUERY_THROW ); 393 394 uno::Sequence< uno::Any > aArgs( 1 ); 395 aArgs[0] <<= aComServiceName; 396 uno::Sequence< sal_Int16 > aOutIDDummy; 397 uno::Sequence< uno::Any > aOutDummy; 398 xControlInvoke->invoke( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SOAddAXControl" ) ), aArgs, aOutIDDummy, aOutDummy ); 399 } 400 catch( uno::Exception& ) 401 { 402 xDialogContainer->removeByName( aNewName ); 403 throw; 404 } 405 } 406 407 if ( xNewControl.is() ) 408 { 409 UpdateCollectionIndex( lcl_controlsWrapper( mxDialog ) ); 410 aResult <<= xNewControl; 411 aResult = createCollectionObject( aResult ); 412 uno::Reference< msforms::XControl > xVBAControl( aResult, uno::UNO_QUERY_THROW ); 413 if( fDefWidth > 0.0 ) 414 xVBAControl->setWidth( fDefWidth ); 415 if( fDefHeight > 0.0 ) 416 xVBAControl->setHeight( fDefHeight ); 417 } 418 else 419 throw uno::RuntimeException(); 420 } 421 catch( uno::RuntimeException& ) 422 { 423 throw; 424 } 425 catch( uno::Exception& e ) 426 { 427 throw lang::WrappedTargetException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Can not create AXControl!" ) ), 428 uno::Reference< uno::XInterface >(), 429 uno::makeAny( e ) ); 430 } 431 432 return aResult; 433 } 434 435 void SAL_CALL ScVbaControls::Remove( const uno::Any& StringKeyOrIndex ) 436 { 437 ::rtl::OUString aControlName; 438 sal_Int32 nIndex = -1; 439 440 try 441 { 442 if ( !mxDialog.is() ) 443 throw uno::RuntimeException(); 444 445 uno::Reference< lang::XMultiServiceFactory > xModelFactory( mxDialog->getModel(), uno::UNO_QUERY_THROW ); 446 uno::Reference< container::XNameContainer > xDialogContainer( xModelFactory, uno::UNO_QUERY_THROW ); 447 448 if ( !( ( StringKeyOrIndex >>= aControlName ) && aControlName.getLength() ) 449 && !( ( StringKeyOrIndex >>= nIndex ) && nIndex >= 0 && nIndex < m_xIndexAccess->getCount() ) ) 450 throw uno::RuntimeException(); 451 452 uno::Reference< awt::XControl > xControl; 453 if ( aControlName.getLength() ) 454 { 455 uno::Reference< awt::XControlContainer > xControlContainer( mxDialog, uno::UNO_QUERY_THROW ); 456 xControl = xControlContainer->getControl( aControlName ); 457 } 458 else 459 { 460 m_xIndexAccess->getByIndex( nIndex ) >>= xControl; 461 } 462 463 if ( !xControl.is() ) 464 throw uno::RuntimeException(); 465 466 if ( !aControlName.getLength() ) 467 aControlName = ControlArrayWrapper::getControlName( xControl ); 468 469 xDialogContainer->removeByName( aControlName ); 470 xControl->dispose(); 471 } 472 catch( uno::RuntimeException& ) 473 { 474 // the exceptions are not rethrown, impossibility to find or remove the control is currently not reported 475 // since in most cases it means just that the controls is already not there, the VBA seems to do it in the same way 476 477 // throw; 478 } 479 catch( uno::Exception& e ) 480 { 481 // throw lang::WrappedTargetException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Can not create AXControl!" ) ), 482 // uno::Reference< uno::XInterface >(), 483 // uno::makeAny( e ) ); 484 } 485 } 486 487 488 uno::Type 489 ScVbaControls::getElementType() 490 { 491 return ooo::vba::msforms::XControl::static_type(0); 492 } 493 494 VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaControls, "ooo.vba.msforms.Controls" ) 495