1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/awt/XControl.hpp>
21 #include <com/sun/star/awt/XControlContainer.hpp>
22 #include <com/sun/star/awt/FontWeight.hpp>
23 #include <com/sun/star/awt/FontSlant.hpp>
24 #include <com/sun/star/awt/FontStrikeout.hpp>
25 #include <com/sun/star/awt/FontUnderline.hpp>
26 #include <com/sun/star/container/XNameContainer.hpp>
27 #include <com/sun/star/script/XInvocation.hpp>
28 #include <com/sun/star/lang/WrappedTargetException.hpp>
30 #include "vbacontrols.hxx"
31 #include "vbacontrol.hxx"
32 #include <cppuhelper/implbase2.hxx>
33 #include <ooo/vba/XControlProvider.hpp>
34 #include <boost/unordered_map.hpp>
36 using namespace com::sun::star
;
37 using namespace ooo::vba
;
40 typedef ::cppu::WeakImplHelper2
< container::XNameAccess
, container::XIndexAccess
> ArrayWrapImpl
;
42 typedef boost::unordered_map
< OUString
, sal_Int32
, OUStringHash
,
43 ::std::equal_to
< OUString
> > ControlIndexMap
;
44 typedef std::vector
< uno::Reference
< awt::XControl
> > ControlVec
;
46 class ControlArrayWrapper
: public ArrayWrapImpl
48 uno::Reference
< awt::XControlContainer
> mxDialog
;
49 uno::Sequence
< OUString
> msNames
;
51 ControlIndexMap mIndices
;
54 void SetArrayElementTo( const uno::Reference
< awt::XControl
>& xCtrl
, sal_Int32 nIndex
= -1 )
56 // initialize the element with specified index to the control
60 nIndex
= msNames
.getLength();
62 if ( nIndex
>= msNames
.getLength() )
63 msNames
.realloc( nIndex
);
65 msNames
[ nIndex
] = getControlName( xCtrl
);
66 mControls
.push_back( xCtrl
);
67 mIndices
[ msNames
[ nIndex
] ] = nIndex
;
70 void getNestedControls( ControlVec
& vControls
, uno::Reference
< awt::XControlContainer
>& xContainer
)
72 uno::Sequence
< uno::Reference
< awt::XControl
> > aControls
= xContainer
->getControls();
73 const uno::Reference
< awt::XControl
>* pCtrl
= aControls
.getConstArray();
74 const uno::Reference
< awt::XControl
>* pCtrlsEnd
= pCtrl
+ aControls
.getLength();
75 for ( ; pCtrl
< pCtrlsEnd
; ++pCtrl
)
77 uno::Reference
< awt::XControlContainer
> xC( *pCtrl
, uno::UNO_QUERY
);
78 vControls
.push_back( *pCtrl
);
80 getNestedControls( vControls
, xC
);
84 ControlArrayWrapper( const uno::Reference
< awt::XControl
>& xDialog
)
88 mxDialog
.set( xDialog
, uno::UNO_QUERY_THROW
);
89 uno::Sequence
< uno::Reference
< awt::XControl
> > sXControls
= mxDialog
->getControls();
91 msNames
.realloc( sXControls
.getLength() );
92 for ( sal_Int32 i
= 0; i
< sXControls
.getLength(); ++i
)
93 SetArrayElementTo( sXControls
[ i
], i
);
95 catch (const uno::Exception
&)
97 // accept the case when the dialog already does not exist
98 // in this case the wrapper should work in dummy mode
102 static OUString
getControlName( const uno::Reference
< awt::XControl
>& xCtrl
)
105 throw uno::RuntimeException();
107 uno::Reference
< beans::XPropertySet
> xProp( xCtrl
->getModel(), uno::UNO_QUERY_THROW
);
109 xProp
->getPropertyValue( "Name" ) >>= sName
;
115 virtual uno::Type SAL_CALL
getElementType( ) throw (uno::RuntimeException
)
117 return awt::XControl::static_type(0);
120 virtual ::sal_Bool SAL_CALL
hasElements( ) throw (uno::RuntimeException
)
122 return ( !mControls
.empty() );
126 virtual uno::Any SAL_CALL
getByName( const OUString
& aName
) throw (container::NoSuchElementException
, lang::WrappedTargetException
, uno::RuntimeException
)
128 if ( !hasByName( aName
) )
129 throw container::NoSuchElementException();
130 return getByIndex( mIndices
[ aName
] );
133 virtual uno::Sequence
< OUString
> SAL_CALL
getElementNames( ) throw (uno::RuntimeException
)
138 virtual ::sal_Bool SAL_CALL
hasByName( const OUString
& aName
) throw (css::uno::RuntimeException
)
140 ControlIndexMap::iterator it
= mIndices
.find( aName
);
141 return it
!= mIndices
.end();
145 virtual ::sal_Int32 SAL_CALL
getCount( ) throw (css::uno::RuntimeException
)
147 return mControls
.size();
150 virtual uno::Any SAL_CALL
getByIndex( ::sal_Int32 Index
) throw (lang::IndexOutOfBoundsException
, lang::WrappedTargetException
, uno::RuntimeException
)
152 if ( Index
< 0 || Index
>= static_cast< sal_Int32
>( mControls
.size() ) )
153 throw lang::IndexOutOfBoundsException();
154 return uno::makeAny( mControls
[ Index
] );
159 class ControlsEnumWrapper
: public EnumerationHelper_BASE
161 uno::Reference
<XHelperInterface
> m_xParent
;
162 uno::Reference
<uno::XComponentContext
> m_xContext
;
163 uno::Reference
<container::XIndexAccess
> m_xIndexAccess
;
164 uno::Reference
<awt::XControl
> m_xDlg
;
165 uno::Reference
< frame::XModel
> m_xModel
;
173 const uno::Reference
< XHelperInterface
>& xParent
,
174 const uno::Reference
< uno::XComponentContext
>& xContext
,
175 const uno::Reference
< container::XIndexAccess
>& xIndexAccess
,
176 const uno::Reference
< awt::XControl
>& xDlg
,
177 const uno::Reference
< frame::XModel
>& xModel
,
178 double fOffsetX
, double fOffsetY
) :
179 m_xParent( xParent
),
180 m_xContext( xContext
),
181 m_xIndexAccess( xIndexAccess
),
184 mfOffsetX( fOffsetX
),
185 mfOffsetY( fOffsetY
),
188 virtual ::sal_Bool SAL_CALL
hasMoreElements( ) throw (uno::RuntimeException
)
190 return ( nIndex
< m_xIndexAccess
->getCount() );
193 virtual uno::Any SAL_CALL
nextElement( ) throw (container::NoSuchElementException
, lang::WrappedTargetException
, uno::RuntimeException
)
195 if ( nIndex
< m_xIndexAccess
->getCount() )
197 uno::Reference
< awt::XControl
> xControl
;
198 m_xIndexAccess
->getByIndex( nIndex
++ ) >>= xControl
;
200 uno::Reference
< msforms::XControl
> xVBAControl
;
201 if ( xControl
.is() && m_xDlg
.is() )
202 xVBAControl
= ScVbaControlFactory::createUserformControl( m_xContext
, xControl
, m_xDlg
, m_xModel
, mfOffsetX
, mfOffsetY
);
203 return uno::makeAny( xVBAControl
);
205 throw container::NoSuchElementException();
211 static uno::Reference
<container::XIndexAccess
>
212 lcl_controlsWrapper( const uno::Reference
< awt::XControl
>& xDlg
)
214 return new ControlArrayWrapper( xDlg
);
217 ScVbaControls::ScVbaControls(
218 const uno::Reference
< XHelperInterface
>& xParent
,
219 const uno::Reference
< uno::XComponentContext
>& xContext
,
220 const css::uno::Reference
< awt::XControl
>& xDialog
,
221 const uno::Reference
< frame::XModel
>& xModel
,
222 double fOffsetX
, double fOffsetY
) :
223 ControlsImpl_BASE( xParent
, xContext
, lcl_controlsWrapper( xDialog
) ),
226 mfOffsetX( fOffsetX
),
227 mfOffsetY( fOffsetY
)
231 uno::Reference
< container::XEnumeration
>
232 ScVbaControls::createEnumeration() throw (uno::RuntimeException
)
234 uno::Reference
< container::XEnumeration
> xEnum( new ControlsEnumWrapper( mxParent
, mxContext
, m_xIndexAccess
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
) );
236 throw uno::RuntimeException();
241 ScVbaControls::createCollectionObject( const css::uno::Any
& aSource
)
243 // Create control from awt::XControl
244 uno::Reference
< awt::XControl
> xControl( aSource
, uno::UNO_QUERY_THROW
);
245 uno::Reference
< msforms::XControl
> xVBAControl
= ScVbaControlFactory::createUserformControl( mxContext
, xControl
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
);
246 return uno::Any( xVBAControl
);
250 ScVbaControls::Move( double cx
, double cy
) throw (uno::RuntimeException
)
252 uno::Reference
< container::XEnumeration
> xEnum( createEnumeration() );
253 while ( xEnum
->hasMoreElements() )
255 uno::Reference
< msforms::XControl
> xControl( xEnum
->nextElement(), uno::UNO_QUERY_THROW
);
256 xControl
->setLeft( xControl
->getLeft() + cx
);
257 xControl
->setTop( xControl
->getTop() + cy
);
261 uno::Any SAL_CALL
ScVbaControls::Add( const uno::Any
& Object
, const uno::Any
& StringKey
, const uno::Any
& /*Before*/, const uno::Any
& /*After*/ )
262 throw (uno::RuntimeException
)
265 OUString aComServiceName
;
269 if ( !mxDialog
.is() )
270 throw uno::RuntimeException();
272 uno::Reference
< awt::XControl
> xNewControl
;
273 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
275 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
277 Object
>>= aComServiceName
;
279 // TODO: Support Before and After?
281 StringKey
>>= aNewName
;
282 if ( aNewName
.isEmpty() )
284 aNewName
= aComServiceName
;
285 if ( aNewName
.isEmpty() )
286 aNewName
= "Control";
289 while( xDialogContainer
->hasByName( aNewName
) && (nInd
< SAL_MAX_INT32
) )
291 aNewName
= aComServiceName
;
292 aNewName
+= OUString::valueOf( nInd
++ );
296 double fDefWidth
= 72.0, fDefHeight
= 18.0;
297 if ( !aComServiceName
.isEmpty() )
299 // create a UNO control model based on the passed control type
300 uno::Reference
< awt::XControlModel
> xNewModel
;
301 bool bFontSupport
= false;
302 bool bNativeAX
= false;
303 if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.CommandButton.1" ) ) )
305 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
306 fDefWidth
= 72.0; fDefHeight
= 24.0;
309 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.Label.1" ) ) )
311 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlFixedTextModel" ), uno::UNO_QUERY_THROW
);
312 fDefWidth
= 72.0; fDefHeight
= 18.0;
315 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.Image.1" ) ) )
317 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlImageControlModel" ), uno::UNO_QUERY_THROW
);
318 fDefWidth
= 72.0; fDefHeight
= 72.0;
320 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.CheckBox.1" ) ) )
322 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlCheckBoxModel" ), uno::UNO_QUERY_THROW
);
323 fDefWidth
= 108.0; fDefHeight
= 18.0;
326 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.OptionButton.1" ) ) )
328 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlRadioButtonModel" ), uno::UNO_QUERY_THROW
);
329 fDefWidth
= 108.0; fDefHeight
= 18.0;
332 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.TextBox.1" ) ) )
334 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlEditModel" ), uno::UNO_QUERY_THROW
);
335 fDefWidth
= 72.0; fDefHeight
= 18.0;
338 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ListBox.1" ) ) )
340 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlListBoxModel" ), uno::UNO_QUERY_THROW
);
341 fDefWidth
= 72.0; fDefHeight
= 18.0;
344 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ComboBox.1" ) ) )
346 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlComboBoxModel" ), uno::UNO_QUERY_THROW
);
347 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
348 xProps
->setPropertyValue( "Dropdown" , uno::Any( true ) );
349 fDefWidth
= 72.0; fDefHeight
= 18.0;
352 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ToggleButton.1" ) ) )
354 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
355 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
356 xProps
->setPropertyValue( "Toggle" , uno::Any( true ) );
357 fDefWidth
= 72.0; fDefHeight
= 18.0;
360 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.Frame.1" ) ) )
362 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlGroupBoxModel" ), uno::UNO_QUERY_THROW
);
363 fDefWidth
= 216.0; fDefHeight
= 144.0;
366 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.SpinButton.1" ) ) )
368 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlSpinButtonModel" ), uno::UNO_QUERY_THROW
);
369 fDefWidth
= 12.75; fDefHeight
= 25.5;
371 else if( aComServiceName
.equalsIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "Forms.ScrollBar.1" ) ) )
373 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlScrollBarModel" ), uno::UNO_QUERY_THROW
);
374 fDefWidth
= 12.75; fDefHeight
= 63.8;
378 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.custom.awt.UnoControlSystemAXContainerModel" ), uno::UNO_QUERY_THROW
);
379 fDefWidth
= 72.0; fDefHeight
= 18.0;
383 // need to set a few font properties to get rid of the default DONT_KNOW values
386 uno::Reference
< beans::XPropertySet
> xModelProps( xNewModel
, uno::UNO_QUERY_THROW
);
387 xModelProps
->setPropertyValue( "FontName" , uno::Any( OUString("Tahoma" ) ) );
388 xModelProps
->setPropertyValue( "FontHeight" , uno::Any( float( 8.0 ) ) );
389 xModelProps
->setPropertyValue( "FontWeight" , uno::Any( awt::FontWeight::NORMAL
) );
390 xModelProps
->setPropertyValue( "FontSlant" , uno::Any( awt::FontSlant_NONE
) );
391 xModelProps
->setPropertyValue( "FontUnderline" , uno::Any( awt::FontUnderline::NONE
) );
392 xModelProps
->setPropertyValue( "FontStrikeout" , uno::Any( awt::FontStrikeout::NONE
) );
395 xDialogContainer
->insertByName( aNewName
, uno::makeAny( xNewModel
) );
396 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
397 xNewControl
= xControlContainer
->getControl( aNewName
);
401 uno::Reference
< script::XInvocation
> xControlInvoke( xNewControl
, uno::UNO_QUERY_THROW
);
403 uno::Sequence
< uno::Any
> aArgs( 1 );
404 aArgs
[0] <<= aComServiceName
;
405 uno::Sequence
< sal_Int16
> aOutIDDummy
;
406 uno::Sequence
< uno::Any
> aOutDummy
;
407 xControlInvoke
->invoke( "SOAddAXControl" , aArgs
, aOutIDDummy
, aOutDummy
);
409 catch (const uno::Exception
&)
411 xDialogContainer
->removeByName( aNewName
);
416 if ( xNewControl
.is() )
418 UpdateCollectionIndex( lcl_controlsWrapper( mxDialog
) );
419 aResult
<<= xNewControl
;
420 aResult
= createCollectionObject( aResult
);
421 uno::Reference
< msforms::XControl
> xVBAControl( aResult
, uno::UNO_QUERY_THROW
);
422 if( fDefWidth
> 0.0 )
423 xVBAControl
->setWidth( fDefWidth
);
424 if( fDefHeight
> 0.0 )
425 xVBAControl
->setHeight( fDefHeight
);
428 throw uno::RuntimeException();
430 catch (const uno::RuntimeException
&)
434 catch (const uno::Exception
& e
)
436 throw lang::WrappedTargetException( "Can not create AXControl!",
437 uno::Reference
< uno::XInterface
>(),
444 void SAL_CALL
ScVbaControls::Remove( const uno::Any
& StringKeyOrIndex
)
445 throw (uno::RuntimeException
)
447 OUString aControlName
;
448 sal_Int32 nIndex
= -1;
452 if ( !mxDialog
.is() )
453 throw uno::RuntimeException();
455 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
456 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
458 if ( !( ( StringKeyOrIndex
>>= aControlName
) && !aControlName
.isEmpty() )
459 && !( ( StringKeyOrIndex
>>= nIndex
) && nIndex
>= 0 && nIndex
< m_xIndexAccess
->getCount() ) )
460 throw uno::RuntimeException();
462 uno::Reference
< awt::XControl
> xControl
;
463 if ( !aControlName
.isEmpty() )
465 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
466 xControl
= xControlContainer
->getControl( aControlName
);
470 m_xIndexAccess
->getByIndex( nIndex
) >>= xControl
;
473 if ( !xControl
.is() )
474 throw uno::RuntimeException();
476 if ( aControlName
.isEmpty() )
477 aControlName
= ControlArrayWrapper::getControlName( xControl
);
479 xDialogContainer
->removeByName( aControlName
);
482 catch (const uno::RuntimeException
&)
484 // the exceptions are not rethrown, impossibility to find or remove the control is currently not reported
485 // since in most cases it means just that the controls is already not there, the VBA seems to do it in the same way
489 catch (const uno::Exception
&)
491 // throw lang::WrappedTargetException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Can not create AXControl!" ) ),
492 // uno::Reference< uno::XInterface >(),
493 // uno::makeAny( e ) );
499 ScVbaControls::getElementType() throw (uno::RuntimeException
)
501 return ooo::vba::msforms::XControl::static_type(0);
504 VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaControls
, "ooo.vba.msforms.Controls" )
505 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */