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>
29 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
31 #include "vbacontrols.hxx"
32 #include "vbacontrol.hxx"
33 #include <cppuhelper/implbase2.hxx>
34 #include <ooo/vba/XControlProvider.hpp>
35 #include <unordered_map>
37 using namespace com::sun::star
;
38 using namespace ooo::vba
;
41 typedef ::cppu::WeakImplHelper2
< container::XNameAccess
, container::XIndexAccess
> ArrayWrapImpl
;
43 typedef std::unordered_map
< OUString
, sal_Int32
, OUStringHash
,
44 std::equal_to
< OUString
> > ControlIndexMap
;
45 typedef std::vector
< uno::Reference
< awt::XControl
> > ControlVec
;
47 class ControlArrayWrapper
: public ArrayWrapImpl
49 uno::Reference
< awt::XControlContainer
> mxDialog
;
50 uno::Sequence
< OUString
> msNames
;
52 ControlIndexMap mIndices
;
55 void SetArrayElementTo( const uno::Reference
< awt::XControl
>& xCtrl
, sal_Int32 nIndex
= -1 )
57 // initialize the element with specified index to the control
61 nIndex
= msNames
.getLength();
63 if ( nIndex
>= msNames
.getLength() )
64 msNames
.realloc( nIndex
);
66 msNames
[ nIndex
] = getControlName( xCtrl
);
67 mControls
.push_back( xCtrl
);
68 mIndices
[ msNames
[ nIndex
] ] = nIndex
;
71 void getNestedControls( ControlVec
& vControls
, uno::Reference
< awt::XControlContainer
>& xContainer
)
73 uno::Sequence
< uno::Reference
< awt::XControl
> > aControls
= xContainer
->getControls();
74 const uno::Reference
< awt::XControl
>* pCtrl
= aControls
.getConstArray();
75 const uno::Reference
< awt::XControl
>* pCtrlsEnd
= pCtrl
+ aControls
.getLength();
76 for ( ; pCtrl
< pCtrlsEnd
; ++pCtrl
)
78 uno::Reference
< awt::XControlContainer
> xC( *pCtrl
, uno::UNO_QUERY
);
79 vControls
.push_back( *pCtrl
);
81 getNestedControls( vControls
, xC
);
85 ControlArrayWrapper( const uno::Reference
< awt::XControl
>& xDialog
)
89 mxDialog
.set( xDialog
, uno::UNO_QUERY_THROW
);
90 uno::Sequence
< uno::Reference
< awt::XControl
> > sXControls
= mxDialog
->getControls();
92 msNames
.realloc( sXControls
.getLength() );
93 for ( sal_Int32 i
= 0; i
< sXControls
.getLength(); ++i
)
94 SetArrayElementTo( sXControls
[ i
], i
);
96 catch (const uno::Exception
&)
98 // accept the case when the dialog already does not exist
99 // in this case the wrapper should work in dummy mode
103 static OUString
getControlName( const uno::Reference
< awt::XControl
>& xCtrl
)
106 throw uno::RuntimeException();
108 uno::Reference
< beans::XPropertySet
> xProp( xCtrl
->getModel(), uno::UNO_QUERY_THROW
);
110 xProp
->getPropertyValue( "Name" ) >>= sName
;
116 virtual uno::Type SAL_CALL
getElementType( ) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
118 return cppu::UnoType
<awt::XControl
>::get();
121 virtual sal_Bool SAL_CALL
hasElements( ) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
123 return ( !mControls
.empty() );
127 virtual uno::Any SAL_CALL
getByName( const OUString
& aName
) throw (container::NoSuchElementException
, lang::WrappedTargetException
, uno::RuntimeException
, std::exception
) SAL_OVERRIDE
129 if ( !hasByName( aName
) )
130 throw container::NoSuchElementException();
131 return getByIndex( mIndices
[ aName
] );
134 virtual uno::Sequence
< OUString
> SAL_CALL
getElementNames( ) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
139 virtual sal_Bool SAL_CALL
hasByName( const OUString
& aName
) throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
141 ControlIndexMap::iterator it
= mIndices
.find( aName
);
142 return it
!= mIndices
.end();
146 virtual ::sal_Int32 SAL_CALL
getCount( ) throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
148 return mControls
.size();
151 virtual uno::Any SAL_CALL
getByIndex( ::sal_Int32 Index
) throw (lang::IndexOutOfBoundsException
, lang::WrappedTargetException
, uno::RuntimeException
, std::exception
) SAL_OVERRIDE
153 if ( Index
< 0 || Index
>= static_cast< sal_Int32
>( mControls
.size() ) )
154 throw lang::IndexOutOfBoundsException();
155 return uno::makeAny( mControls
[ Index
] );
160 class ControlsEnumWrapper
: public EnumerationHelper_BASE
162 uno::Reference
<XHelperInterface
> m_xParent
;
163 uno::Reference
<uno::XComponentContext
> m_xContext
;
164 uno::Reference
<container::XIndexAccess
> m_xIndexAccess
;
165 uno::Reference
<awt::XControl
> m_xDlg
;
166 uno::Reference
< frame::XModel
> m_xModel
;
174 const uno::Reference
< XHelperInterface
>& xParent
,
175 const uno::Reference
< uno::XComponentContext
>& xContext
,
176 const uno::Reference
< container::XIndexAccess
>& xIndexAccess
,
177 const uno::Reference
< awt::XControl
>& xDlg
,
178 const uno::Reference
< frame::XModel
>& xModel
,
179 double fOffsetX
, double fOffsetY
) :
180 m_xParent( xParent
),
181 m_xContext( xContext
),
182 m_xIndexAccess( xIndexAccess
),
185 mfOffsetX( fOffsetX
),
186 mfOffsetY( fOffsetY
),
189 virtual sal_Bool SAL_CALL
hasMoreElements( ) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
191 return ( nIndex
< m_xIndexAccess
->getCount() );
194 virtual uno::Any SAL_CALL
nextElement( ) throw (container::NoSuchElementException
, lang::WrappedTargetException
, uno::RuntimeException
, std::exception
) SAL_OVERRIDE
196 if ( nIndex
< m_xIndexAccess
->getCount() )
198 uno::Reference
< awt::XControl
> xControl
;
199 m_xIndexAccess
->getByIndex( nIndex
++ ) >>= xControl
;
201 uno::Reference
< msforms::XControl
> xVBAControl
;
202 if ( xControl
.is() && m_xDlg
.is() )
203 xVBAControl
= ScVbaControlFactory::createUserformControl( m_xContext
, xControl
, m_xDlg
, m_xModel
, mfOffsetX
, mfOffsetY
);
204 return uno::makeAny( xVBAControl
);
206 throw container::NoSuchElementException();
212 static uno::Reference
<container::XIndexAccess
>
213 lcl_controlsWrapper( const uno::Reference
< awt::XControl
>& xDlg
)
215 return new ControlArrayWrapper( xDlg
);
218 ScVbaControls::ScVbaControls(
219 const uno::Reference
< XHelperInterface
>& xParent
,
220 const uno::Reference
< uno::XComponentContext
>& xContext
,
221 const css::uno::Reference
< awt::XControl
>& xDialog
,
222 const uno::Reference
< frame::XModel
>& xModel
,
223 double fOffsetX
, double fOffsetY
) :
224 ControlsImpl_BASE( xParent
, xContext
, lcl_controlsWrapper( xDialog
) ),
227 mfOffsetX( fOffsetX
),
228 mfOffsetY( fOffsetY
)
232 uno::Reference
< container::XEnumeration
>
233 ScVbaControls::createEnumeration() throw (uno::RuntimeException
)
235 uno::Reference
< container::XEnumeration
> xEnum( new ControlsEnumWrapper( mxParent
, mxContext
, m_xIndexAccess
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
) );
237 throw uno::RuntimeException();
242 ScVbaControls::createCollectionObject( const css::uno::Any
& aSource
)
244 // Create control from awt::XControl
245 uno::Reference
< awt::XControl
> xControl( aSource
, uno::UNO_QUERY_THROW
);
246 uno::Reference
< msforms::XControl
> xVBAControl
= ScVbaControlFactory::createUserformControl( mxContext
, xControl
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
);
247 return uno::Any( xVBAControl
);
251 ScVbaControls::Move( double cx
, double cy
) throw (uno::RuntimeException
, std::exception
)
253 uno::Reference
< container::XEnumeration
> xEnum( createEnumeration() );
254 while ( xEnum
->hasMoreElements() )
256 uno::Reference
< msforms::XControl
> xControl( xEnum
->nextElement(), uno::UNO_QUERY_THROW
);
257 xControl
->setLeft( xControl
->getLeft() + cx
);
258 xControl
->setTop( xControl
->getTop() + cy
);
262 uno::Any SAL_CALL
ScVbaControls::Add( const uno::Any
& Object
, const uno::Any
& StringKey
, const uno::Any
& /*Before*/, const uno::Any
& /*After*/ )
263 throw (uno::RuntimeException
, std::exception
)
266 OUString aComServiceName
;
270 if ( !mxDialog
.is() )
271 throw uno::RuntimeException();
273 uno::Reference
< awt::XControl
> xNewControl
;
274 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
276 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
278 Object
>>= aComServiceName
;
280 // TODO: Support Before and After?
282 StringKey
>>= aNewName
;
283 if ( aNewName
.isEmpty() )
285 aNewName
= aComServiceName
;
286 if ( aNewName
.isEmpty() )
287 aNewName
= "Control";
290 while( xDialogContainer
->hasByName( aNewName
) && (nInd
< SAL_MAX_INT32
) )
292 aNewName
= aComServiceName
;
293 aNewName
+= OUString::number( nInd
++ );
297 double fDefWidth
= 72.0, fDefHeight
= 18.0;
298 if ( !aComServiceName
.isEmpty() )
300 // create a UNO control model based on the passed control type
301 uno::Reference
< awt::XControlModel
> xNewModel
;
302 bool bFontSupport
= false;
303 bool bNativeAX
= false;
304 if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CommandButton.1" ) )
306 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
307 fDefWidth
= 72.0; fDefHeight
= 24.0;
310 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Label.1" ) )
312 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlFixedTextModel" ), uno::UNO_QUERY_THROW
);
313 fDefWidth
= 72.0; fDefHeight
= 18.0;
316 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Image.1" ) )
318 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlImageControlModel" ), uno::UNO_QUERY_THROW
);
319 fDefWidth
= 72.0; fDefHeight
= 72.0;
321 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CheckBox.1" ) )
323 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlCheckBoxModel" ), uno::UNO_QUERY_THROW
);
324 fDefWidth
= 108.0; fDefHeight
= 18.0;
327 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.OptionButton.1" ) )
329 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlRadioButtonModel" ), uno::UNO_QUERY_THROW
);
330 fDefWidth
= 108.0; fDefHeight
= 18.0;
333 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.TextBox.1" ) )
335 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlEditModel" ), uno::UNO_QUERY_THROW
);
336 fDefWidth
= 72.0; fDefHeight
= 18.0;
339 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ListBox.1" ) )
341 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlListBoxModel" ), uno::UNO_QUERY_THROW
);
342 fDefWidth
= 72.0; fDefHeight
= 18.0;
345 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ComboBox.1" ) )
347 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlComboBoxModel" ), uno::UNO_QUERY_THROW
);
348 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
349 xProps
->setPropertyValue( "Dropdown" , uno::Any( true ) );
350 fDefWidth
= 72.0; fDefHeight
= 18.0;
353 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ToggleButton.1" ) )
355 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
356 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
357 xProps
->setPropertyValue( "Toggle" , uno::Any( true ) );
358 fDefWidth
= 72.0; fDefHeight
= 18.0;
361 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Frame.1" ) )
363 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlGroupBoxModel" ), uno::UNO_QUERY_THROW
);
364 fDefWidth
= 216.0; fDefHeight
= 144.0;
367 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.SpinButton.1" ) )
369 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlSpinButtonModel" ), uno::UNO_QUERY_THROW
);
370 fDefWidth
= 12.75; fDefHeight
= 25.5;
372 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ScrollBar.1" ) )
374 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlScrollBarModel" ), uno::UNO_QUERY_THROW
);
375 fDefWidth
= 12.75; fDefHeight
= 63.8;
379 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.custom.awt.UnoControlSystemAXContainerModel" ), uno::UNO_QUERY_THROW
);
380 fDefWidth
= 72.0; fDefHeight
= 18.0;
384 // need to set a few font properties to get rid of the default DONT_KNOW values
387 uno::Reference
< beans::XPropertySet
> xModelProps( xNewModel
, uno::UNO_QUERY_THROW
);
388 xModelProps
->setPropertyValue( "FontName" , uno::Any( OUString("Tahoma" ) ) );
389 xModelProps
->setPropertyValue( "FontHeight" , uno::Any( float( 8.0 ) ) );
390 xModelProps
->setPropertyValue( "FontWeight" , uno::Any( awt::FontWeight::NORMAL
) );
391 xModelProps
->setPropertyValue( "FontSlant" , uno::Any( awt::FontSlant_NONE
) );
392 xModelProps
->setPropertyValue( "FontUnderline" , uno::Any( awt::FontUnderline::NONE
) );
393 xModelProps
->setPropertyValue( "FontStrikeout" , uno::Any( awt::FontStrikeout::NONE
) );
396 xDialogContainer
->insertByName( aNewName
, uno::makeAny( xNewModel
) );
397 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
398 xNewControl
= xControlContainer
->getControl( aNewName
);
402 uno::Reference
< script::XInvocation
> xControlInvoke( xNewControl
, uno::UNO_QUERY_THROW
);
404 uno::Sequence
< uno::Any
> aArgs( 1 );
405 aArgs
[0] <<= aComServiceName
;
406 uno::Sequence
< sal_Int16
> aOutIDDummy
;
407 uno::Sequence
< uno::Any
> aOutDummy
;
408 xControlInvoke
->invoke( "SOAddAXControl" , aArgs
, aOutIDDummy
, aOutDummy
);
410 catch (const uno::Exception
&)
412 xDialogContainer
->removeByName( aNewName
);
417 if ( xNewControl
.is() )
419 UpdateCollectionIndex( lcl_controlsWrapper( mxDialog
) );
420 aResult
<<= xNewControl
;
421 aResult
= createCollectionObject( aResult
);
422 uno::Reference
< msforms::XControl
> xVBAControl( aResult
, uno::UNO_QUERY_THROW
);
423 if( fDefWidth
> 0.0 )
424 xVBAControl
->setWidth( fDefWidth
);
425 if( fDefHeight
> 0.0 )
426 xVBAControl
->setHeight( fDefHeight
);
429 throw uno::RuntimeException();
431 catch (const uno::RuntimeException
&)
435 catch (const uno::Exception
& e
)
437 throw lang::WrappedTargetRuntimeException( "Can not create AXControl!",
438 uno::Reference
< uno::XInterface
>(),
445 void SAL_CALL
ScVbaControls::Remove( const uno::Any
& StringKeyOrIndex
)
446 throw (uno::RuntimeException
, std::exception
)
448 OUString aControlName
;
449 sal_Int32 nIndex
= -1;
453 if ( !mxDialog
.is() )
454 throw uno::RuntimeException();
456 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
457 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
459 if ( !( ( StringKeyOrIndex
>>= aControlName
) && !aControlName
.isEmpty() )
460 && !( ( StringKeyOrIndex
>>= nIndex
) && nIndex
>= 0 && nIndex
< m_xIndexAccess
->getCount() ) )
461 throw uno::RuntimeException();
463 uno::Reference
< awt::XControl
> xControl
;
464 if ( !aControlName
.isEmpty() )
466 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
467 xControl
= xControlContainer
->getControl( aControlName
);
471 m_xIndexAccess
->getByIndex( nIndex
) >>= xControl
;
474 if ( !xControl
.is() )
475 throw uno::RuntimeException();
477 if ( aControlName
.isEmpty() )
478 aControlName
= ControlArrayWrapper::getControlName( xControl
);
480 xDialogContainer
->removeByName( aControlName
);
483 catch (const uno::RuntimeException
&)
485 // the exceptions are not rethrown, impossibility to find or remove the control is currently not reported
486 // since in most cases it means just that the controls is already not there, the VBA seems to do it in the same way
490 catch (const uno::Exception
&)
492 // throw lang::WrappedTargetException("Can not create AXControl!",
493 // uno::Reference< uno::XInterface >(),
494 // uno::makeAny( e ) );
500 ScVbaControls::getElementType() throw (uno::RuntimeException
)
502 return cppu::UnoType
<ooo::vba::msforms::XControl
>::get();
505 VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaControls
, "ooo.vba.msforms.Controls" )
506 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */