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 <unordered_map>
37 using namespace com::sun::star
;
38 using namespace ooo::vba
;
41 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
.getArray()[ 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
;
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();
195 static uno::Reference
<container::XIndexAccess
>
196 lcl_controlsWrapper( const uno::Reference
< awt::XControl
>& xDlg
)
198 return new ControlArrayWrapper( xDlg
);
201 ScVbaControls::ScVbaControls(
202 const uno::Reference
< XHelperInterface
>& xParent
,
203 const uno::Reference
< uno::XComponentContext
>& xContext
,
204 const css::uno::Reference
< awt::XControl
>& xDialog
,
205 const uno::Reference
< frame::XModel
>& xModel
,
206 double fOffsetX
, double fOffsetY
) :
207 ControlsImpl_BASE( xParent
, xContext
, lcl_controlsWrapper( xDialog
) ),
210 mfOffsetX( fOffsetX
),
211 mfOffsetY( fOffsetY
)
215 uno::Reference
< container::XEnumeration
>
216 ScVbaControls::createEnumeration()
218 uno::Reference
< container::XEnumeration
> xEnum( new ControlsEnumWrapper( mxContext
, m_xIndexAccess
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
) );
220 throw uno::RuntimeException();
225 ScVbaControls::createCollectionObject( const css::uno::Any
& aSource
)
227 // Create control from awt::XControl
228 uno::Reference
< awt::XControl
> xControl( aSource
, uno::UNO_QUERY_THROW
);
229 uno::Reference
< msforms::XControl
> xVBAControl
= ScVbaControlFactory::createUserformControl( mxContext
, xControl
, mxDialog
, mxModel
, mfOffsetX
, mfOffsetY
);
230 return uno::Any( xVBAControl
);
234 ScVbaControls::Move( double cx
, double cy
)
236 uno::Reference
< container::XEnumeration
> xEnum( createEnumeration() );
237 while ( xEnum
->hasMoreElements() )
239 uno::Reference
< msforms::XControl
> xControl( xEnum
->nextElement(), uno::UNO_QUERY_THROW
);
240 xControl
->setLeft( xControl
->getLeft() + cx
);
241 xControl
->setTop( xControl
->getTop() + cy
);
245 uno::Any SAL_CALL
ScVbaControls::Add( const uno::Any
& Object
, const uno::Any
& StringKey
, const uno::Any
& /*Before*/, const uno::Any
& /*After*/ )
248 OUString aComServiceName
;
252 if ( !mxDialog
.is() )
253 throw uno::RuntimeException();
255 uno::Reference
< awt::XControl
> xNewControl
;
256 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
258 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
260 Object
>>= aComServiceName
;
262 // TODO: Support Before and After?
264 StringKey
>>= aNewName
;
265 if ( aNewName
.isEmpty() )
267 aNewName
= aComServiceName
;
268 if ( aNewName
.isEmpty() )
269 aNewName
= "Control";
272 while( xDialogContainer
->hasByName( aNewName
) && (nInd
< SAL_MAX_INT32
) )
274 aNewName
= aComServiceName
+ OUString::number( nInd
++ );
278 double fDefWidth
= 72.0, fDefHeight
= 18.0;
279 if ( !aComServiceName
.isEmpty() )
281 // create a UNO control model based on the passed control type
282 uno::Reference
< awt::XControlModel
> xNewModel
;
283 bool bFontSupport
= false;
284 bool bNativeAX
= false;
285 if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CommandButton.1" ) )
287 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
288 fDefWidth
= 72.0; fDefHeight
= 24.0;
291 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Label.1" ) )
293 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlFixedTextModel" ), uno::UNO_QUERY_THROW
);
294 fDefWidth
= 72.0; fDefHeight
= 18.0;
297 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Image.1" ) )
299 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlImageControlModel" ), uno::UNO_QUERY_THROW
);
300 fDefWidth
= 72.0; fDefHeight
= 72.0;
302 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.CheckBox.1" ) )
304 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlCheckBoxModel" ), uno::UNO_QUERY_THROW
);
305 fDefWidth
= 108.0; fDefHeight
= 18.0;
308 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.OptionButton.1" ) )
310 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlRadioButtonModel" ), uno::UNO_QUERY_THROW
);
311 fDefWidth
= 108.0; fDefHeight
= 18.0;
314 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.TextBox.1" ) )
316 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlEditModel" ), uno::UNO_QUERY_THROW
);
317 fDefWidth
= 72.0; fDefHeight
= 18.0;
320 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ListBox.1" ) )
322 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlListBoxModel" ), uno::UNO_QUERY_THROW
);
323 fDefWidth
= 72.0; fDefHeight
= 18.0;
326 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ComboBox.1" ) )
328 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlComboBoxModel" ), uno::UNO_QUERY_THROW
);
329 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
330 xProps
->setPropertyValue( "Dropdown" , uno::Any( true ) );
331 fDefWidth
= 72.0; fDefHeight
= 18.0;
334 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ToggleButton.1" ) )
336 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlButtonModel" ), uno::UNO_QUERY_THROW
);
337 uno::Reference
< beans::XPropertySet
> xProps( xNewModel
, uno::UNO_QUERY_THROW
);
338 xProps
->setPropertyValue( "Toggle" , uno::Any( true ) );
339 fDefWidth
= 72.0; fDefHeight
= 18.0;
342 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.Frame.1" ) )
344 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlGroupBoxModel" ), uno::UNO_QUERY_THROW
);
345 fDefWidth
= 216.0; fDefHeight
= 144.0;
348 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.SpinButton.1" ) )
350 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlSpinButtonModel" ), uno::UNO_QUERY_THROW
);
351 fDefWidth
= 12.75; fDefHeight
= 25.5;
353 else if( aComServiceName
.equalsIgnoreAsciiCase( "Forms.ScrollBar.1" ) )
355 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.awt.UnoControlScrollBarModel" ), uno::UNO_QUERY_THROW
);
356 fDefWidth
= 12.75; fDefHeight
= 63.8;
360 xNewModel
.set( xModelFactory
->createInstance( "com.sun.star.custom.awt.UnoControlSystemAXContainerModel" ), uno::UNO_QUERY_THROW
);
361 fDefWidth
= 72.0; fDefHeight
= 18.0;
365 // need to set a few font properties to get rid of the default DONT_KNOW values
368 uno::Reference
< beans::XPropertySet
> xModelProps( xNewModel
, uno::UNO_QUERY_THROW
);
369 xModelProps
->setPropertyValue( "FontName" , uno::Any( OUString("Tahoma" ) ) );
370 xModelProps
->setPropertyValue( "FontHeight" , uno::Any( float( 8.0 ) ) );
371 xModelProps
->setPropertyValue( "FontWeight" , uno::Any( awt::FontWeight::NORMAL
) );
372 xModelProps
->setPropertyValue( "FontSlant" , uno::Any( awt::FontSlant_NONE
) );
373 xModelProps
->setPropertyValue( "FontUnderline" , uno::Any( awt::FontUnderline::NONE
) );
374 xModelProps
->setPropertyValue( "FontStrikeout" , uno::Any( awt::FontStrikeout::NONE
) );
377 xDialogContainer
->insertByName( aNewName
, uno::makeAny( xNewModel
) );
378 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
379 xNewControl
= xControlContainer
->getControl( aNewName
);
383 uno::Reference
< script::XInvocation
> xControlInvoke( xNewControl
, uno::UNO_QUERY_THROW
);
385 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(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
)
428 OUString aControlName
;
429 sal_Int32 nIndex
= -1;
430 if ( !mxDialog
.is() )
431 throw uno::RuntimeException();
433 uno::Reference
< lang::XMultiServiceFactory
> xModelFactory( mxDialog
->getModel(), uno::UNO_QUERY_THROW
);
434 uno::Reference
< container::XNameContainer
> xDialogContainer( xModelFactory
, uno::UNO_QUERY_THROW
);
436 if ( StringKeyOrIndex
>>= aControlName
)
438 if ( aControlName
.isEmpty() )
439 throw uno::RuntimeException();
441 else if ( StringKeyOrIndex
>>= nIndex
)
443 if (nIndex
>= 0 && nIndex
< m_xIndexAccess
->getCount() )
444 throw uno::RuntimeException();
447 throw uno::RuntimeException();
449 uno::Reference
< awt::XControl
> xControl
;
450 if ( !aControlName
.isEmpty() )
452 uno::Reference
< awt::XControlContainer
> xControlContainer( mxDialog
, uno::UNO_QUERY_THROW
);
453 xControl
= xControlContainer
->getControl( aControlName
);
457 m_xIndexAccess
->getByIndex( nIndex
) >>= xControl
;
460 if ( !xControl
.is() )
461 throw uno::RuntimeException();
463 if ( aControlName
.isEmpty() )
464 aControlName
= ControlArrayWrapper::getControlName( xControl
);
466 xDialogContainer
->removeByName( aControlName
);
469 catch (const uno::RuntimeException
&)
471 // the exceptions are not rethrown, impossibility to find or remove the control is currently not reported
472 // 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 catch (const uno::Exception
&)
478 // throw lang::WrappedTargetException("Can not create AXControl!",
479 // uno::Reference< uno::XInterface >(),
480 // uno::makeAny( e ) );
486 ScVbaControls::getElementType()
488 return cppu::UnoType
<ooo::vba::msforms::XControl
>::get();
491 VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaControls
, "ooo.vba.msforms.Controls" )
492 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */