cid#1640468 Dereference after null check
[LibreOffice.git] / forms / source / component / ListBox.cxx
blob35e63a69e9354850e2c72125f072fa25f976f120
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <config_features.h>
21 #include <config_fuzzers.h>
23 #include "ListBox.hxx"
24 #include <property.hxx>
25 #include <services.hxx>
26 #include <frm_resource.hxx>
27 #include <strings.hrc>
28 #include "BaseListBox.hxx"
29 #include <componenttools.hxx>
31 #include <com/sun/star/beans/PropertyAttribute.hpp>
32 #include <com/sun/star/form/FormComponentType.hpp>
33 #include <com/sun/star/container/XIndexAccess.hpp>
34 #include <com/sun/star/sdbc/XRow.hpp>
35 #include <com/sun/star/awt/XWindow.hpp>
36 #include <com/sun/star/sdbc/XConnection.hpp>
38 #include <comphelper/basicio.hxx>
39 #include <comphelper/property.hxx>
40 #include <comphelper/sequence.hxx>
41 #include <comphelper/string.hxx>
42 #include <comphelper/types.hxx>
43 #include <connectivity/dbtools.hxx>
44 #include <connectivity/formattedcolumnvalue.hxx>
45 #include <o3tl/any.hxx>
46 #include <o3tl/safeint.hxx>
47 #include <tools/debug.hxx>
48 #include <comphelper/diagnose_ex.hxx>
49 #include <sal/log.hxx>
50 #include <unotools/sharedunocomponent.hxx>
52 #include <optional>
54 #include <algorithm>
55 #include <iterator>
56 #include <climits>
58 namespace frm
60 using namespace ::com::sun::star::uno;
61 using namespace ::com::sun::star::sdb;
62 using namespace ::com::sun::star::sdbc;
63 using namespace ::com::sun::star::sdbcx;
64 using namespace ::com::sun::star::beans;
65 using namespace ::com::sun::star::container;
66 using namespace ::com::sun::star::form;
67 using namespace ::com::sun::star::awt;
68 using namespace ::com::sun::star::io;
69 using namespace ::com::sun::star::lang;
70 using namespace ::com::sun::star::util;
71 using namespace ::com::sun::star::form::binding;
72 using namespace ::dbtools;
74 using ::connectivity::ORowSetValue;
76 const ::connectivity::ORowSetValue OListBoxModel::s_aEmptyValue;
77 const ::connectivity::ORowSetValue OListBoxModel::s_aEmptyStringValue = OUString();
79 //= helper
81 namespace
84 struct RowSetValueToString
86 OUString operator()( const ORowSetValue& _value ) const
88 return _value.getString();
93 struct AppendRowSetValueString
95 explicit AppendRowSetValueString( OUString& _string )
96 :m_string( _string )
100 void operator()( const ORowSetValue& _append )
102 m_string += _append.getString();
105 private:
106 OUString& m_string;
110 Sequence< OUString > lcl_convertToStringSequence( const ValueList& _values )
112 Sequence< OUString > aStrings( _values.size() );
113 ::std::transform(
114 _values.begin(),
115 _values.end(),
116 aStrings.getArray(),
117 RowSetValueToString()
119 return aStrings;
124 //= ItemEventDescription
126 typedef ::comphelper::EventHolder< ItemEvent > ItemEventDescription;
129 //= OListBoxModel
131 Sequence< Type> OListBoxModel::_getTypes()
133 return TypeBag(
134 OBoundControlModel::_getTypes(),
135 OEntryListHelper::getTypes(),
136 OErrorBroadcaster::getTypes()
137 ).getTypes();
140 // stuff common to all constructors
141 void OListBoxModel::init()
143 startAggregatePropertyListening( PROPERTY_STRINGITEMLIST );
144 startAggregatePropertyListening( PROPERTY_TYPEDITEMLIST );
148 OListBoxModel::OListBoxModel(const Reference<XComponentContext>& _rxFactory)
149 :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_LISTBOX, FRM_SUN_CONTROL_LISTBOX, true, true, true )
150 // use the old control name for compatibility reasons
151 ,OEntryListHelper( static_cast<OControlModel&>(*this) )
152 ,OErrorBroadcaster( OComponentHelper::rBHelper )
153 ,m_nConvertedBoundValuesType(0)
154 ,m_nNULLPos(-1)
155 ,m_nBoundColumnType( DataType::SQLNULL )
158 m_nClassId = FormComponentType::LISTBOX;
159 m_eListSourceType = ListSourceType_VALUELIST;
160 m_aBoundColumn <<= sal_Int16(1);
161 initValueProperty( PROPERTY_SELECT_SEQ, PROPERTY_ID_SELECT_SEQ);
163 init();
167 OListBoxModel::OListBoxModel( const OListBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
168 :OBoundControlModel( _pOriginal, _rxFactory )
169 ,OEntryListHelper( *_pOriginal, static_cast<OControlModel&>(*this) )
170 ,OErrorBroadcaster( OComponentHelper::rBHelper )
171 ,m_eListSourceType( _pOriginal->m_eListSourceType )
172 ,m_aBoundColumn( _pOriginal->m_aBoundColumn )
173 ,m_aListSourceValues( _pOriginal->m_aListSourceValues )
174 ,m_aBoundValues( _pOriginal->m_aBoundValues )
175 ,m_nConvertedBoundValuesType(0)
176 ,m_aDefaultSelectSeq( _pOriginal->m_aDefaultSelectSeq )
177 ,m_nNULLPos(-1)
178 ,m_nBoundColumnType( DataType::SQLNULL )
181 init();
185 OListBoxModel::~OListBoxModel()
187 if (!OComponentHelper::rBHelper.bDisposed)
189 acquire();
190 dispose();
195 // XCloneable
197 css::uno::Reference< css::util::XCloneable > SAL_CALL OListBoxModel::createClone()
199 rtl::Reference<OListBoxModel> pClone = new OListBoxModel(this, getContext());
200 pClone->clonedFrom(this);
201 return pClone;
204 // XServiceInfo
206 css::uno::Sequence<OUString> SAL_CALL OListBoxModel::getSupportedServiceNames()
208 css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
210 sal_Int32 nOldLen = aSupported.getLength();
211 aSupported.realloc( nOldLen + 9 );
212 OUString* pStoreTo = aSupported.getArray() + nOldLen;
214 *pStoreTo++ = BINDABLE_CONTROL_MODEL;
215 *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
216 *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
218 *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
219 *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
221 *pStoreTo++ = FRM_SUN_COMPONENT_LISTBOX;
222 *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_LISTBOX;
223 *pStoreTo++ = BINDABLE_DATABASE_LIST_BOX;
225 *pStoreTo++ = FRM_COMPONENT_LISTBOX;
227 return aSupported;
231 Any SAL_CALL OListBoxModel::queryAggregation(const Type& _rType)
233 Any aReturn = OBoundControlModel::queryAggregation( _rType );
234 if ( !aReturn.hasValue() )
235 aReturn = OEntryListHelper::queryInterface( _rType );
236 if ( !aReturn.hasValue() )
237 aReturn = OErrorBroadcaster::queryInterface( _rType );
238 return aReturn;
241 // OComponentHelper
243 void OListBoxModel::disposing()
245 OBoundControlModel::disposing();
246 OEntryListHelper::disposing();
247 OErrorBroadcaster::disposing();
251 void OListBoxModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
253 switch (_nHandle)
255 case PROPERTY_ID_BOUNDCOLUMN:
256 _rValue = m_aBoundColumn;
257 break;
259 case PROPERTY_ID_LISTSOURCETYPE:
260 _rValue <<= m_eListSourceType;
261 break;
263 case PROPERTY_ID_LISTSOURCE:
264 _rValue <<= lcl_convertToStringSequence( m_aListSourceValues );
265 break;
267 case PROPERTY_ID_VALUE_SEQ:
268 _rValue <<= lcl_convertToStringSequence( m_aBoundValues );
269 break;
271 case PROPERTY_ID_SELECT_VALUE_SEQ:
272 _rValue <<= getCurrentMultiValue();
273 break;
275 case PROPERTY_ID_SELECT_VALUE:
276 _rValue = getCurrentSingleValue();
277 break;
279 case PROPERTY_ID_DEFAULT_SELECT_SEQ:
280 _rValue <<= m_aDefaultSelectSeq;
281 break;
283 case PROPERTY_ID_STRINGITEMLIST:
284 _rValue <<= comphelper::containerToSequence(getStringItemList());
285 break;
287 case PROPERTY_ID_TYPEDITEMLIST:
288 _rValue <<= getTypedItemList();
289 break;
291 default:
292 OBoundControlModel::getFastPropertyValue(_rValue, _nHandle);
297 void OListBoxModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
299 switch (_nHandle)
301 case PROPERTY_ID_BOUNDCOLUMN :
302 DBG_ASSERT((_rValue.getValueTypeClass() == TypeClass_SHORT) || (_rValue.getValueTypeClass() == TypeClass_VOID),
303 "OListBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
304 m_aBoundColumn = _rValue;
305 break;
307 case PROPERTY_ID_LISTSOURCETYPE :
308 DBG_ASSERT(_rValue.getValueType().equals(::cppu::UnoType<ListSourceType>::get()),
309 "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
310 _rValue >>= m_eListSourceType;
311 break;
313 case PROPERTY_ID_LISTSOURCE:
315 // extract
316 Sequence< OUString > aListSource;
317 OSL_VERIFY( _rValue >>= aListSource );
319 // copy to member
320 ValueList().swap(m_aListSourceValues);
321 ::std::copy(
322 std::cbegin(aListSource),
323 std::cend(aListSource),
324 ::std::insert_iterator< ValueList >( m_aListSourceValues, m_aListSourceValues.end() )
327 // propagate
328 if ( m_eListSourceType == ListSourceType_VALUELIST )
330 setBoundValues(std::vector(m_aListSourceValues));
332 else
334 if ( m_xCursor.is() && !hasField() && !hasExternalListSource() )
335 // listbox is already connected to a database, and no external list source
336 // data source changed -> refresh
337 loadData( false );
340 break;
342 case PROPERTY_ID_VALUE_SEQ :
343 SAL_WARN( "forms.component", "ValueItemList is read-only!" );
344 throw PropertyVetoException();
346 case PROPERTY_ID_SELECT_VALUE_SEQ :
348 Sequence< const Any > v;
349 _rValue >>= v;
350 Any newSelectSeq(translateBindingValuesToControlValue(v));
351 setControlValue( newSelectSeq, eOther );
353 break;
355 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
356 case PROPERTY_ID_SELECT_VALUE :
358 ORowSetValue v;
359 v.fill(_rValue);
360 Any newSelectSeq(translateDbValueToControlValue(v));
361 setControlValue( newSelectSeq, eOther );
363 break;
364 #endif
365 case PROPERTY_ID_DEFAULT_SELECT_SEQ :
366 DBG_ASSERT(_rValue.getValueType().equals(cppu::UnoType<Sequence<sal_Int16>>::get()),
367 "OListBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
368 _rValue >>= m_aDefaultSelectSeq;
370 DBG_ASSERT(m_xAggregateFastSet.is(), "OListBoxModel::setFastPropertyValue_NoBroadcast(DEFAULT_SELECT_SEQ) : invalid aggregate !");
371 if ( m_xAggregateFastSet.is() )
372 setControlValue( _rValue, eOther );
373 break;
375 case PROPERTY_ID_STRINGITEMLIST:
377 ControlModelLock aLock( *this );
378 setNewStringItemList( _rValue, aLock );
379 // TODO: this is bogus. setNewStringItemList expects a guard which has the *only*
380 // lock to the mutex, but setFastPropertyValue_NoBroadcast is already called with
381 // a lock - so we effectively has two locks here, of which setNewStringItemList can
382 // only control one.
384 resetNoBroadcast();
385 break;
387 case PROPERTY_ID_TYPEDITEMLIST:
389 ControlModelLock aLock( *this );
390 setNewTypedItemList( _rValue, aLock );
391 // Same TODO as above.
393 resetNoBroadcast();
394 break;
396 default:
397 OBoundControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
402 sal_Bool OListBoxModel::convertFastPropertyValue(
403 Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
405 bool bModified(false);
406 switch (_nHandle)
408 case PROPERTY_ID_BOUNDCOLUMN :
409 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aBoundColumn, ::cppu::UnoType<sal_Int16>::get());
410 break;
412 case PROPERTY_ID_LISTSOURCETYPE:
413 bModified = tryPropertyValueEnum(_rConvertedValue, _rOldValue, _rValue, m_eListSourceType);
414 break;
416 case PROPERTY_ID_LISTSOURCE:
417 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, lcl_convertToStringSequence( m_aListSourceValues ) );
418 break;
420 case PROPERTY_ID_VALUE_SEQ :
421 SAL_WARN( "forms.component", "ValueItemList is read-only!" );
422 throw IllegalArgumentException();
424 case PROPERTY_ID_SELECT_VALUE_SEQ :
425 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, getCurrentMultiValue());
426 break;
428 case PROPERTY_ID_SELECT_VALUE :
430 // Any from connectivity::ORowSetValue
431 Any aCurrentValue = getCurrentSingleValue();
432 if (aCurrentValue != _rValue)
434 _rOldValue = std::move(aCurrentValue);
435 _rConvertedValue = _rValue;
436 bModified = true;
438 break;
440 case PROPERTY_ID_DEFAULT_SELECT_SEQ :
441 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aDefaultSelectSeq);
442 break;
444 case PROPERTY_ID_STRINGITEMLIST:
445 bModified = convertNewListSourceProperty( _rConvertedValue, _rOldValue, _rValue );
446 break;
448 case PROPERTY_ID_TYPEDITEMLIST :
449 if (hasExternalListSource())
450 throw IllegalArgumentException();
451 bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, getTypedItemList());
452 break;
454 default:
455 return OBoundControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
457 return bModified;
461 void SAL_CALL OListBoxModel::setPropertyValues( const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues )
463 // if both SelectedItems and StringItemList are set, care for this
464 // #i27024#
465 const Any* pSelectSequenceValue = nullptr;
467 const OUString* pSelectedItemsPos = std::find(
468 _rPropertyNames.begin(), _rPropertyNames.end(), PROPERTY_SELECT_SEQ
470 auto aStringItemListExists = std::any_of(
471 _rPropertyNames.begin(), _rPropertyNames.end(),
472 [](OUString const & s) { return s == PROPERTY_STRINGITEMLIST; }
474 if ( ( pSelectedItemsPos != _rPropertyNames.end() ) && aStringItemListExists )
476 if (_rPropertyNames.getLength() != _rValues.getLength())
477 throw css::lang::IllegalArgumentException(u"lengths do not match"_ustr,
478 static_cast<cppu::OWeakObject*>(this), -1);
480 // both properties are present
481 // -> remember the value for the select sequence
482 pSelectSequenceValue = _rValues.getConstArray() + ( pSelectedItemsPos - _rPropertyNames.begin() );
485 OBoundControlModel::setPropertyValues( _rPropertyNames, _rValues );
487 if ( pSelectSequenceValue )
489 setPropertyValue( PROPERTY_SELECT_SEQ, *pSelectSequenceValue );
490 // Note that this is the only reliable way, since one of the properties is implemented
491 // by ourself, and one is implemented by the aggregate, we cannot rely on any particular
492 // results when setting them both - too many undocumented behavior in all the involved
498 void OListBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const
500 OBoundControlModel::describeFixedProperties( _rProps );
501 sal_Int32 nOldCount = _rProps.getLength();
502 _rProps.realloc( nOldCount + 10);
503 css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
504 *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
505 *pProperties++ = css::beans::Property(PROPERTY_BOUNDCOLUMN, PROPERTY_ID_BOUNDCOLUMN, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
506 *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE, cppu::UnoType<ListSourceType>::get(), css::beans::PropertyAttribute::BOUND);
507 *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE, cppu::UnoType<css::uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND);
508 *pProperties++ = css::beans::Property(PROPERTY_VALUE_SEQ, PROPERTY_ID_VALUE_SEQ, cppu::UnoType<css::uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
509 *pProperties++ = css::beans::Property(PROPERTY_SELECT_VALUE_SEQ, PROPERTY_ID_SELECT_VALUE_SEQ, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
510 *pProperties++ = css::beans::Property(PROPERTY_SELECT_VALUE, PROPERTY_ID_SELECT_VALUE, cppu::UnoType<Any>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
511 *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_SELECT_SEQ, PROPERTY_ID_DEFAULT_SELECT_SEQ, cppu::UnoType<Sequence<sal_Int16>>::get(), css::beans::PropertyAttribute::BOUND);
512 *pProperties++ = css::beans::Property(PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
513 *pProperties++ = css::beans::Property(PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::OPTIONAL);
514 DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
518 void OListBoxModel::_propertyChanged( const PropertyChangeEvent& i_rEvent )
520 if ( i_rEvent.PropertyName == PROPERTY_STRINGITEMLIST )
522 ControlModelLock aLock( *this );
523 // SYNCHRONIZED ----->
524 // our aggregate internally changed its StringItemList property - reflect this in our "overridden"
525 // version of the property
526 setNewStringItemList( i_rEvent.NewValue, aLock );
527 // <----- SYNCHRONIZED
528 return;
530 else if ( i_rEvent.PropertyName == PROPERTY_TYPEDITEMLIST )
532 ControlModelLock aLock( *this );
533 // SYNCHRONIZED ----->
534 // our aggregate internally changed its TypedItemList property - reflect this in our "overridden"
535 // version of the property
536 setNewTypedItemList( i_rEvent.NewValue, aLock );
537 // <----- SYNCHRONIZED
538 return;
540 OBoundControlModel::_propertyChanged( i_rEvent );
544 void OListBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
546 OBoundControlModel::describeAggregateProperties( _rAggregateProps );
548 // superseded properties:
549 RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST );
550 RemoveProperty( _rAggregateProps, PROPERTY_TYPEDITEMLIST );
554 OUString SAL_CALL OListBoxModel::getServiceName()
556 return FRM_COMPONENT_LISTBOX; // old (non-sun) name for compatibility !
560 void SAL_CALL OListBoxModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
562 OBoundControlModel::write(_rxOutStream);
564 // Dummy sequence, to stay compatible if SelectSeq is not saved anymore
565 Sequence<sal_Int16> aDummySeq;
567 // Version
568 // Version 0x0002: ListSource becomes StringSeq
569 _rxOutStream->writeShort(0x0004);
571 // Masking for any
572 sal_uInt16 nAnyMask = 0;
573 if (m_aBoundColumn.getValueTypeClass() != TypeClass_VOID)
574 nAnyMask |= BOUNDCOLUMN;
576 _rxOutStream << nAnyMask;
578 _rxOutStream << lcl_convertToStringSequence( m_aListSourceValues );
579 _rxOutStream << static_cast<sal_Int16>(m_eListSourceType);
580 _rxOutStream << aDummySeq;
581 _rxOutStream << m_aDefaultSelectSeq;
583 if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
585 sal_Int16 nBoundColumn = 0;
586 m_aBoundColumn >>= nBoundColumn;
587 _rxOutStream << nBoundColumn;
589 writeHelpTextCompatibly(_rxOutStream);
591 // from version 0x0004 : common properties
592 writeCommonProperties(_rxOutStream);
596 void SAL_CALL OListBoxModel::read(const Reference<XObjectInputStream>& _rxInStream)
598 // We need to respect dependencies for certain variables.
599 // Therefore, we need to set them explicitly via setPropertyValue().
601 OBoundControlModel::read(_rxInStream);
602 ControlModelLock aLock( *this );
604 // since we are "overwriting" the StringItemList of our aggregate (means we have
605 // an own place to store the value, instead of relying on our aggregate storing it),
606 // we need to respect what the aggregate just read for the StringItemList property.
609 if ( m_xAggregateSet.is() )
610 setNewStringItemList( m_xAggregateSet->getPropertyValue( PROPERTY_STRINGITEMLIST ), aLock );
612 catch( const Exception& )
614 TOOLS_WARN_EXCEPTION( "forms.component", "OComboBoxModel::read: caught an exception while examining the aggregate's string item list" );
617 // Version
618 sal_uInt16 nVersion = _rxInStream->readShort();
619 DBG_ASSERT(nVersion > 0, "OListBoxModel::read : version 0 ? this should never have been written !");
621 if (nVersion > 0x0004)
623 SAL_WARN( "forms.component", "OListBoxModel::read : invalid (means unknown) version !");
624 ValueList().swap(m_aListSourceValues);
625 m_aBoundColumn <<= sal_Int16(0);
626 clearBoundValues();
627 m_eListSourceType = ListSourceType_VALUELIST;
628 m_aDefaultSelectSeq.realloc(0);
629 defaultCommonProperties();
630 return;
633 // Masking for any
634 sal_uInt16 nAnyMask;
635 _rxInStream >> nAnyMask;
637 // ListSourceSeq
638 css::uno::Sequence<OUString> aListSourceSeq;
639 if (nVersion == 0x0001)
641 // Create ListSourceSeq from String
642 OUString sListSource;
643 _rxInStream >> sListSource;
645 const sal_Int32 nTokens{ comphelper::string::getTokenCount(sListSource, ';') };
646 aListSourceSeq.realloc( nTokens );
647 sal_Int32 nIdx{ 0 };
648 for (sal_Int32 i=0; i<nTokens; ++i)
650 aListSourceSeq.getArray()[i] = sListSource.getToken(0, ';', nIdx);
653 else
654 _rxInStream >> aListSourceSeq;
656 sal_Int16 nListSourceType;
657 _rxInStream >> nListSourceType;
658 m_eListSourceType = static_cast<ListSourceType>(nListSourceType);
659 Any aListSourceSeqAny;
660 aListSourceSeqAny <<= aListSourceSeq;
662 setFastPropertyValue(PROPERTY_ID_LISTSOURCE, aListSourceSeqAny );
664 // Dummy sequence, to stay compatible if SelectSeq is not saved anymore
665 Sequence<sal_Int16> aDummySeq;
666 _rxInStream >> aDummySeq;
668 // DefaultSelectSeq
669 Sequence<sal_Int16> aDefaultSelectSeq;
670 _rxInStream >> aDefaultSelectSeq;
671 Any aDefaultSelectSeqAny;
672 aDefaultSelectSeqAny <<= aDefaultSelectSeq;
673 setFastPropertyValue(PROPERTY_ID_DEFAULT_SELECT_SEQ, aDefaultSelectSeqAny);
675 // BoundColumn
676 if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
678 sal_Int16 nValue;
679 _rxInStream >> nValue;
680 m_aBoundColumn <<= nValue;
682 else // the constructor initialises to 1, so if it is empty,
683 // we must explicitly set to empty
685 m_aBoundColumn = Any();
688 if (nVersion > 2)
689 readHelpTextCompatibly(_rxInStream);
691 // if our string list is not filled from the value list, we must empty it
692 // this can be the case when somebody saves in alive mode
693 if ( ( m_eListSourceType != ListSourceType_VALUELIST )
694 && !hasExternalListSource()
697 setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
698 setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
701 if (nVersion > 3)
702 readCommonProperties(_rxInStream);
704 // Display the default values after reading
705 if ( !getControlSource().isEmpty() )
706 // (not if we don't have a control source - the "State" property acts like it is persistent, then
707 resetNoBroadcast();
711 void OListBoxModel::loadData( bool _bForce )
713 SAL_INFO( "forms.component", "OListBoxModel::loadData" );
714 DBG_ASSERT( m_eListSourceType != ListSourceType_VALUELIST, "OListBoxModel::loadData: cannot load value list from DB!" );
715 DBG_ASSERT( !hasExternalListSource(), "OListBoxModel::loadData: cannot load from DB when I have an external list source!" );
717 const sal_Int16 nNULLPosBackup( m_nNULLPos );
718 const sal_Int32 nBoundColumnTypeBackup( m_nBoundColumnType );
719 m_nNULLPos = -1;
720 m_nBoundColumnType = DataType::SQLNULL;
722 // pre-requisites:
723 // PRE1: connection
724 Reference< XConnection > xConnection;
725 // is the active connection of our form
726 Reference< XPropertySet > xFormProps( m_xCursor, UNO_QUERY );
727 if ( xFormProps.is() )
728 xFormProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection;
730 // PRE2: list source
731 OUString sListSource;
732 // if our list source type is no value list, we need to concatenate
733 // the single list source elements
734 ::std::for_each(
735 m_aListSourceValues.begin(),
736 m_aListSourceValues.end(),
737 AppendRowSetValueString( sListSource )
740 // outta here if we don't have all pre-requisites
741 if ( !xConnection.is() || sListSource.isEmpty() )
743 clearBoundValues();
744 return;
747 ::std::optional< sal_Int16 > aBoundColumn(std::nullopt);
748 if ( m_aBoundColumn.getValueTypeClass() == TypeClass_SHORT )
750 sal_Int16 nBoundColumn( 0 );
751 m_aBoundColumn >>= nBoundColumn;
752 aBoundColumn = nBoundColumn;
755 ::utl::SharedUNOComponent< XResultSet > xListCursor;
758 m_aListRowSet.setConnection( xConnection );
760 bool bExecute = false;
761 switch (m_eListSourceType)
763 case ListSourceType_TABLEFIELDS:
764 // don't work with a statement here, the fields will be collected below
765 break;
767 case ListSourceType_TABLE:
769 Reference<XNameAccess> xFieldsByName = getTableFields(xConnection, sListSource);
770 Reference<XIndexAccess> xFieldsByIndex(xFieldsByName, UNO_QUERY);
772 // do we have a bound column if yes we have to select it
773 // and the displayed column is the first column otherwise we act as a combobox
774 OUString aFieldName;
775 OUString aBoundFieldName;
777 if ( !!aBoundColumn && ( *aBoundColumn >= 0 ) && xFieldsByIndex.is() )
779 if ( *aBoundColumn >= xFieldsByIndex->getCount() )
780 break;
782 Reference<XPropertySet> xFieldAsSet(xFieldsByIndex->getByIndex( *aBoundColumn ),UNO_QUERY);
783 assert(xFieldAsSet.is());
784 xFieldAsSet->getPropertyValue(PROPERTY_NAME) >>= aBoundFieldName;
785 aBoundColumn = 1;
787 xFieldAsSet.set(xFieldsByIndex->getByIndex(0),UNO_QUERY);
788 xFieldAsSet->getPropertyValue(PROPERTY_NAME) >>= aFieldName;
790 else if (xFieldsByName.is())
792 if ( xFieldsByName->hasByName( getControlSource() ) )
793 aFieldName = getControlSource();
794 else
796 // otherwise look for the alias
797 Reference< XColumnsSupplier > xSupplyFields;
798 xFormProps->getPropertyValue(u"SingleSelectQueryComposer"_ustr) >>= xSupplyFields;
800 // search the field
801 DBG_ASSERT(xSupplyFields.is(), "OListBoxModel::loadData : invalid query composer !");
803 Reference<XNameAccess> xFieldNames = xSupplyFields->getColumns();
804 if ( xFieldNames->hasByName( getControlSource() ) )
806 Reference<XPropertySet> xComposerFieldAsSet;
807 xFieldNames->getByName( getControlSource() ) >>= xComposerFieldAsSet;
808 if (hasProperty(PROPERTY_FIELDSOURCE, xComposerFieldAsSet))
809 xComposerFieldAsSet->getPropertyValue(PROPERTY_FIELDSOURCE) >>= aFieldName;
813 if (aFieldName.isEmpty())
814 break;
816 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
817 OUString aQuote = xMeta->getIdentifierQuoteString();
818 OUString aStatement(u"SELECT "_ustr);
819 if (aBoundFieldName.isEmpty()) // act like a combobox
820 aStatement += "DISTINCT ";
822 aStatement += quoteName(aQuote,aFieldName);
823 if (!aBoundFieldName.isEmpty())
825 aStatement += ", " + quoteName(aQuote, aBoundFieldName);
827 aStatement += " FROM ";
829 OUString sCatalog, sSchema, sTable;
830 qualifiedNameComponents( xMeta, sListSource, sCatalog, sSchema, sTable, EComposeRule::InDataManipulation );
831 aStatement += composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable );
833 m_aListRowSet.setEscapeProcessing( false );
834 m_aListRowSet.setCommand( aStatement );
835 bExecute = true;
837 break;
839 case ListSourceType_QUERY:
840 m_aListRowSet.setCommandFromQuery( sListSource );
841 bExecute = true;
842 break;
844 default:
845 m_aListRowSet.setEscapeProcessing( ListSourceType_SQLPASSTHROUGH != m_eListSourceType );
846 m_aListRowSet.setCommand( sListSource );
847 bExecute = true;
848 break;
851 if (bExecute)
853 if ( !_bForce && !m_aListRowSet.isDirty() )
855 // if none of the settings of the row set changed, compared to the last
856 // invocation of loadData, then don't re-fill the list. Instead, assume
857 // the list entries are the same.
858 m_nNULLPos = nNULLPosBackup;
859 m_nBoundColumnType = nBoundColumnTypeBackup;
860 return;
862 xListCursor.reset( m_aListRowSet.execute() );
865 catch(const SQLException& eSQL)
867 onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
868 return;
870 catch(const Exception&)
872 return;
875 // Fill display and value lists
876 ValueList aDisplayList, aValueList;
877 bool bUseNULL = hasField() && !isRequired();
879 // empty BoundColumn is treated as BoundColumn==0,
880 if(!aBoundColumn)
881 aBoundColumn = 0;
885 OSL_ENSURE( xListCursor.is() || ( ListSourceType_TABLEFIELDS == m_eListSourceType ),
886 "OListBoxModel::loadData: logic error!" );
887 if ( !xListCursor.is() && ( ListSourceType_TABLEFIELDS != m_eListSourceType ) )
888 return;
890 switch (m_eListSourceType)
892 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
893 case ListSourceType_SQL:
894 case ListSourceType_SQLPASSTHROUGH:
895 case ListSourceType_TABLE:
896 case ListSourceType_QUERY:
898 // Get field of the ResultSet's 1st column
899 Reference<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
900 DBG_ASSERT(xSupplyCols.is(), "OListBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
901 Reference<XIndexAccess> xColumns;
902 if (xSupplyCols.is())
904 xColumns.set(xSupplyCols->getColumns(), UNO_QUERY);
905 DBG_ASSERT(xColumns.is(), "OListBoxModel::loadData : no columns supplied by the row set !");
908 Reference< XPropertySet > xDataField;
909 if ( xColumns.is() )
910 xColumns->getByIndex(0) >>= xDataField;
911 if ( !xDataField.is() )
912 return;
914 ::dbtools::FormattedColumnValue aValueFormatter( getContext(), m_xCursor, xDataField );
916 // Get the field of BoundColumn of the ResultSet
917 m_nBoundColumnType = DataType::SQLNULL;
918 if ( *aBoundColumn >= 0 )
922 Reference< XPropertySet > xBoundField( xColumns->getByIndex( *aBoundColumn ), UNO_QUERY_THROW );
923 OSL_VERIFY( xBoundField->getPropertyValue(u"Type"_ustr) >>= m_nBoundColumnType );
925 catch( const Exception& )
927 DBG_UNHANDLED_EXCEPTION("forms.component");
930 else if ( *aBoundColumn == -1)
931 m_nBoundColumnType = DataType::SMALLINT;
933 // If the LB is bound to a field and empty entries are valid, we remember the position
934 // for an empty entry
935 SAL_INFO( "forms.component", "OListBoxModel::loadData: string collection" );
936 OUString aStr;
937 sal_Int16 entryPos = 0;
938 ORowSetValue aBoundValue;
939 Reference< XRow > xCursorRow( xListCursor, UNO_QUERY_THROW );
940 while ( xListCursor->next() && ( entryPos++ < SHRT_MAX ) ) // SHRT_MAX is the maximum number of entries
942 aStr = aValueFormatter.getFormattedValue();
943 aDisplayList.emplace_back(aStr );
945 if(*aBoundColumn >= 0)
946 aBoundValue.fill( *aBoundColumn + 1, m_nBoundColumnType, xCursorRow );
947 else
948 // -1 because getRow() is 1-indexed, but ListBox positions are 0-indexed
949 aBoundValue = static_cast<sal_Int16>(xListCursor->getRow()-1);
950 aValueList.push_back( aBoundValue );
952 if ( m_nNULLPos == -1 && aBoundValue.isNull() )
953 m_nNULLPos = sal_Int16( aDisplayList.size() - 1 );
954 if ( bUseNULL && ( m_nNULLPos == -1 ) && aStr.isEmpty() )
955 // There is already a non-NULL entry with empty display string;
956 // adding another one for NULL would make things confusing,
957 // so back off.
958 bUseNULL = false;
961 break;
962 #endif
963 case ListSourceType_TABLEFIELDS:
965 Reference<XNameAccess> xFieldNames = getTableFields(xConnection, sListSource);
966 if (xFieldNames.is())
968 const css::uno::Sequence<OUString> seqNames = xFieldNames->getElementNames();
969 ::std::copy(
970 seqNames.begin(),
971 seqNames.end(),
972 ::std::insert_iterator< ValueList >( aDisplayList, aDisplayList.end() )
974 if(*aBoundColumn == -1)
976 // the type of i matters! It will be the type of the ORowSetValue pushed to aValueList!
977 for(size_t i=0; i < aDisplayList.size(); ++i)
979 aValueList.emplace_back(sal_Int16(i));
982 else
984 aValueList = aDisplayList;
988 break;
989 default:
990 SAL_WARN( "forms.component", "OListBoxModel::loadData: unreachable!" );
991 break;
994 catch(const SQLException& eSQL)
996 onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
997 return;
999 catch( const Exception& )
1001 DBG_UNHANDLED_EXCEPTION("forms.component");
1002 return;
1006 // Create Values sequence
1007 // Add NULL entry
1008 if (bUseNULL && m_nNULLPos == -1)
1010 aValueList.insert( aValueList.begin(), ORowSetValue() );
1012 aDisplayList.insert( aDisplayList.begin(), ORowSetValue( OUString() ) );
1013 m_nNULLPos = 0;
1016 setBoundValues(std::move(aValueList));
1018 setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( lcl_convertToStringSequence( aDisplayList ) ) );
1019 setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
1023 void OListBoxModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
1025 // list boxes which are bound to a db column don't have multi selection
1026 // - this would be unable to reflect in the db column
1027 if ( hasField() )
1029 setFastPropertyValue( PROPERTY_ID_MULTISELECTION, css::uno::Any(false) );
1032 if ( !hasExternalListSource() )
1033 impl_refreshDbEntryList( false );
1037 void OListBoxModel::onDisconnectedDbColumn()
1039 clearBoundValues();
1040 m_nNULLPos = -1;
1041 m_nBoundColumnType = DataType::SQLNULL;
1043 if ( m_eListSourceType != ListSourceType_VALUELIST )
1045 if ( !hasExternalListSource() )
1046 setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
1048 m_aListRowSet.dispose();
1053 void OListBoxModel::setBoundValues(ValueList && l)
1055 m_aConvertedBoundValues.clear();
1056 m_aBoundValues = std::move(l);
1060 void OListBoxModel::clearBoundValues()
1062 ValueList().swap(m_aConvertedBoundValues);
1063 ValueList().swap(m_aBoundValues);
1067 void OListBoxModel::convertBoundValues(const sal_Int32 nFieldType) const
1069 assert(s_aEmptyValue.isNull());
1070 m_nNULLPos = -1;
1071 m_aConvertedBoundValues.resize(m_aBoundValues.size());
1072 ValueList::iterator dst = m_aConvertedBoundValues.begin();
1073 sal_Int16 nPos = 0;
1074 for (auto const& src : m_aBoundValues)
1076 if(m_nNULLPos == -1 &&
1077 !isRequired() &&
1078 (src == s_aEmptyStringValue || src == s_aEmptyValue || src.isNull()) )
1080 m_nNULLPos = nPos;
1081 dst->setNull();
1083 else
1085 *dst = src;
1087 dst->setTypeKind(nFieldType);
1088 ++dst;
1089 ++nPos;
1091 m_nConvertedBoundValuesType = nFieldType;
1092 OSL_ENSURE(dst == m_aConvertedBoundValues.end(), "OListBoxModel::convertBoundValues expected to have overwritten all of m_aConvertedBoundValues, but did not.");
1093 assert(dst == m_aConvertedBoundValues.end());
1096 sal_Int32 OListBoxModel::getValueType() const
1098 return (m_nBoundColumnType != css::sdbc::DataType::SQLNULL) ?
1099 m_nBoundColumnType :
1100 ( hasField() ? getFieldType() : DataType::VARCHAR);
1103 ValueList OListBoxModel::impl_getValues() const
1105 const sal_Int32 nFieldType = getValueType();
1107 if ( !m_aConvertedBoundValues.empty() && m_nConvertedBoundValuesType == nFieldType )
1108 return m_aConvertedBoundValues;
1110 if ( !m_aBoundValues.empty() )
1112 convertBoundValues(nFieldType);
1113 return m_aConvertedBoundValues;
1116 const std::vector< OUString >& aStringItems( getStringItemList() );
1117 ValueList aValues( aStringItems.size() );
1118 ValueList::iterator dst = aValues.begin();
1119 for (auto const& src : aStringItems)
1121 *dst = src;
1122 dst->setTypeKind(nFieldType);
1123 ++dst;
1125 m_nConvertedBoundValuesType = nFieldType;
1126 OSL_ENSURE(dst == aValues.end(), "OListBoxModel::impl_getValues expected to have set all of aValues, but did not.");
1127 assert(dst == aValues.end());
1128 return aValues;
1131 ORowSetValue OListBoxModel::getFirstSelectedValue() const
1133 DBG_ASSERT( m_xAggregateFastSet.is(), "OListBoxModel::getFirstSelectedValue: invalid aggregate!" );
1134 if ( !m_xAggregateFastSet.is() )
1135 return s_aEmptyValue;
1137 Sequence< sal_Int16 > aSelectedIndices;
1138 OSL_VERIFY( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) >>= aSelectedIndices );
1139 if ( !aSelectedIndices.hasElements() )
1140 // nothing selected at all
1141 return s_aEmptyValue;
1143 if ( ( m_nNULLPos != -1 ) && ( aSelectedIndices[0] == m_nNULLPos ) )
1144 // the dedicated "NULL" entry is selected
1145 return s_aEmptyValue;
1147 ValueList aValues( impl_getValues() );
1149 size_t selectedValue = aSelectedIndices[0];
1150 if ( selectedValue >= aValues.size() )
1152 SAL_WARN( "forms.component", "OListBoxModel::getFirstSelectedValue: inconsistent selection/valuelist!" );
1153 return s_aEmptyValue;
1156 return aValues[ selectedValue ];
1160 bool OListBoxModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
1162 // current selection list
1163 const ORowSetValue aCurrentValue( getFirstSelectedValue() );
1164 if ( aCurrentValue != m_aSaveValue )
1166 if ( aCurrentValue.isNull() )
1167 m_xColumnUpdate->updateNull();
1168 else
1172 m_xColumnUpdate->updateObject( aCurrentValue.makeAny() );
1174 catch ( const Exception& )
1176 return false;
1179 m_aSaveValue = aCurrentValue;
1181 return true;
1185 Sequence< sal_Int16 > OListBoxModel::translateDbValueToControlValue(const ORowSetValue &i_aValue) const
1187 Sequence< sal_Int16 > aSelectionIndicies;
1189 // reset selection for NULL values
1190 if ( i_aValue.isNull() )
1192 if ( m_nNULLPos != -1 )
1194 aSelectionIndicies = { m_nNULLPos };
1197 else
1199 ValueList aValues( impl_getValues() );
1200 assert( m_nConvertedBoundValuesType == getValueType());
1201 ORowSetValue v(i_aValue);
1202 v.setTypeKind( m_nConvertedBoundValuesType );
1203 ValueList::const_iterator curValuePos = ::std::find( aValues.begin(), aValues.end(), v );
1204 if ( curValuePos != aValues.end() )
1206 aSelectionIndicies = { o3tl::narrowing<sal_Int16>(curValuePos - aValues.begin()) };
1210 return aSelectionIndicies;
1213 Sequence< sal_Int16 > OListBoxModel::translateBindingValuesToControlValue(const Sequence< const Any > &i_aValues) const
1215 const ValueList aValues( impl_getValues() );
1216 assert( m_nConvertedBoundValuesType == getValueType());
1217 Sequence< sal_Int16 > aSelectionIndicies(i_aValues.getLength());
1219 sal_Int32 nCount(0);
1221 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
1222 sal_Int16 *pIndex = aSelectionIndicies.getArray();
1223 for ( auto const & value : i_aValues)
1225 if ( value.hasValue() )
1227 ORowSetValue v;
1228 v.fill(value);
1229 v.setTypeKind( m_nConvertedBoundValuesType );
1230 ValueList::const_iterator curValuePos = ::std::find( aValues.begin(), aValues.end(), v );
1231 if ( curValuePos != aValues.end() )
1233 *pIndex = curValuePos - aValues.begin();
1234 ++pIndex;
1235 ++nCount;
1238 else
1240 if ( m_nNULLPos != -1 )
1242 *pIndex = m_nNULLPos;
1243 ++pIndex;
1244 ++nCount;
1248 assert(aSelectionIndicies.getArray() + nCount == pIndex);
1249 #endif
1250 aSelectionIndicies.realloc(nCount);
1251 return aSelectionIndicies;
1254 Any OListBoxModel::translateDbColumnToControlValue()
1256 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
1257 Reference< XPropertySet > xBoundField( getField() );
1258 if ( !xBoundField.is() )
1260 SAL_WARN( "forms.component", "OListBoxModel::translateDbColumnToControlValue: no field? How could that happen?!" );
1261 return Any();
1264 ORowSetValue aCurrentValue;
1265 aCurrentValue.fill( getValueType(), m_xColumn );
1267 m_aSaveValue = aCurrentValue;
1269 return Any( translateDbValueToControlValue(aCurrentValue) );
1270 #else
1271 return Any();
1272 #endif
1275 // XReset
1277 Any OListBoxModel::getDefaultForReset() const
1279 Any aValue;
1280 if (m_aDefaultSelectSeq.hasElements())
1281 aValue <<= m_aDefaultSelectSeq;
1282 else if (m_nNULLPos != -1) // bound Listbox
1284 Sequence<sal_Int16> aSeq { m_nNULLPos };
1285 aValue <<= aSeq;
1287 else
1289 Sequence<sal_Int16> aSeq;
1290 aValue <<= aSeq;
1293 return aValue;
1297 void OListBoxModel::resetNoBroadcast()
1299 OBoundControlModel::resetNoBroadcast();
1300 m_aSaveValue.setNull();
1304 void SAL_CALL OListBoxModel::disposing( const EventObject& _rSource )
1306 if ( !OEntryListHelper::handleDisposing( _rSource ) )
1307 OBoundControlModel::disposing( _rSource );
1311 namespace
1313 // The type of how we should transfer our selection to external value bindings
1314 enum ExchangeType
1316 eIndexList, /// as list of indexes of selected entries
1317 eIndex, /// as index of the selected entry
1318 eEntryList, /// as list of string representations of selected *display* entries
1319 eEntry, /// as string representation of the selected *display* entry
1320 eValueList, /// as list of string representations of selected values
1321 eValue /// as string representation of the selected value
1325 ExchangeType lcl_getCurrentExchangeType( const Type& _rExchangeType )
1327 switch ( _rExchangeType.getTypeClass() )
1329 case TypeClass_ANY:
1330 return eValue;
1331 case TypeClass_STRING:
1332 return eEntry;
1333 case TypeClass_LONG:
1334 return eIndex;
1335 case TypeClass_SEQUENCE:
1337 Type aElementType = ::comphelper::getSequenceElementType( _rExchangeType );
1338 switch ( aElementType.getTypeClass() )
1340 case TypeClass_ANY:
1341 return eValueList;
1342 case TypeClass_STRING:
1343 return eEntryList;
1344 case TypeClass_LONG:
1345 return eIndexList;
1346 default:
1347 break;
1349 break;
1351 default:
1352 break;
1354 SAL_WARN( "forms.component", "lcl_getCurrentExchangeType: unsupported (unexpected) exchange type!" );
1355 return eEntry;
1360 Any OListBoxModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
1362 Sequence< sal_Int16 > aSelectIndexes;
1364 switch ( lcl_getCurrentExchangeType( getExternalValueType() ) )
1366 case eValueList:
1368 Sequence< const Any > aExternalValues;
1369 OSL_VERIFY( _rExternalValue >>= aExternalValues );
1370 aSelectIndexes = translateBindingValuesToControlValue( aExternalValues );
1372 break;
1374 case eValue:
1375 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
1377 ORowSetValue v;
1378 v.fill(_rExternalValue);
1379 aSelectIndexes = translateDbValueToControlValue(v);
1381 #endif
1382 break;
1384 case eIndexList:
1386 // unfortunately, our select sequence is a sequence<short>, while the external binding
1387 // supplies sequence<int> only -> transform this
1388 Sequence< sal_Int32 > aSelectIndexesPure;
1389 OSL_VERIFY( _rExternalValue >>= aSelectIndexesPure );
1390 aSelectIndexes.realloc( aSelectIndexesPure.getLength() );
1391 ::std::copy(
1392 std::cbegin(aSelectIndexesPure),
1393 std::cend(aSelectIndexesPure),
1394 aSelectIndexes.getArray()
1397 break;
1399 case eIndex:
1401 sal_Int32 nSelectIndex = -1;
1402 OSL_VERIFY( _rExternalValue >>= nSelectIndex );
1403 if ( ( nSelectIndex >= 0 ) && ( o3tl::make_unsigned(nSelectIndex) < getStringItemList().size() ) )
1405 aSelectIndexes = { o3tl::narrowing<sal_Int16>(nSelectIndex) };
1408 break;
1410 case eEntryList:
1412 // we can retrieve a string list from the binding for multiple selection
1413 Sequence< OUString > aSelectEntries;
1414 OSL_VERIFY( _rExternalValue >>= aSelectEntries );
1416 ::std::set< sal_Int16 > aSelectionSet;
1418 // find the selection entries in our item list
1419 for (OUString const& selectEntry : aSelectEntries)
1421 int idx = 0;
1422 for(const OUString& s : getStringItemList())
1424 if (s==selectEntry)
1425 aSelectionSet.insert(idx);
1426 ++idx;
1430 // copy the indexes to the sequence
1431 aSelectIndexes = comphelper::containerToSequence( aSelectionSet );
1433 break;
1435 case eEntry:
1437 OUString sStringToSelect;
1438 OSL_VERIFY( _rExternalValue >>= sStringToSelect );
1439 ::std::set< sal_Int16 > aSelectionSet;
1440 int idx = 0;
1441 for(const OUString& s : getStringItemList())
1443 if (s==sStringToSelect)
1444 aSelectionSet.insert(idx);
1445 ++idx;
1448 aSelectIndexes = comphelper::containerToSequence( aSelectionSet );
1450 break;
1453 return Any( aSelectIndexes );
1457 namespace
1460 struct ExtractStringFromSequence_Safe
1462 protected:
1463 const std::vector< OUString >& m_rList;
1465 public:
1466 explicit ExtractStringFromSequence_Safe( const std::vector< OUString >& _rList ) : m_rList( _rList ) { }
1468 const OUString & operator ()( sal_Int16 _nIndex )
1470 OSL_ENSURE( _nIndex < static_cast<sal_Int32>(m_rList.size()), "ExtractStringFromSequence_Safe: inconsistence!" );
1471 if ( _nIndex < static_cast<sal_Int32>(m_rList.size()) )
1472 return m_rList[ _nIndex ];
1473 return EMPTY_OUSTRING;
1478 Any lcl_getSingleSelectedEntryTyped( const Sequence< sal_Int16 >& _rSelectSequence, const Sequence<Any>& _rTypedList )
1480 Any aReturn;
1482 // by definition, multiple selected entries are transferred as NULL if the
1483 // binding does not support lists
1484 if ( _rSelectSequence.getLength() <= 1 )
1486 if ( _rSelectSequence.getLength() == 1 )
1488 sal_Int32 nIndex = _rSelectSequence[0];
1489 if (0 <= nIndex && nIndex < _rTypedList.getLength())
1490 aReturn = _rTypedList[nIndex];
1494 return aReturn;
1498 Any lcl_getSingleSelectedEntry( const Sequence< sal_Int16 >& _rSelectSequence, const std::vector< OUString >& _rStringList )
1500 Any aReturn;
1502 // by definition, multiple selected entries are transferred as NULL if the
1503 // binding does not support string lists
1504 if ( _rSelectSequence.getLength() <= 1 )
1506 OUString sSelectedEntry;
1508 if ( _rSelectSequence.getLength() == 1 )
1509 sSelectedEntry = ExtractStringFromSequence_Safe( _rStringList )( _rSelectSequence[0] );
1511 aReturn <<= sSelectedEntry;
1514 return aReturn;
1518 Any lcl_getMultiSelectedEntries( const Sequence< sal_Int16 >& _rSelectSequence, const std::vector< OUString >& _rStringList )
1520 Sequence< OUString > aSelectedEntriesTexts( _rSelectSequence.getLength() );
1521 ::std::transform(
1522 _rSelectSequence.begin(),
1523 _rSelectSequence.end(),
1524 aSelectedEntriesTexts.getArray(),
1525 ExtractStringFromSequence_Safe( _rStringList )
1527 return Any( aSelectedEntriesTexts );
1531 struct ExtractAnyFromValueList_Safe
1533 protected:
1534 const ValueList& m_rList;
1536 public:
1537 explicit ExtractAnyFromValueList_Safe( const ValueList& _rList ) : m_rList( _rList ) { }
1539 Any operator ()( sal_Int16 _nIndex )
1541 OSL_ENSURE( o3tl::make_unsigned(_nIndex) < m_rList.size(), "ExtractAnyFromValueList: inconsistence!" );
1542 if ( o3tl::make_unsigned(_nIndex) < m_rList.size() )
1543 return m_rList[ _nIndex ].makeAny();
1544 return Any();
1549 Any lcl_getSingleSelectedEntryAny( const Sequence< sal_Int16 >& _rSelectSequence, const ValueList& _rStringList )
1551 Any aReturn;
1553 // by definition, multiple selected entries are transferred as NULL if the
1554 // binding does not support string lists
1555 if ( _rSelectSequence.getLength() <= 1 )
1557 if ( _rSelectSequence.getLength() == 1 )
1558 aReturn = ExtractAnyFromValueList_Safe( _rStringList )( _rSelectSequence[0] );
1561 return aReturn;
1565 Sequence< Any > lcl_getMultiSelectedEntriesAny( const Sequence< sal_Int16 >& _rSelectSequence, const ValueList& _rStringList )
1567 Sequence< Any > aSelectedEntriesValues( _rSelectSequence.getLength() );
1568 ::std::transform(
1569 _rSelectSequence.begin(),
1570 _rSelectSequence.end(),
1571 aSelectedEntriesValues.getArray(),
1572 ExtractAnyFromValueList_Safe( _rStringList )
1574 return aSelectedEntriesValues;
1579 Any OListBoxModel::translateControlValueToExternalValue( ) const
1581 OSL_PRECOND( hasExternalValueBinding(), "OListBoxModel::translateControlValueToExternalValue: no binding!" );
1583 Sequence< sal_Int16 > aSelectSequence;
1584 OSL_VERIFY( getControlValue() >>= aSelectSequence );
1586 Any aReturn;
1587 switch ( lcl_getCurrentExchangeType( getExternalValueType() ) )
1589 case eValueList:
1590 aReturn <<= getCurrentMultiValue();
1591 break;
1593 case eValue:
1594 aReturn = getCurrentSingleValue();
1595 break;
1597 case eIndexList:
1599 // unfortunately, the select sequence is a sequence<short>, but our binding
1600 // expects int's
1601 Sequence< sal_Int32 > aTransformed( aSelectSequence.getLength() );
1602 ::std::copy(
1603 std::cbegin(aSelectSequence),
1604 std::cend(aSelectSequence),
1605 aTransformed.getArray()
1607 aReturn <<= aTransformed;
1609 break;
1611 case eIndex:
1612 if ( aSelectSequence.getLength() <= 1 )
1614 sal_Int32 nIndex = -1;
1616 if ( aSelectSequence.getLength() == 1 )
1617 nIndex = aSelectSequence[0];
1619 aReturn <<= nIndex;
1621 break;
1623 case eEntryList:
1624 aReturn = lcl_getMultiSelectedEntries( aSelectSequence, getStringItemList() );
1625 break;
1627 case eEntry:
1629 const std::vector<OUString>& rStrings = getStringItemList();
1630 const Sequence<Any>& rValues = getTypedItemList();
1631 if (rStrings.size() == static_cast<size_t>(rValues.getLength()))
1632 aReturn = lcl_getSingleSelectedEntryTyped( aSelectSequence, rValues );
1633 else
1634 aReturn = lcl_getSingleSelectedEntry( aSelectSequence, rStrings );
1636 break;
1639 return aReturn;
1643 Any OListBoxModel::translateControlValueToValidatableValue( ) const
1645 OSL_PRECOND( hasValidator(), "OListBoxModel::translateControlValueToValidatableValue: no validator, so why should I?" );
1646 return getCurrentFormComponentValue();
1650 Any OListBoxModel::getCurrentSingleValue() const
1652 Any aCurrentValue;
1656 Sequence< sal_Int16 > aSelectSequence;
1657 OSL_VERIFY( getControlValue() >>= aSelectSequence );
1658 aCurrentValue = lcl_getSingleSelectedEntryAny( aSelectSequence, impl_getValues() );
1660 catch( const Exception& )
1662 DBG_UNHANDLED_EXCEPTION("forms.component");
1665 return aCurrentValue;
1668 Sequence< Any > OListBoxModel::getCurrentMultiValue() const
1670 Sequence< Any > aCurrentValue;
1674 Sequence< sal_Int16 > aSelectSequence;
1675 OSL_VERIFY( getControlValue() >>= aSelectSequence );
1676 aCurrentValue = lcl_getMultiSelectedEntriesAny( aSelectSequence, impl_getValues() );
1678 catch( const Exception& )
1680 DBG_UNHANDLED_EXCEPTION("forms.component");
1683 return aCurrentValue;
1686 Any OListBoxModel::getCurrentFormComponentValue() const
1689 Reference< css::form::validation::XValidator > vtor (const_cast<OListBoxModel*>(this)->getValidator());
1690 Reference< XValueBinding > extBinding (const_cast<OListBoxModel*>(this)->getValueBinding());
1691 if ( vtor.is() && vtor == extBinding )
1692 return translateControlValueToExternalValue();
1695 Any aCurrentValue;
1699 bool bMultiSelection( false );
1700 OSL_VERIFY( const_cast< OListBoxModel* >( this )->getPropertyValue( PROPERTY_MULTISELECTION ) >>= bMultiSelection );
1702 if ( bMultiSelection )
1703 aCurrentValue <<= getCurrentMultiValue();
1704 else
1705 aCurrentValue = getCurrentSingleValue();
1707 catch( const Exception& )
1709 DBG_UNHANDLED_EXCEPTION("forms.component");
1712 return aCurrentValue;
1716 Sequence< Type > OListBoxModel::getSupportedBindingTypes()
1718 return
1720 cppu::UnoType<Sequence< Any >>::get(),
1721 cppu::UnoType<Any>::get(),
1722 cppu::UnoType<Sequence< sal_Int32 >>::get(),
1723 cppu::UnoType<sal_Int32>::get(),
1724 cppu::UnoType<Sequence< OUString >>::get(),
1725 cppu::UnoType<OUString>::get()
1730 void OListBoxModel::stringItemListChanged( ControlModelLock& _rInstanceLock )
1732 if ( !m_xAggregateSet.is() )
1733 return;
1735 suspendValueListening();
1738 m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, Any( comphelper::containerToSequence(getStringItemList()) ) );
1739 m_xAggregateSet->setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( getTypedItemList() ) );
1741 catch( const Exception& )
1743 DBG_UNHANDLED_EXCEPTION("forms.component");
1745 resumeValueListening();
1747 // update the selection here
1748 if ( hasExternalValueBinding( ) )
1749 transferExternalValueToControl( _rInstanceLock );
1750 else
1752 if ( hasField() )
1754 // TODO: update the selection in case we're bound to a database column
1756 else
1758 if ( m_aDefaultSelectSeq.hasElements() )
1759 setControlValue( Any( m_aDefaultSelectSeq ), eOther );
1765 void OListBoxModel::impl_refreshDbEntryList( bool _bForce )
1767 DBG_ASSERT( !hasExternalListSource(), "OListBoxModel::impl_refreshDbEntryList: invalid call!" );
1769 if ( !hasExternalListSource( )
1770 && ( m_eListSourceType != ListSourceType_VALUELIST )
1771 && ( m_xCursor.is() )
1774 loadData( _bForce );
1779 void OListBoxModel::refreshInternalEntryList()
1781 impl_refreshDbEntryList( true );
1782 if ( hasField() && m_xCursor.is() )
1783 initFromField( m_xCursor );
1787 // OListBoxControl
1789 Sequence< Type> OListBoxControl::_getTypes()
1791 return TypeBag(
1792 OBoundControl::_getTypes(),
1793 OListBoxControl_BASE::getTypes()
1794 ).getTypes();
1798 Any SAL_CALL OListBoxControl::queryAggregation(const Type& _rType)
1800 Any aReturn = OListBoxControl_BASE::queryInterface( _rType );
1802 if ( !aReturn.hasValue()
1803 || _rType.equals( cppu::UnoType<XTypeProvider>::get() )
1805 aReturn = OBoundControl::queryAggregation( _rType );
1807 return aReturn;
1811 OListBoxControl::OListBoxControl(const Reference<XComponentContext>& _rxFactory)
1812 :OBoundControl( _rxFactory, VCL_CONTROL_LISTBOX, false )
1813 ,m_aChangeListeners( m_aMutex )
1814 ,m_aItemListeners( m_aMutex )
1815 ,m_aChangeIdle("forms OListBoxControl m_aChangedIdle")
1818 osl_atomic_increment(&m_refCount);
1820 // Register as FocusListener
1821 if (auto xComp = query_aggregation<XWindow>(m_xAggregate))
1822 xComp->addFocusListener(this);
1824 // Register as ItemListener
1825 if ( query_aggregation( m_xAggregate, m_xAggregateListBox ) )
1826 m_xAggregateListBox->addItemListener(this);
1828 // Refcount at 2 for registered Listener
1829 osl_atomic_decrement(&m_refCount);
1831 doSetDelegator();
1833 m_aChangeIdle.SetPriority(TaskPriority::LOWEST);
1834 m_aChangeIdle.SetInvokeHandler(LINK(this,OListBoxControl,OnTimeout));
1838 OListBoxControl::~OListBoxControl()
1840 if (!OComponentHelper::rBHelper.bDisposed)
1842 acquire();
1843 dispose();
1846 doResetDelegator();
1847 m_xAggregateListBox.clear();
1852 css::uno::Sequence<OUString> SAL_CALL OListBoxControl::getSupportedServiceNames()
1854 css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
1855 aSupported.realloc(aSupported.getLength() + 2);
1857 OUString* pArray = aSupported.getArray();
1858 pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_LISTBOX;
1859 pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_LISTBOX;
1860 return aSupported;
1864 // XFocusListener
1866 void SAL_CALL OListBoxControl::focusGained(const FocusEvent& /*_rEvent*/)
1868 ::osl::MutexGuard aGuard(m_aMutex);
1869 if ( m_aChangeListeners.getLength() ) // only if there are listeners
1871 Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
1872 if (xSet.is())
1874 // memorize the current selection for posting the change event
1875 m_aCurrentSelection = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
1881 void SAL_CALL OListBoxControl::focusLost(const FocusEvent& /*_rEvent*/)
1883 m_aCurrentSelection.clear();
1886 // XItemListener
1888 void SAL_CALL OListBoxControl::itemStateChanged(const ItemEvent& _rEvent)
1890 // forward this to our listeners
1891 Reference< XChild > xChild( getModel(), UNO_QUERY );
1892 if ( xChild.is() && xChild->getParent().is() )
1894 ::osl::MutexGuard aGuard( m_aMutex );
1895 if ( m_aItemListeners.getLength() )
1897 if ( !m_pItemBroadcaster.is() )
1899 m_pItemBroadcaster.set(
1900 new ::comphelper::AsyncEventNotifier("ListBox"));
1901 m_pItemBroadcaster->launch();
1903 m_pItemBroadcaster->addEvent( new ItemEventDescription( _rEvent ), this );
1906 else
1907 m_aItemListeners.notifyEach( &XItemListener::itemStateChanged, _rEvent );
1909 // and do the handling for the ChangeListeners
1910 osl::MutexGuard aGuard(m_aMutex);
1911 if ( m_aChangeIdle.IsActive() )
1913 Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
1914 m_aCurrentSelection = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
1916 m_aChangeIdle.Stop();
1917 m_aChangeIdle.Start();
1919 else
1921 if ( m_aChangeListeners.getLength() && m_aCurrentSelection.hasValue() )
1923 Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
1924 if (xSet.is())
1926 // Has the selection been changed?
1927 bool bModified(false);
1928 Any aValue = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
1930 Sequence<sal_Int16> const & rSelection = *o3tl::doAccess<Sequence<sal_Int16>>(aValue);
1931 Sequence<sal_Int16> const & rOldSelection = *o3tl::doAccess<Sequence<sal_Int16>>(m_aCurrentSelection);
1932 sal_Int32 nLen = rSelection.getLength();
1933 if (nLen != rOldSelection.getLength())
1934 bModified = true;
1935 else
1937 const sal_Int16* pVal = rSelection.getConstArray();
1938 const sal_Int16* pCompVal = rOldSelection.getConstArray();
1940 while (nLen-- && !bModified)
1941 bModified = pVal[nLen] != pCompVal[nLen];
1944 if (bModified)
1946 m_aCurrentSelection = std::move(aValue);
1947 m_aChangeIdle.Start();
1951 else if (m_aCurrentSelection.hasValue())
1952 m_aCurrentSelection.clear();
1956 // XEventListener
1958 void SAL_CALL OListBoxControl::disposing(const EventObject& _rSource)
1960 OBoundControl::disposing(_rSource);
1963 // XChangeBroadcaster
1965 void SAL_CALL OListBoxControl::addChangeListener(const Reference<XChangeListener>& _rxListener)
1967 m_aChangeListeners.addInterface( _rxListener );
1971 void SAL_CALL OListBoxControl::removeChangeListener(const Reference<XChangeListener>& _rxListener)
1973 m_aChangeListeners.removeInterface( _rxListener );
1976 // OComponentHelper
1978 void OListBoxControl::disposing()
1980 if (m_aChangeIdle.IsActive())
1981 m_aChangeIdle.Stop();
1983 EventObject aEvent( *this );
1984 m_aChangeListeners.disposeAndClear( aEvent );
1985 m_aItemListeners.disposeAndClear( aEvent );
1987 rtl::Reference< comphelper::AsyncEventNotifier > t;
1989 ::osl::MutexGuard aGuard( m_aMutex );
1990 if ( m_pItemBroadcaster.is() )
1992 t = m_pItemBroadcaster;
1993 m_pItemBroadcaster->removeEventsForProcessor( this );
1994 m_pItemBroadcaster->terminate();
1995 m_pItemBroadcaster = nullptr;
1998 if (t.is()) {
1999 t->join();
2002 OBoundControl::disposing();
2006 void OListBoxControl::processEvent( const AnyEvent& _rEvent )
2008 Reference< XListBox > xKeepAlive( this );
2010 ::osl::MutexGuard aGuard( m_aMutex );
2011 if ( OComponentHelper::rBHelper.bDisposed )
2012 return;
2014 const ItemEventDescription& rItemEvent = static_cast< const ItemEventDescription& >( _rEvent );
2015 m_aItemListeners.notifyEach( &XItemListener::itemStateChanged, rItemEvent.getEventObject() );
2019 IMPL_LINK_NOARG(OListBoxControl, OnTimeout, Timer*, void)
2021 m_aChangeListeners.notifyEach( &XChangeListener::changed, EventObject( *this ) );
2025 void SAL_CALL OListBoxControl::addItemListener( const Reference< XItemListener >& l )
2027 m_aItemListeners.addInterface( l );
2031 void SAL_CALL OListBoxControl::removeItemListener( const Reference< XItemListener >& l )
2033 m_aItemListeners.removeInterface( l );
2037 void SAL_CALL OListBoxControl::addActionListener( const Reference< XActionListener >& l )
2039 if ( m_xAggregateListBox.is() )
2040 m_xAggregateListBox->addActionListener( l );
2044 void SAL_CALL OListBoxControl::removeActionListener( const Reference< XActionListener >& l )
2046 if ( m_xAggregateListBox.is() )
2047 m_xAggregateListBox->removeActionListener( l );
2051 void SAL_CALL OListBoxControl::addItem( const OUString& aItem, ::sal_Int16 nPos )
2053 if ( m_xAggregateListBox.is() )
2054 m_xAggregateListBox->addItem( aItem, nPos );
2058 void SAL_CALL OListBoxControl::addItems( const Sequence< OUString >& aItems, ::sal_Int16 nPos )
2060 if ( m_xAggregateListBox.is() )
2061 m_xAggregateListBox->addItems( aItems, nPos );
2065 void SAL_CALL OListBoxControl::removeItems( ::sal_Int16 nPos, ::sal_Int16 nCount )
2067 if ( m_xAggregateListBox.is() )
2068 m_xAggregateListBox->removeItems( nPos, nCount );
2072 ::sal_Int16 SAL_CALL OListBoxControl::getItemCount( )
2074 if ( m_xAggregateListBox.is() )
2075 return m_xAggregateListBox->getItemCount();
2076 return 0;
2080 OUString SAL_CALL OListBoxControl::getItem( ::sal_Int16 nPos )
2082 if ( m_xAggregateListBox.is() )
2083 return m_xAggregateListBox->getItem( nPos );
2084 return OUString( );
2088 Sequence< OUString > SAL_CALL OListBoxControl::getItems( )
2090 if ( m_xAggregateListBox.is() )
2091 return m_xAggregateListBox->getItems();
2092 return Sequence< OUString >( );
2096 ::sal_Int16 SAL_CALL OListBoxControl::getSelectedItemPos( )
2098 if ( m_xAggregateListBox.is() )
2099 return m_xAggregateListBox->getSelectedItemPos();
2100 return -1;
2104 Sequence< ::sal_Int16 > SAL_CALL OListBoxControl::getSelectedItemsPos( )
2106 if ( m_xAggregateListBox.is() )
2107 return m_xAggregateListBox->getSelectedItemsPos();
2108 return Sequence< ::sal_Int16 >( );
2112 OUString SAL_CALL OListBoxControl::getSelectedItem( )
2114 if ( m_xAggregateListBox.is() )
2115 return m_xAggregateListBox->getSelectedItem();
2116 return OUString( );
2120 Sequence< OUString > SAL_CALL OListBoxControl::getSelectedItems( )
2122 if ( m_xAggregateListBox.is() )
2123 return m_xAggregateListBox->getSelectedItems();
2124 return Sequence< OUString >( );
2128 void SAL_CALL OListBoxControl::selectItemPos( ::sal_Int16 nPos, sal_Bool bSelect )
2130 if ( m_xAggregateListBox.is() )
2131 m_xAggregateListBox->selectItemPos( nPos, bSelect );
2135 void SAL_CALL OListBoxControl::selectItemsPos( const Sequence< ::sal_Int16 >& aPositions, sal_Bool bSelect )
2137 if ( m_xAggregateListBox.is() )
2138 m_xAggregateListBox->selectItemsPos( aPositions, bSelect );
2142 void SAL_CALL OListBoxControl::selectItem( const OUString& aItem, sal_Bool bSelect )
2144 if ( m_xAggregateListBox.is() )
2145 m_xAggregateListBox->selectItem( aItem, bSelect );
2149 sal_Bool SAL_CALL OListBoxControl::isMutipleMode( )
2151 if ( m_xAggregateListBox.is() )
2152 return m_xAggregateListBox->isMutipleMode();
2153 return false;
2157 void SAL_CALL OListBoxControl::setMultipleMode( sal_Bool bMulti )
2159 if ( m_xAggregateListBox.is() )
2160 m_xAggregateListBox->setMultipleMode( bMulti );
2164 ::sal_Int16 SAL_CALL OListBoxControl::getDropDownLineCount( )
2166 if ( m_xAggregateListBox.is() )
2167 return m_xAggregateListBox->getDropDownLineCount();
2168 return 0;
2172 void SAL_CALL OListBoxControl::setDropDownLineCount( ::sal_Int16 nLines )
2174 if ( m_xAggregateListBox.is() )
2175 m_xAggregateListBox->setDropDownLineCount( nLines );
2179 void SAL_CALL OListBoxControl::makeVisible( ::sal_Int16 nEntry )
2181 if ( m_xAggregateListBox.is() )
2182 m_xAggregateListBox->makeVisible( nEntry );
2187 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2188 com_sun_star_form_OListBoxModel_get_implementation(css::uno::XComponentContext* component,
2189 css::uno::Sequence<css::uno::Any> const &)
2191 return cppu::acquire(new frm::OListBoxModel(component));
2194 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2195 com_sun_star_form_OListBoxControl_get_implementation(css::uno::XComponentContext* component,
2196 css::uno::Sequence<css::uno::Any> const &)
2198 return cppu::acquire(new frm::OListBoxControl(component));
2201 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */