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/WrappedTargetRuntimeException.hpp>
29 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include "vbacontrols.hxx"
32 #include "vbacontrol.hxx"
33 #include <cppuhelper/exc_hlp.hxx>
34 #include <cppuhelper/implbase.hxx>
35 #include <o3tl/safeint.hxx>
36 #include <unordered_map>
39 using namespace com::sun::star
;
40 using namespace ooo::vba
;
43 typedef std::unordered_map
< OUString
, sal_Int32
> ControlIndexMap
;
47 class ControlArrayWrapper
: public ::cppu::WeakImplHelper
< container::XNameAccess
, container::XIndexAccess
>
49 uno::Reference
< awt::XControlContainer
> mxDialog
;
50 uno::Sequence
< OUString
> msNames
;
51 std::vector
< uno::Reference
< awt::XControl
> > mControls
;
52 ControlIndexMap mIndices
;
55 void SetArrayElementTo( const uno::Reference
< awt::XControl
>& xCtrl
, sal_Int32 nIndex
)
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
.getArray()[ nIndex
] = getControlName( xCtrl
);
67 mControls
.push_back( xCtrl
);
68 mIndices
[ msNames
[ nIndex
] ] = nIndex
;
72 explicit ControlArrayWrapper( const uno::Reference
< awt::XControl
>& xDialog
)
76 mxDialog
.set( xDialog
, uno::UNO_QUERY_THROW
);
77 uno::Sequence
< uno::Reference
< awt::XControl
> > sXControls
= mxDialog
->getControls();
79 msNames
.realloc( sXControls
.getLength() );
80 for ( sal_Int32 i
= 0; i
< sXControls
.getLength(); ++i
)
81 SetArrayElementTo( sXControls
[ i
], i
);
83 catch (const uno::Exception
&)
85 // accept the case when the dialog already does not exist
86 // in this case the wrapper should work in dummy mode
90 static OUString
getControlName( const uno::Reference
< awt::XControl
>& xCtrl
)
93 throw uno::RuntimeException();
95 uno::Reference
< beans::XPropertySet
> xProp( xCtrl
->getModel(), uno::UNO_QUERY_THROW
);
97 xProp
->getPropertyValue( u
"Name"_ustr
) >>= sName
;
103 virtual uno::Type SAL_CALL
getElementType( ) override
105 return cppu::UnoType
<awt::XControl
>::get();
108 virtual sal_Bool SAL_CALL
hasElements( ) override
110 return ( !mControls
.empty() );
114 virtual uno::Any SAL_CALL
getByName( const OUString
& aName
) override
116 if ( !hasByName( aName
) )
117 throw container::NoSuchElementException();
118 return getByIndex( mIndices
[ aName
] );
121 virtual uno::Sequence
< OUString
> SAL_CALL
getElementNames( ) override
126 virtual sal_Bool SAL_CALL
hasByName( const OUString
& aName
) override
128 ControlIndexMap::iterator it
= mIndices
.find( aName
);
129 return it
!= mIndices
.end();
133 virtual ::sal_Int32 SAL_CALL
getCount( ) override
135 return mControls
.size();
138 virtual uno::Any SAL_CALL
getByIndex( ::sal_Int32 Index
) override
140 if ( Index
< 0 || o3tl::make_unsigned(Index
) >= mControls
.size() )
141 throw lang::IndexOutOfBoundsException();
142 return uno::Any( mControls
[ Index
] );
147 class ControlsEnumWrapper
: public EnumerationHelper_BASE
149 uno::Reference
<uno::XComponentContext
> m_xContext
;
150 uno::Reference
<container::XIndexAccess
> m_xIndexAccess
;
151 uno::Reference
<awt::XControl
> m_xDlg
;
152 uno::Reference
< frame::XModel
> m_xModel
;
160 uno::Reference
< uno::XComponentContext
> xContext
,
161 uno::Reference
< container::XIndexAccess
> xIndexAccess
,
162 uno::Reference
< awt::XControl
> xDlg
,
163 uno::Reference
< frame::XModel
> xModel
,
164 double fOffsetX
, double fOffsetY
) :
165 m_xContext(std::move( xContext
)),
166 m_xIndexAccess(std::move( xIndexAccess
)),
167 m_xDlg(std::move( xDlg
)),
168 m_xModel(std::move( xModel
)),
169 mfOffsetX( fOffsetX
),
170 mfOffsetY( fOffsetY
),
173 virtual sal_Bool SAL_CALL
hasMoreElements( ) override
175 return ( nIndex
< m_xIndexAccess
->getCount() );
178 virtual uno::Any SAL_CALL
nextElement( ) override
180 if ( nIndex
< m_xIndexAccess
->getCount() )
182 uno::Reference
< awt::XControl
> xControl
;
183 m_xIndexAccess
->getByIndex( nIndex
++ ) >>= xControl
;
185 uno::Reference
< msforms::XControl
> xVBAControl
;
186 if ( xControl
.is() && m_xDlg
.is() )
187 xVBAControl
= ScVbaControlFactory::createUserformControl( m_xContext
, xControl
, m_xDlg
, m_xModel
, mfOffsetX
, mfOffsetY
);
188 return uno::Any( xVBAControl
);
190 throw container::NoSuchElementException();
197 static uno::Reference
<container::XIndexAccess
>
198 lcl_controlsWrapper( const uno::Reference
< awt::XControl
>& xDlg
)
200 return new ControlArrayWrapper( xDlg
);
203 ScVbaControls::ScVbaControls(
204 const uno::Reference
< XHelperInterface
>& xParent
,
205 const uno::Reference
< uno::XComponentContext
>& xContext
,
206 const css::uno::Reference
< awt::XControl
>& xDialog
,
207 uno::Reference
< frame::XModel
> xModel
,
208 double fOffsetX
, double fOffsetY
) :
209 ControlsImpl_BASE( xParent
, xContext
, lcl_controlsWrapper( xDialog
) ),
211 mxModel(std::move( xModel
)),
212 mfOffsetX( fOffsetX
),
213 mfOffsetY( fOffsetY
)
217 uno::Reference
< container::XEnumeration
>
218 ScVbaControls::createEnumeration()
220 uno::Reference
< container::XEnumeration
> xEnum( new ControlsEnumWrapper( mxContext
, m_xIndexAccess
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
) );
222 throw uno::RuntimeException();
227 ScVbaControls::createCollectionObject( const css::uno::Any
& aSource
)
229 // Create control from awt::XControl
230 uno::Reference
< awt::XControl
> xControl( aSource
, uno::UNO_QUERY_THROW
);
231 uno::Reference
< msforms::XControl
> xVBAControl
= ScVbaControlFactory::createUserformControl( mxContext
, xControl
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
);
232 return uno::Any( xVBAControl
);
236 ScVbaControls::Move( double cx
, double cy
)
238 uno::Reference
< container::XEnumeration
> xEnum( createEnumeration() );
239 while ( xEnum
->hasMoreElements() )
241 uno::Reference
< msforms::XControl
> xControl( xEnum
->nextElement(), uno::UNO_QUERY_THROW
);
242 xControl
->setLeft( xControl
->getLeft() + cx
);
243 xControl
->setTop( xControl
->getTop() + cy
);
247 uno::Any SAL_CALL
ScVbaControls::Add( const uno::Any
& Object
, const uno::Any
& StringKey
, const uno::Any
& /*Before*/, const uno::Any
& /*After*/ )
250 OUString aComServiceName
;
254 if ( !mxDialog
.is() )
255 throw uno::RuntimeException();
257 uno::Reference
< awt::XControl
> xNewControl
;
258 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
260 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
262 Object
>>= aComServiceName
;
264 // TODO: Support Before and After?
266 StringKey
>>= aNewName
;
267 if ( aNewName
.isEmpty() )
269 aNewName
= aComServiceName
;
270 if ( aNewName
.isEmpty() )
271 aNewName
= "Control";
274 while( xDialogContainer
->hasByName( aNewName
) && (nInd
< SAL_MAX_INT32
) )
276 aNewName
= aComServiceName
+ OUString::number( nInd
++ );
280 double fDefWidth
= 72.0, fDefHeight
= 18.0;
281 if ( !aComServiceName
.isEmpty() )
283 // create a UNO control model based on the passed control type
284 uno::Reference
< awt::XControlModel
> xNewModel
;
285 bool bFontSupport
= false;
286 bool bNativeAX
= false;
287 if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CommandButton.1" ) )
289 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlButtonModel"_ustr
), uno::UNO_QUERY_THROW
);
290 fDefWidth
= 72.0; fDefHeight
= 24.0;
293 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Label.1" ) )
295 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlFixedTextModel"_ustr
), uno::UNO_QUERY_THROW
);
296 fDefWidth
= 72.0; fDefHeight
= 18.0;
299 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Image.1" ) )
301 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlImageControlModel"_ustr
), uno::UNO_QUERY_THROW
);
302 fDefWidth
= 72.0; fDefHeight
= 72.0;
304 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CheckBox.1" ) )
306 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlCheckBoxModel"_ustr
), uno::UNO_QUERY_THROW
);
307 fDefWidth
= 108.0; fDefHeight
= 18.0;
310 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.OptionButton.1" ) )
312 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlRadioButtonModel"_ustr
), uno::UNO_QUERY_THROW
);
313 fDefWidth
= 108.0; fDefHeight
= 18.0;
316 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.TextBox.1" ) )
318 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlEditModel"_ustr
), uno::UNO_QUERY_THROW
);
319 fDefWidth
= 72.0; fDefHeight
= 18.0;
322 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ListBox.1" ) )
324 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlListBoxModel"_ustr
), uno::UNO_QUERY_THROW
);
325 fDefWidth
= 72.0; fDefHeight
= 18.0;
328 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ComboBox.1" ) )
330 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlComboBoxModel"_ustr
), uno::UNO_QUERY_THROW
);
331 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
332 xProps
->setPropertyValue( u
"Dropdown"_ustr
, uno::Any( true ) );
333 fDefWidth
= 72.0; fDefHeight
= 18.0;
336 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ToggleButton.1" ) )
338 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlButtonModel"_ustr
), uno::UNO_QUERY_THROW
);
339 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
340 xProps
->setPropertyValue( u
"Toggle"_ustr
, uno::Any( true ) );
341 fDefWidth
= 72.0; fDefHeight
= 18.0;
344 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Frame.1" ) )
346 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlGroupBoxModel"_ustr
), uno::UNO_QUERY_THROW
);
347 fDefWidth
= 216.0; fDefHeight
= 144.0;
350 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.SpinButton.1" ) )
352 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlSpinButtonModel"_ustr
), uno::UNO_QUERY_THROW
);
353 fDefWidth
= 12.75; fDefHeight
= 25.5;
355 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ScrollBar.1" ) )
357 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.awt.UnoControlScrollBarModel"_ustr
), uno::UNO_QUERY_THROW
);
358 fDefWidth
= 12.75; fDefHeight
= 63.8;
362 xNewModel
.set( xModelFactory
->createInstance( u
"com.sun.star.custom.awt.UnoControlSystemAXContainerModel"_ustr
), uno::UNO_QUERY_THROW
);
363 fDefWidth
= 72.0; fDefHeight
= 18.0;
367 // need to set a few font properties to get rid of the default DONT_KNOW values
370 uno::Reference
< beans::XPropertySet
> xModelProps( xNewModel
, uno::UNO_QUERY_THROW
);
371 xModelProps
->setPropertyValue( u
"FontName"_ustr
, uno::Any( u
"Tahoma"_ustr
) );
372 xModelProps
->setPropertyValue( u
"FontHeight"_ustr
, uno::Any( float( 8.0 ) ) );
373 xModelProps
->setPropertyValue( u
"FontWeight"_ustr
, uno::Any( awt::FontWeight::NORMAL
) );
374 xModelProps
->setPropertyValue( u
"FontSlant"_ustr
, uno::Any( awt::FontSlant_NONE
) );
375 xModelProps
->setPropertyValue( u
"FontUnderline"_ustr
, uno::Any( awt::FontUnderline::NONE
) );
376 xModelProps
->setPropertyValue( u
"FontStrikeout"_ustr
, uno::Any( awt::FontStrikeout::NONE
) );
379 xDialogContainer
->insertByName( aNewName
, uno::Any( xNewModel
) );
380 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
381 xNewControl
= xControlContainer
->getControl( aNewName
);
385 uno::Reference
< script::XInvocation
> xControlInvoke( xNewControl
, uno::UNO_QUERY_THROW
);
387 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(aComServiceName
) };
388 uno::Sequence
< sal_Int16
> aOutIDDummy
;
389 uno::Sequence
< uno::Any
> aOutDummy
;
390 xControlInvoke
->invoke( u
"SOAddAXControl"_ustr
, aArgs
, aOutIDDummy
, aOutDummy
);
392 catch (const uno::Exception
&)
394 xDialogContainer
->removeByName( aNewName
);
399 if ( !xNewControl
.is() )
400 throw uno::RuntimeException();
402 UpdateCollectionIndex( lcl_controlsWrapper( mxDialog
) );
403 aResult
<<= xNewControl
;
404 aResult
= createCollectionObject( aResult
);
405 uno::Reference
< msforms::XControl
> xVBAControl( aResult
, uno::UNO_QUERY_THROW
);
406 if( fDefWidth
> 0.0 )
407 xVBAControl
->setWidth( fDefWidth
);
408 if( fDefHeight
> 0.0 )
409 xVBAControl
->setHeight( fDefHeight
);
411 catch (const uno::RuntimeException
&)
415 catch (const uno::Exception
&)
417 css::uno::Any anyEx
= cppu::getCaughtException();
418 throw lang::WrappedTargetRuntimeException( u
"Can not create AXControl!"_ustr
,
419 uno::Reference
< uno::XInterface
>(),
426 void SAL_CALL
ScVbaControls::Remove( const uno::Any
& StringKeyOrIndex
)
430 OUString aControlName
;
431 sal_Int32 nIndex
= -1;
432 if ( !mxDialog
.is() )
433 throw uno::RuntimeException();
435 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
436 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
438 if ( StringKeyOrIndex
>>= aControlName
)
440 if ( aControlName
.isEmpty() )
441 throw uno::RuntimeException();
443 else if ( StringKeyOrIndex
>>= nIndex
)
445 if (nIndex
>= 0 && nIndex
< m_xIndexAccess
->getCount() )
446 throw uno::RuntimeException();
449 throw uno::RuntimeException();
451 uno::Reference
< awt::XControl
> xControl
;
452 if ( !aControlName
.isEmpty() )
454 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
455 xControl
= xControlContainer
->getControl( aControlName
);
459 m_xIndexAccess
->getByIndex( nIndex
) >>= xControl
;
462 if ( !xControl
.is() )
463 throw uno::RuntimeException();
465 if ( aControlName
.isEmpty() )
466 aControlName
= ControlArrayWrapper::getControlName( xControl
);
468 xDialogContainer
->removeByName( aControlName
);
471 catch (const uno::RuntimeException
&)
473 // the exceptions are not rethrown, impossibility to find or remove the control is currently not reported
474 // since in most cases it means just that the controls is already not there, the VBA seems to do it in the same way
478 catch (const uno::Exception
&)
480 // throw lang::WrappedTargetException("Can not create AXControl!",
481 // uno::Reference< uno::XInterface >(),
482 // uno::makeAny( e ) );
488 ScVbaControls::getElementType()
490 return cppu::UnoType
<ooo::vba::msforms::XControl
>::get();
493 VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaControls
, u
"ooo.vba.msforms.Controls"_ustr
)
494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */