Update git submodules
[LibreOffice.git] / forms / source / component / ComboBox.cxx
blobf49d8bba1dd5a07cc351be973532a90f4de639ed
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 .
21 #include "ComboBox.hxx"
22 #include <property.hxx>
23 #include <services.hxx>
25 #include <frm_resource.hxx>
26 #include <strings.hrc>
27 #include "BaseListBox.hxx"
29 #include <com/sun/star/beans/PropertyAttribute.hpp>
30 #include <com/sun/star/form/FormComponentType.hpp>
31 #include <com/sun/star/sdbc/XRowSet.hpp>
32 #include <com/sun/star/container/XIndexAccess.hpp>
33 #include <com/sun/star/sdbc/XConnection.hpp>
35 #include <comphelper/basicio.hxx>
36 #include <comphelper/property.hxx>
37 #include <connectivity/dbtools.hxx>
38 #include <tools/debug.hxx>
39 #include <comphelper/diagnose_ex.hxx>
40 #include <unotools/sharedunocomponent.hxx>
42 #include <limits.h>
44 using namespace dbtools;
47 namespace frm
49 using namespace ::com::sun::star::uno;
50 using namespace ::com::sun::star::sdb;
51 using namespace ::com::sun::star::sdbc;
52 using namespace ::com::sun::star::sdbcx;
53 using namespace ::com::sun::star::beans;
54 using namespace ::com::sun::star::container;
55 using namespace ::com::sun::star::form;
56 using namespace ::com::sun::star::io;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::util;
61 Sequence<Type> OComboBoxModel::_getTypes()
63 return ::comphelper::concatSequences(
64 OBoundControlModel::_getTypes(),
65 OEntryListHelper::getTypes(),
66 OErrorBroadcaster::getTypes()
70 // XServiceInfo
72 css::uno::Sequence<OUString> SAL_CALL OComboBoxModel::getSupportedServiceNames()
74 css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
76 sal_Int32 nOldLen = aSupported.getLength();
77 aSupported.realloc( nOldLen + 9 );
78 OUString* pStoreTo = aSupported.getArray() + nOldLen;
80 *pStoreTo++ = BINDABLE_CONTROL_MODEL;
81 *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
82 *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
84 *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
85 *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
87 *pStoreTo++ = FRM_SUN_COMPONENT_COMBOBOX;
88 *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_COMBOBOX;
89 *pStoreTo++ = BINDABLE_DATABASE_COMBO_BOX;
91 *pStoreTo++ = FRM_COMPONENT_COMBOBOX;
93 return aSupported;
97 Any SAL_CALL OComboBoxModel::queryAggregation(const Type& _rType)
99 Any aReturn = OBoundControlModel::queryAggregation( _rType );
100 if ( !aReturn.hasValue() )
101 aReturn = OEntryListHelper::queryInterface( _rType );
102 if ( !aReturn.hasValue() )
103 aReturn = OErrorBroadcaster::queryInterface( _rType );
104 return aReturn;
108 OComboBoxModel::OComboBoxModel(const Reference<XComponentContext>& _rxFactory)
109 :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_COMBOBOX, FRM_SUN_CONTROL_COMBOBOX, true, true, true )
110 // use the old control name for compatibility reasons
111 ,OEntryListHelper( static_cast<OControlModel&>(*this) )
112 ,OErrorBroadcaster( OComponentHelper::rBHelper )
113 ,m_eListSourceType(ListSourceType_TABLE)
114 ,m_bEmptyIsNull(true)
116 m_nClassId = FormComponentType::COMBOBOX;
117 initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT );
121 OComboBoxModel::OComboBoxModel( const OComboBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
122 :OBoundControlModel( _pOriginal, _rxFactory )
123 ,OEntryListHelper( *_pOriginal, static_cast<OControlModel&>(*this) )
124 ,OErrorBroadcaster( OComponentHelper::rBHelper )
125 ,m_aListSource( _pOriginal->m_aListSource )
126 ,m_aDefaultText( _pOriginal->m_aDefaultText )
127 ,m_eListSourceType( _pOriginal->m_eListSourceType )
128 ,m_bEmptyIsNull( _pOriginal->m_bEmptyIsNull )
133 OComboBoxModel::~OComboBoxModel()
135 if (!OComponentHelper::rBHelper.bDisposed)
137 acquire();
138 dispose();
143 // XCloneable
145 css::uno::Reference< css::util::XCloneable > SAL_CALL OComboBoxModel::createClone()
147 rtl::Reference<OComboBoxModel> pClone = new OComboBoxModel(this, getContext());
148 pClone->clonedFrom(this);
149 return pClone;
153 void OComboBoxModel::disposing()
155 OBoundControlModel::disposing();
156 OEntryListHelper::disposing();
157 OErrorBroadcaster::disposing();
161 void OComboBoxModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
163 switch (_nHandle)
165 case PROPERTY_ID_LISTSOURCETYPE:
166 _rValue <<= m_eListSourceType;
167 break;
169 case PROPERTY_ID_LISTSOURCE:
170 _rValue <<= m_aListSource;
171 break;
173 case PROPERTY_ID_EMPTY_IS_NULL:
174 _rValue <<= m_bEmptyIsNull;
175 break;
177 case PROPERTY_ID_DEFAULT_TEXT:
178 _rValue <<= m_aDefaultText;
179 break;
181 case PROPERTY_ID_STRINGITEMLIST:
182 _rValue <<= comphelper::containerToSequence(getStringItemList());
183 break;
185 case PROPERTY_ID_TYPEDITEMLIST:
186 _rValue <<= getTypedItemList();
187 break;
189 default:
190 OBoundControlModel::getFastPropertyValue(_rValue, _nHandle);
195 void OComboBoxModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
197 switch (_nHandle)
199 case PROPERTY_ID_LISTSOURCETYPE :
200 DBG_ASSERT(_rValue.getValueType().equals(::cppu::UnoType<ListSourceType>::get()),
201 "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
202 _rValue >>= m_eListSourceType;
203 break;
205 case PROPERTY_ID_LISTSOURCE :
206 DBG_ASSERT(_rValue.getValueTypeClass() == TypeClass_STRING,
207 "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
208 _rValue >>= m_aListSource;
209 // The ListSource has changed -> reload
210 if (ListSourceType_VALUELIST != m_eListSourceType)
212 if ( m_xCursor.is() && !hasField() && !hasExternalListSource() )
213 // combo box is already connected to a database, and no external list source
214 // data source changed -> refresh
215 loadData( false );
217 break;
219 case PROPERTY_ID_EMPTY_IS_NULL :
220 DBG_ASSERT(_rValue.getValueTypeClass() == TypeClass_BOOLEAN,
221 "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
222 _rValue >>= m_bEmptyIsNull;
223 break;
225 case PROPERTY_ID_DEFAULT_TEXT :
226 DBG_ASSERT(_rValue.getValueTypeClass() == TypeClass_STRING,
227 "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
228 _rValue >>= m_aDefaultText;
229 resetNoBroadcast();
230 break;
232 case PROPERTY_ID_STRINGITEMLIST:
234 ControlModelLock aLock( *this );
235 setNewStringItemList( _rValue, aLock );
236 // FIXME: this is bogus. setNewStringItemList expects a guard which has the *only*
237 // lock to the mutex, but setFastPropertyValue_NoBroadcast is already called with
238 // a lock - so we effectively has two locks here, of which setNewStringItemList can
239 // only control one.
241 break;
243 case PROPERTY_ID_TYPEDITEMLIST:
245 ControlModelLock aLock( *this );
246 setNewTypedItemList( _rValue, aLock );
247 // Same FIXME as above.
249 break;
251 default:
252 OBoundControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
256 sal_Bool OComboBoxModel::convertFastPropertyValue(
257 Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
259 bool bModified(false);
260 switch (_nHandle)
262 case PROPERTY_ID_LISTSOURCETYPE :
263 bModified = tryPropertyValueEnum(_rConvertedValue, _rOldValue, _rValue, m_eListSourceType);
264 break;
266 case PROPERTY_ID_LISTSOURCE :
267 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aListSource);
268 break;
270 case PROPERTY_ID_EMPTY_IS_NULL :
271 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bEmptyIsNull);
272 break;
274 case PROPERTY_ID_DEFAULT_TEXT :
275 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aDefaultText);
276 break;
278 case PROPERTY_ID_STRINGITEMLIST:
279 bModified = convertNewListSourceProperty( _rConvertedValue, _rOldValue, _rValue );
280 break;
282 case PROPERTY_ID_TYPEDITEMLIST :
283 if (hasExternalListSource())
284 throw IllegalArgumentException();
285 bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, getTypedItemList());
286 break;
288 default:
289 bModified = OBoundControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
290 break;
292 return bModified;
295 void OComboBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const
297 OBoundControlModel::describeFixedProperties( _rProps );
298 sal_Int32 nOldCount = _rProps.getLength();
299 _rProps.realloc( nOldCount + 7);
300 css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
301 *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
302 *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE, cppu::UnoType<ListSourceType>::get(), css::beans::PropertyAttribute::BOUND);
303 *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
304 *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
305 css::beans::PropertyAttribute::BOUND);
306 *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
307 *pProperties++ = css::beans::Property(PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
308 *pProperties++ = css::beans::Property(PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::OPTIONAL);
309 DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
313 void OComboBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
315 OBoundControlModel::describeAggregateProperties( _rAggregateProps );
317 // superseded properties:
318 RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST );
319 RemoveProperty( _rAggregateProps, PROPERTY_TYPEDITEMLIST );
323 OUString SAL_CALL OComboBoxModel::getServiceName()
325 return FRM_COMPONENT_COMBOBOX; // old (non-sun) name for compatibility !
329 void SAL_CALL OComboBoxModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
331 OBoundControlModel::write(_rxOutStream);
333 // Version
334 // Version 0x0002: EmptyIsNull
335 // Version 0x0003: ListSource->Seq
336 // Version 0x0004: DefaultText
337 // Version 0x0005: HelpText
338 _rxOutStream->writeShort(0x0006);
340 // Mask for Any
341 sal_uInt16 nAnyMask = 0;
342 if (m_aBoundColumn.getValueTypeClass() == TypeClass_SHORT)
343 nAnyMask |= BOUNDCOLUMN;
344 _rxOutStream << nAnyMask;
346 css::uno::Sequence<OUString> aListSourceSeq(&m_aListSource, 1);
347 _rxOutStream << aListSourceSeq;
348 _rxOutStream << static_cast<sal_Int16>(m_eListSourceType);
350 if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
352 sal_Int16 nBoundColumn = 0;
353 m_aBoundColumn >>= nBoundColumn;
354 _rxOutStream << nBoundColumn;
357 _rxOutStream << m_bEmptyIsNull;
358 _rxOutStream << m_aDefaultText;
359 writeHelpTextCompatibly(_rxOutStream);
361 // from version 0x0006 : common properties
362 writeCommonProperties(_rxOutStream);
366 void SAL_CALL OComboBoxModel::read(const Reference<css::io::XObjectInputStream>& _rxInStream)
368 OBoundControlModel::read(_rxInStream);
369 ControlModelLock aLock( *this );
371 // since we are "overwriting" the StringItemList of our aggregate (means we have
372 // an own place to store the value, instead of relying on our aggregate storing it),
373 // we need to respect what the aggregate just read for the StringItemList property.
376 if ( m_xAggregateSet.is() )
377 setNewStringItemList( m_xAggregateSet->getPropertyValue( PROPERTY_STRINGITEMLIST ), aLock );
379 catch( const Exception& )
381 TOOLS_WARN_EXCEPTION( "forms.component", "OComboBoxModel::read: caught an exception while examining the aggregate's string item list!" );
384 // Version
385 sal_uInt16 nVersion = _rxInStream->readShort();
386 DBG_ASSERT(nVersion > 0, "OComboBoxModel::read : version 0 ? this should never have been written !");
388 if (nVersion > 0x0006)
390 OSL_FAIL("OComboBoxModel::read : invalid (means unknown) version !");
391 m_aListSource.clear();
392 m_aBoundColumn <<= sal_Int16(0);
393 m_aDefaultText.clear();
394 m_eListSourceType = ListSourceType_TABLE;
395 m_bEmptyIsNull = true;
396 defaultCommonProperties();
397 return;
400 // Mask for Any
401 sal_uInt16 nAnyMask;
402 _rxInStream >> nAnyMask;
404 // ListSource
405 if (nVersion < 0x0003)
407 _rxInStream >> m_aListSource;
409 else // nVersion == 4
411 m_aListSource.clear();
412 css::uno::Sequence<OUString> aListSource;
413 _rxInStream >> aListSource;
414 for (const OUString& rToken : aListSource)
415 m_aListSource += rToken;
418 sal_Int16 nListSourceType;
419 _rxInStream >> nListSourceType;
420 m_eListSourceType = static_cast<ListSourceType>(nListSourceType);
422 if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
424 sal_Int16 nValue;
425 _rxInStream >> nValue;
426 m_aBoundColumn <<= nValue;
429 if (nVersion > 0x0001)
431 bool bNull;
432 _rxInStream >> bNull;
433 m_bEmptyIsNull = bNull;
436 if (nVersion > 0x0003) // nVersion == 4
437 _rxInStream >> m_aDefaultText;
439 // StringList must be emptied if a ListSource is set.
440 // This can be the case if we save in alive mode.
441 if ( !m_aListSource.isEmpty()
442 && !hasExternalListSource()
445 setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
446 setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
449 if (nVersion > 0x0004)
450 readHelpTextCompatibly(_rxInStream);
452 if (nVersion > 0x0005)
453 readCommonProperties(_rxInStream);
455 // After reading in, display the default values
456 if ( !getControlSource().isEmpty() )
458 // (not if we don't have a control source - the "State" property acts like it is persistent, then
459 resetNoBroadcast();
464 void OComboBoxModel::loadData( bool _bForce )
466 DBG_ASSERT(m_eListSourceType != ListSourceType_VALUELIST, "OComboBoxModel::loadData : do not call for a value list !");
467 DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::loadData: cannot load from DB when I have an external list source!" );
469 if ( hasExternalListSource() )
470 return;
472 // Get Connection
473 if (!m_xCursor.is())
474 return;
475 Reference<XConnection> xConnection = getConnection(m_xCursor);
476 if (!xConnection.is())
477 return;
479 Reference<XServiceInfo> xServiceInfo(xConnection, UNO_QUERY);
480 if (!xServiceInfo.is() || !xServiceInfo->supportsService(SRV_SDB_CONNECTION))
482 OSL_FAIL("OComboBoxModel::loadData : invalid connection !");
483 return;
486 if (m_aListSource.isEmpty() || m_eListSourceType == ListSourceType_VALUELIST)
487 return;
489 ::utl::SharedUNOComponent< XResultSet > xListCursor;
492 m_aListRowSet.setConnection( xConnection );
494 bool bExecuteRowSet( false );
495 switch (m_eListSourceType)
497 case ListSourceType_TABLEFIELDS:
498 // don't work with a statement here, the fields will be collected below
499 break;
500 case ListSourceType_TABLE:
502 // does the bound field belong to the table ?
503 // if we use an alias for the bound field, we won't find it
504 // in that case we use the first field of the table
506 Reference<XNameAccess> xFieldsByName = getTableFields(xConnection, m_aListSource);
508 OUString aFieldName;
509 if ( xFieldsByName.is() && xFieldsByName->hasByName( getControlSource() ) )
511 aFieldName = getControlSource();
513 else
515 // otherwise look for the alias
516 Reference<XPropertySet> xFormProp(m_xCursor,UNO_QUERY);
517 Reference< XColumnsSupplier > xSupplyFields;
518 xFormProp->getPropertyValue(u"SingleSelectQueryComposer"_ustr) >>= xSupplyFields;
520 // search the field
521 DBG_ASSERT(xSupplyFields.is(), "OComboBoxModel::loadData : invalid query composer !");
523 Reference< XNameAccess > xFieldNames = xSupplyFields->getColumns();
524 if ( xFieldNames->hasByName( getControlSource() ) )
526 Reference< XPropertySet > xComposerFieldAsSet;
527 xFieldNames->getByName( getControlSource() ) >>= xComposerFieldAsSet;
528 if (hasProperty(PROPERTY_FIELDSOURCE, xComposerFieldAsSet))
529 xComposerFieldAsSet->getPropertyValue(PROPERTY_FIELDSOURCE) >>= aFieldName;
533 if (aFieldName.isEmpty())
534 break;
536 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
537 OSL_ENSURE(xMeta.is(),"No database meta data!");
538 if ( xMeta.is() )
540 OUString aQuote = xMeta->getIdentifierQuoteString();
542 OUString sCatalog, sSchema, sTable;
543 qualifiedNameComponents( xMeta, m_aListSource, sCatalog, sSchema, sTable, EComposeRule::InDataManipulation );
545 OUString aStatement =
546 "SELECT DISTINCT " +
547 quoteName( aQuote, aFieldName ) +
548 " FROM " +
549 composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable );
551 m_aListRowSet.setEscapeProcessing( false );
552 m_aListRowSet.setCommand( aStatement );
553 bExecuteRowSet = true;
555 } break;
556 case ListSourceType_QUERY:
558 m_aListRowSet.setCommandFromQuery( m_aListSource );
559 bExecuteRowSet = true;
561 break;
563 default:
565 m_aListRowSet.setEscapeProcessing( ListSourceType_SQLPASSTHROUGH != m_eListSourceType );
566 m_aListRowSet.setCommand( m_aListSource );
567 bExecuteRowSet = true;
571 if ( bExecuteRowSet )
573 if ( !_bForce && !m_aListRowSet.isDirty() )
575 // if none of the settings of the row set changed, compared to the last
576 // invocation of loadData, then don't re-fill the list. Instead, assume
577 // the list entries are the same.
578 return;
580 xListCursor.reset( m_aListRowSet.execute() );
583 catch(const SQLException& eSQL)
585 onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
586 return;
588 catch( const Exception& )
590 DBG_UNHANDLED_EXCEPTION("forms.component");
591 return;
594 ::std::vector< OUString > aStringList;
595 aStringList.reserve(16);
598 OSL_ENSURE( xListCursor.is() || ( ListSourceType_TABLEFIELDS == m_eListSourceType ),
599 "OComboBoxModel::loadData: logic error!" );
600 if ( !xListCursor.is() && ( ListSourceType_TABLEFIELDS != m_eListSourceType ) )
601 return;
603 switch (m_eListSourceType)
605 case ListSourceType_SQL:
606 case ListSourceType_SQLPASSTHROUGH:
607 case ListSourceType_TABLE:
608 case ListSourceType_QUERY:
610 // The XDatabaseVariant of the first column
611 Reference<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
612 DBG_ASSERT(xSupplyCols.is(), "OComboBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
613 Reference<XIndexAccess> xColumns;
614 if (xSupplyCols.is())
616 xColumns.set(xSupplyCols->getColumns(), UNO_QUERY);
617 DBG_ASSERT(xColumns.is(), "OComboBoxModel::loadData : no columns supplied by the row set !");
619 Reference< XPropertySet > xDataField;
620 if ( xColumns.is() )
621 xColumns->getByIndex(0) >>= xDataField;
622 if ( !xDataField.is() )
623 return;
625 ::dbtools::FormattedColumnValue aValueFormatter( getContext(), m_xCursor, xDataField );
627 // Fill Lists
628 sal_Int16 i = 0;
629 // At the moment by definition the list cursor is positioned _before_ the first row
630 while (xListCursor->next() && (i++<SHRT_MAX)) // Set max. count
632 aStringList.push_back( aValueFormatter.getFormattedValue() );
635 break;
636 case ListSourceType_TABLEFIELDS:
638 Reference<XNameAccess> xFieldNames = getTableFields(xConnection, m_aListSource);
639 if (xFieldNames.is())
641 const Sequence<OUString> aFieldNames = xFieldNames->getElementNames();
642 aStringList.insert(aStringList.end(), aFieldNames.begin(), aFieldNames.end());
645 break;
646 default:
647 OSL_FAIL( "OComboBoxModel::loadData: unreachable!" );
648 break;
651 catch(const SQLException& eSQL)
653 onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
654 return;
656 catch( const Exception& )
658 DBG_UNHANDLED_EXCEPTION("forms.component");
659 return;
662 // Set String-Sequence at ListBox
663 setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( comphelper::containerToSequence(aStringList) ) );
664 // Reset TypedItemList, no matching data.
665 setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
669 void OComboBoxModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
671 Reference<XPropertySet> xField = getField();
672 if ( xField.is() )
673 m_pValueFormatter.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) );
674 getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= m_aDesignModeStringItems;
676 // Only load data if a ListSource was supplied
677 if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() )
678 loadData( false );
682 void OComboBoxModel::onDisconnectedDbColumn()
684 m_pValueFormatter.reset();
686 // reset the string item list
687 if ( !hasExternalListSource() )
688 setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( m_aDesignModeStringItems ) );
690 m_aListRowSet.dispose();
694 void SAL_CALL OComboBoxModel::reloaded( const EventObject& aEvent )
696 OBoundControlModel::reloaded(aEvent);
698 // reload data if we have a list source
699 if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() )
700 loadData( false );
704 void OComboBoxModel::resetNoBroadcast()
706 OBoundControlModel::resetNoBroadcast();
707 m_aLastKnownValue.clear();
711 bool OComboBoxModel::commitControlValueToDbColumn( bool _bPostReset )
713 Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
715 OUString sNewValue;
716 aNewValue >>= sNewValue;
718 bool bModified = ( aNewValue != m_aLastKnownValue );
719 if ( bModified )
721 if ( !aNewValue.hasValue()
722 || ( sNewValue.isEmpty() // an empty string
723 && m_bEmptyIsNull // which should be interpreted as NULL
727 m_xColumnUpdate->updateNull();
729 else
733 OSL_PRECOND(m_pValueFormatter,
734 "OComboBoxModel::commitControlValueToDbColumn: no value formatter!");
735 if (m_pValueFormatter)
737 if ( !m_pValueFormatter->setFormattedValue( sNewValue ) )
738 return false;
740 else
741 m_xColumnUpdate->updateString( sNewValue );
743 catch ( const Exception& )
745 return false;
749 m_aLastKnownValue = std::move(aNewValue);
752 // add the new value to the list
753 bool bAddToList = bModified && !_bPostReset;
754 // (only if this is not the "commit" triggered by a "reset")
756 if ( !bAddToList )
757 return true;
759 css::uno::Sequence<OUString> aStringItemList;
760 if ( !(getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aStringItemList) )
761 return true;
763 bool bFound = false;
764 for (const OUString& rStringItem : aStringItemList)
766 if ( (bFound = rStringItem == sNewValue) )
767 break;
770 // not found -> add
771 if (!bFound)
773 sal_Int32 nOldLen = aStringItemList.getLength();
774 aStringItemList.realloc( nOldLen + 1 );
775 aStringItemList.getArray()[ nOldLen ] = sNewValue;
777 setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( aStringItemList ) );
778 setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
781 return true;
784 // XPropertiesChangeListener
786 Any OComboBoxModel::translateDbColumnToControlValue()
788 OSL_PRECOND(m_pValueFormatter,
789 "OComboBoxModel::translateDbColumnToControlValue: no value formatter!");
790 if (m_pValueFormatter)
792 OUString sValue( m_pValueFormatter->getFormattedValue() );
793 if ( sValue.isEmpty()
794 && m_pValueFormatter->getColumn().is()
795 && m_pValueFormatter->getColumn()->wasNull()
798 m_aLastKnownValue.clear();
800 else
803 m_aLastKnownValue <<= sValue;
806 else
807 m_aLastKnownValue.clear();
809 return m_aLastKnownValue.hasValue() ? m_aLastKnownValue : Any( OUString() );
810 // (m_aLastKnownValue is allowed to be VOID, the control value isn't)
814 Any OComboBoxModel::getDefaultForReset() const
816 return Any( m_aDefaultText );
820 void OComboBoxModel::stringItemListChanged( ControlModelLock& /*_rInstanceLock*/ )
822 if ( m_xAggregateSet.is() )
824 m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, Any( comphelper::containerToSequence(getStringItemList()) ) );
825 m_xAggregateSet->setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( getTypedItemList()) ) ;
830 void OComboBoxModel::refreshInternalEntryList()
832 DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::refreshInternalEntryList: invalid call!" );
834 if ( !hasExternalListSource( )
835 && ( m_eListSourceType != ListSourceType_VALUELIST )
836 && ( m_xCursor.is() )
839 loadData( true );
844 void SAL_CALL OComboBoxModel::disposing( const EventObject& _rSource )
846 if ( !OEntryListHelper::handleDisposing( _rSource ) )
847 OBoundControlModel::disposing( _rSource );
851 //= OComboBoxControl
853 OComboBoxControl::OComboBoxControl(const Reference<XComponentContext>& _rxContext)
854 :OBoundControl(_rxContext, VCL_CONTROL_COMBOBOX)
859 css::uno::Sequence<OUString> SAL_CALL OComboBoxControl::getSupportedServiceNames()
861 css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
862 aSupported.realloc(aSupported.getLength() + 2);
864 OUString* pArray = aSupported.getArray();
865 pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_COMBOBOX;
866 pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_COMBOBOX;
867 return aSupported;
872 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
873 com_sun_star_form_OComboBoxModel_get_implementation(css::uno::XComponentContext* component,
874 css::uno::Sequence<css::uno::Any> const &)
876 return cppu::acquire(new frm::OComboBoxModel(component));
879 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
880 com_sun_star_form_OComboBoxControl_get_implementation(css::uno::XComponentContext* component,
881 css::uno::Sequence<css::uno::Any> const &)
883 return cppu::acquire(new frm::OComboBoxControl(component));
886 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */