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>
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include "vbacontrols.hxx"
33 #include "vbacontrol.hxx"
34 #include <cppuhelper/exc_hlp.hxx>
35 #include <cppuhelper/implbase.hxx>
36 #include <ooo/vba/XControlProvider.hpp>
37 #include <unordered_map>
39 using namespace com::sun::star
;
40 using namespace ooo::vba
;
43 typedef std::unordered_map
< OUString
, sal_Int32
> ControlIndexMap
;
45 class ControlArrayWrapper
: public ::cppu::WeakImplHelper
< container::XNameAccess
, container::XIndexAccess
>
47 uno::Reference
< awt::XControlContainer
> mxDialog
;
48 uno::Sequence
< OUString
> msNames
;
49 std::vector
< uno::Reference
< awt::XControl
> > mControls
;
50 ControlIndexMap mIndices
;
53 void SetArrayElementTo( const uno::Reference
< awt::XControl
>& xCtrl
, sal_Int32 nIndex
)
55 // initialize the element with specified index to the control
59 nIndex
= msNames
.getLength();
61 if ( nIndex
>= msNames
.getLength() )
62 msNames
.realloc( nIndex
);
64 msNames
[ nIndex
] = getControlName( xCtrl
);
65 mControls
.push_back( xCtrl
);
66 mIndices
[ msNames
[ nIndex
] ] = nIndex
;
70 explicit ControlArrayWrapper( const uno::Reference
< awt::XControl
>& xDialog
)
74 mxDialog
.set( xDialog
, uno::UNO_QUERY_THROW
);
75 uno::Sequence
< uno::Reference
< awt::XControl
> > sXControls
= mxDialog
->getControls();
77 msNames
.realloc( sXControls
.getLength() );
78 for ( sal_Int32 i
= 0; i
< sXControls
.getLength(); ++i
)
79 SetArrayElementTo( sXControls
[ i
], i
);
81 catch (const uno::Exception
&)
83 // accept the case when the dialog already does not exist
84 // in this case the wrapper should work in dummy mode
88 static OUString
getControlName( const uno::Reference
< awt::XControl
>& xCtrl
)
91 throw uno::RuntimeException();
93 uno::Reference
< beans::XPropertySet
> xProp( xCtrl
->getModel(), uno::UNO_QUERY_THROW
);
95 xProp
->getPropertyValue( "Name" ) >>= sName
;
101 virtual uno::Type SAL_CALL
getElementType( ) override
103 return cppu::UnoType
<awt::XControl
>::get();
106 virtual sal_Bool SAL_CALL
hasElements( ) override
108 return ( !mControls
.empty() );
112 virtual uno::Any SAL_CALL
getByName( const OUString
& aName
) override
114 if ( !hasByName( aName
) )
115 throw container::NoSuchElementException();
116 return getByIndex( mIndices
[ aName
] );
119 virtual uno::Sequence
< OUString
> SAL_CALL
getElementNames( ) override
124 virtual sal_Bool SAL_CALL
hasByName( const OUString
& aName
) override
126 ControlIndexMap::iterator it
= mIndices
.find( aName
);
127 return it
!= mIndices
.end();
131 virtual ::sal_Int32 SAL_CALL
getCount( ) override
133 return mControls
.size();
136 virtual uno::Any SAL_CALL
getByIndex( ::sal_Int32 Index
) override
138 if ( Index
< 0 || Index
>= static_cast< sal_Int32
>( mControls
.size() ) )
139 throw lang::IndexOutOfBoundsException();
140 return uno::makeAny( mControls
[ Index
] );
145 class ControlsEnumWrapper
: public EnumerationHelper_BASE
147 uno::Reference
<uno::XComponentContext
> m_xContext
;
148 uno::Reference
<container::XIndexAccess
> m_xIndexAccess
;
149 uno::Reference
<awt::XControl
> m_xDlg
;
150 uno::Reference
< frame::XModel
> m_xModel
;
151 double const mfOffsetX
;
152 double const mfOffsetY
;
158 const uno::Reference
< uno::XComponentContext
>& xContext
,
159 const uno::Reference
< container::XIndexAccess
>& xIndexAccess
,
160 const uno::Reference
< awt::XControl
>& xDlg
,
161 const uno::Reference
< frame::XModel
>& xModel
,
162 double fOffsetX
, double fOffsetY
) :
163 m_xContext( xContext
),
164 m_xIndexAccess( xIndexAccess
),
167 mfOffsetX( fOffsetX
),
168 mfOffsetY( fOffsetY
),
171 virtual sal_Bool SAL_CALL
hasMoreElements( ) override
173 return ( nIndex
< m_xIndexAccess
->getCount() );
176 virtual uno::Any SAL_CALL
nextElement( ) override
178 if ( nIndex
< m_xIndexAccess
->getCount() )
180 uno::Reference
< awt::XControl
> xControl
;
181 m_xIndexAccess
->getByIndex( nIndex
++ ) >>= xControl
;
183 uno::Reference
< msforms::XControl
> xVBAControl
;
184 if ( xControl
.is() && m_xDlg
.is() )
185 xVBAControl
= ScVbaControlFactory::createUserformControl( m_xContext
, xControl
, m_xDlg
, m_xModel
, mfOffsetX
, mfOffsetY
);
186 return uno::makeAny( xVBAControl
);
188 throw container::NoSuchElementException();
194 static uno::Reference
<container::XIndexAccess
>
195 lcl_controlsWrapper( const uno::Reference
< awt::XControl
>& xDlg
)
197 return new ControlArrayWrapper( xDlg
);
200 ScVbaControls::ScVbaControls(
201 const uno::Reference
< XHelperInterface
>& xParent
,
202 const uno::Reference
< uno::XComponentContext
>& xContext
,
203 const css::uno::Reference
< awt::XControl
>& xDialog
,
204 const uno::Reference
< frame::XModel
>& xModel
,
205 double fOffsetX
, double fOffsetY
) :
206 ControlsImpl_BASE( xParent
, xContext
, lcl_controlsWrapper( xDialog
) ),
209 mfOffsetX( fOffsetX
),
210 mfOffsetY( fOffsetY
)
214 uno::Reference
< container::XEnumeration
>
215 ScVbaControls::createEnumeration()
217 uno::Reference
< container::XEnumeration
> xEnum( new ControlsEnumWrapper( mxContext
, m_xIndexAccess
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
) );
219 throw uno::RuntimeException();
224 ScVbaControls::createCollectionObject( const css::uno::Any
& aSource
)
226 // Create control from awt::XControl
227 uno::Reference
< awt::XControl
> xControl( aSource
, uno::UNO_QUERY_THROW
);
228 uno::Reference
< msforms::XControl
> xVBAControl
= ScVbaControlFactory::createUserformControl( mxContext
, xControl
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
);
229 return uno::Any( xVBAControl
);
233 ScVbaControls::Move( double cx
, double cy
)
235 uno::Reference
< container::XEnumeration
> xEnum( createEnumeration() );
236 while ( xEnum
->hasMoreElements() )
238 uno::Reference
< msforms::XControl
> xControl( xEnum
->nextElement(), uno::UNO_QUERY_THROW
);
239 xControl
->setLeft( xControl
->getLeft() + cx
);
240 xControl
->setTop( xControl
->getTop() + cy
);
244 uno::Any SAL_CALL
ScVbaControls::Add( const uno::Any
& Object
, const uno::Any
& StringKey
, const uno::Any
& /*Before*/, const uno::Any
& /*After*/ )
247 OUString aComServiceName
;
251 if ( !mxDialog
.is() )
252 throw uno::RuntimeException();
254 uno::Reference
< awt::XControl
> xNewControl
;
255 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
257 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
259 Object
>>= aComServiceName
;
261 // TODO: Support Before and After?
263 StringKey
>>= aNewName
;
264 if ( aNewName
.isEmpty() )
266 aNewName
= aComServiceName
;
267 if ( aNewName
.isEmpty() )
268 aNewName
= "Control";
271 while( xDialogContainer
->hasByName( aNewName
) && (nInd
< SAL_MAX_INT32
) )
273 aNewName
= aComServiceName
+ OUString::number( nInd
++ );
277 double fDefWidth
= 72.0, fDefHeight
= 18.0;
278 if ( !aComServiceName
.isEmpty() )
280 // create a UNO control model based on the passed control type
281 uno::Reference
< awt::XControlModel
> xNewModel
;
282 bool bFontSupport
= false;
283 bool bNativeAX
= false;
284 if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CommandButton.1" ) )
286 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
287 fDefWidth
= 72.0; fDefHeight
= 24.0;
290 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Label.1" ) )
292 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlFixedTextModel" ), uno::UNO_QUERY_THROW
);
293 fDefWidth
= 72.0; fDefHeight
= 18.0;
296 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Image.1" ) )
298 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlImageControlModel" ), uno::UNO_QUERY_THROW
);
299 fDefWidth
= 72.0; fDefHeight
= 72.0;
301 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CheckBox.1" ) )
303 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlCheckBoxModel" ), uno::UNO_QUERY_THROW
);
304 fDefWidth
= 108.0; fDefHeight
= 18.0;
307 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.OptionButton.1" ) )
309 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlRadioButtonModel" ), uno::UNO_QUERY_THROW
);
310 fDefWidth
= 108.0; fDefHeight
= 18.0;
313 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.TextBox.1" ) )
315 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlEditModel" ), uno::UNO_QUERY_THROW
);
316 fDefWidth
= 72.0; fDefHeight
= 18.0;
319 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ListBox.1" ) )
321 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlListBoxModel" ), uno::UNO_QUERY_THROW
);
322 fDefWidth
= 72.0; fDefHeight
= 18.0;
325 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ComboBox.1" ) )
327 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlComboBoxModel" ), uno::UNO_QUERY_THROW
);
328 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
329 xProps
->setPropertyValue( "Dropdown" , uno::Any( true ) );
330 fDefWidth
= 72.0; fDefHeight
= 18.0;
333 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ToggleButton.1" ) )
335 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
336 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
337 xProps
->setPropertyValue( "Toggle" , uno::Any( true ) );
338 fDefWidth
= 72.0; fDefHeight
= 18.0;
341 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Frame.1" ) )
343 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlGroupBoxModel" ), uno::UNO_QUERY_THROW
);
344 fDefWidth
= 216.0; fDefHeight
= 144.0;
347 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.SpinButton.1" ) )
349 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlSpinButtonModel" ), uno::UNO_QUERY_THROW
);
350 fDefWidth
= 12.75; fDefHeight
= 25.5;
352 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ScrollBar.1" ) )
354 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlScrollBarModel" ), uno::UNO_QUERY_THROW
);
355 fDefWidth
= 12.75; fDefHeight
= 63.8;
359 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.custom.awt.UnoControlSystemAXContainerModel" ), uno::UNO_QUERY_THROW
);
360 fDefWidth
= 72.0; fDefHeight
= 18.0;
364 // need to set a few font properties to get rid of the default DONT_KNOW values
367 uno::Reference
< beans::XPropertySet
> xModelProps( xNewModel
, uno::UNO_QUERY_THROW
);
368 xModelProps
->setPropertyValue( "FontName" , uno::Any( OUString("Tahoma" ) ) );
369 xModelProps
->setPropertyValue( "FontHeight" , uno::Any( float( 8.0 ) ) );
370 xModelProps
->setPropertyValue( "FontWeight" , uno::Any( awt::FontWeight::NORMAL
) );
371 xModelProps
->setPropertyValue( "FontSlant" , uno::Any( awt::FontSlant_NONE
) );
372 xModelProps
->setPropertyValue( "FontUnderline" , uno::Any( awt::FontUnderline::NONE
) );
373 xModelProps
->setPropertyValue( "FontStrikeout" , uno::Any( awt::FontStrikeout::NONE
) );
376 xDialogContainer
->insertByName( aNewName
, uno::makeAny( xNewModel
) );
377 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
378 xNewControl
= xControlContainer
->getControl( aNewName
);
382 uno::Reference
< script::XInvocation
> xControlInvoke( xNewControl
, uno::UNO_QUERY_THROW
);
384 uno::Sequence
< uno::Any
> aArgs( 1 );
385 aArgs
[0] <<= aComServiceName
;
386 uno::Sequence
< sal_Int16
> aOutIDDummy
;
387 uno::Sequence
< uno::Any
> aOutDummy
;
388 xControlInvoke
->invoke( "SOAddAXControl" , aArgs
, aOutIDDummy
, aOutDummy
);
390 catch (const uno::Exception
&)
392 xDialogContainer
->removeByName( aNewName
);
397 if ( !xNewControl
.is() )
398 throw uno::RuntimeException();
400 UpdateCollectionIndex( lcl_controlsWrapper( mxDialog
) );
401 aResult
<<= xNewControl
;
402 aResult
= createCollectionObject( aResult
);
403 uno::Reference
< msforms::XControl
> xVBAControl( aResult
, uno::UNO_QUERY_THROW
);
404 if( fDefWidth
> 0.0 )
405 xVBAControl
->setWidth( fDefWidth
);
406 if( fDefHeight
> 0.0 )
407 xVBAControl
->setHeight( fDefHeight
);
409 catch (const uno::RuntimeException
&)
413 catch (const uno::Exception
&)
415 css::uno::Any anyEx
= cppu::getCaughtException();
416 throw lang::WrappedTargetRuntimeException( "Can not create AXControl!",
417 uno::Reference
< uno::XInterface
>(),
424 void SAL_CALL
ScVbaControls::Remove( const uno::Any
& StringKeyOrIndex
)
426 OUString aControlName
;
427 sal_Int32 nIndex
= -1;
431 if ( !mxDialog
.is() )
432 throw uno::RuntimeException();
434 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
435 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
437 if ( !( ( StringKeyOrIndex
>>= aControlName
) && !aControlName
.isEmpty() )
438 && !( ( StringKeyOrIndex
>>= nIndex
) && nIndex
>= 0 && nIndex
< m_xIndexAccess
->getCount() ) )
439 throw uno::RuntimeException();
441 uno::Reference
< awt::XControl
> xControl
;
442 if ( !aControlName
.isEmpty() )
444 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
445 xControl
= xControlContainer
->getControl( aControlName
);
449 m_xIndexAccess
->getByIndex( nIndex
) >>= xControl
;
452 if ( !xControl
.is() )
453 throw uno::RuntimeException();
455 if ( aControlName
.isEmpty() )
456 aControlName
= ControlArrayWrapper::getControlName( xControl
);
458 xDialogContainer
->removeByName( aControlName
);
461 catch (const uno::RuntimeException
&)
463 // the exceptions are not rethrown, impossibility to find or remove the control is currently not reported
464 // since in most cases it means just that the controls is already not there, the VBA seems to do it in the same way
468 catch (const uno::Exception
&)
470 // throw lang::WrappedTargetException("Can not create AXControl!",
471 // uno::Reference< uno::XInterface >(),
472 // uno::makeAny( e ) );
478 ScVbaControls::getElementType()
480 return cppu::UnoType
<ooo::vba::msforms::XControl
>::get();
483 VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaControls
, "ooo.vba.msforms.Controls" )
484 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */