/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
case PROPERTY_ID_STRINGITEMLIST:
{
ControlModelLock aLock( *this );
setNewStringItemList( _rValue, aLock ); // TODO: this is bogus. setNewStringItemList expects a guard which has the *only* // lock to the mutex, but setFastPropertyValue_NoBroadcast is already called with // a lock - so we effectively has two locks here, of which setNewStringItemList can // only control one.
}
resetNoBroadcast(); break;
case PROPERTY_ID_TYPEDITEMLIST:
{
ControlModelLock aLock( *this );
setNewTypedItemList( _rValue, aLock ); // Same TODO as above.
}
resetNoBroadcast(); break;
void SAL_CALL OListBoxModel::setPropertyValues( const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues )
{ // if both SelectedItems and StringItemList are set, care for this // #i27024# const Any* pSelectSequenceValue = nullptr;
const OUString* pSelectedItemsPos = std::find(
_rPropertyNames.begin(), _rPropertyNames.end(), PROPERTY_SELECT_SEQ
); auto aStringItemListExists = std::any_of(
_rPropertyNames.begin(), _rPropertyNames.end(),
[](OUString const & s) { return s == PROPERTY_STRINGITEMLIST; }
); if ( ( pSelectedItemsPos != _rPropertyNames.end() ) && aStringItemListExists )
{ if (_rPropertyNames.getLength() != _rValues.getLength()) throw css::lang::IllegalArgumentException(u"lengths do not match"_ustr, static_cast<cppu::OWeakObject*>(this), -1);
// both properties are present // -> remember the value for the select sequence
pSelectSequenceValue = _rValues.getConstArray() + ( pSelectedItemsPos - _rPropertyNames.begin() );
}
if ( pSelectSequenceValue )
{
setPropertyValue( PROPERTY_SELECT_SEQ, *pSelectSequenceValue ); // Note that this is the only reliable way, since one of the properties is implemented // by ourself, and one is implemented by the aggregate, we cannot rely on any particular // results when setting them both - too many undocumented behavior in all the involved
// from version 0x0004 : common properties
writeCommonProperties(_rxOutStream);
}
void SAL_CALL OListBoxModel::read(const Reference<XObjectInputStream>& _rxInStream)
{ // We need to respect dependencies for certain variables. // Therefore, we need to set them explicitly via setPropertyValue().
// since we are "overwriting" the StringItemList of our aggregate (means we have // an own place to store the value, instead of relying on our aggregate storing it), // we need to respect what the aggregate just read for the StringItemList property. try
{ if ( m_xAggregateSet.is() )
setNewStringItemList( m_xAggregateSet->getPropertyValue( PROPERTY_STRINGITEMLIST ), aLock );
} catch( const Exception& )
{
TOOLS_WARN_EXCEPTION( "forms.component", "OComboBoxModel::read: caught an exception while examining the aggregate's string item list" );
}
// Version
sal_uInt16 nVersion = _rxInStream->readShort();
DBG_ASSERT(nVersion > 0, "OListBoxModel::read : version 0 ? this should never have been written !");
// BoundColumn if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
{
sal_Int16 nValue;
_rxInStream >> nValue;
m_aBoundColumn <<= nValue;
} else// the constructor initialises to 1, so if it is empty, // we must explicitly set to empty
{
m_aBoundColumn = Any();
}
if (nVersion > 2)
readHelpTextCompatibly(_rxInStream);
// if our string list is not filled from the value list, we must empty it // this can be the case when somebody saves in alive mode if ( ( m_eListSourceType != ListSourceType_VALUELIST )
&& !hasExternalListSource()
)
{
setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
}
if (nVersion > 3)
readCommonProperties(_rxInStream);
// Display the default values after reading if ( !getControlSource().isEmpty() ) // (not if we don't have a control source - the "State" property acts like it is persistent, then
resetNoBroadcast();
}
void OListBoxModel::loadData( bool _bForce )
{
SAL_INFO( "forms.component", "OListBoxModel::loadData" );
DBG_ASSERT( m_eListSourceType != ListSourceType_VALUELIST, "OListBoxModel::loadData: cannot load value list from DB!" );
DBG_ASSERT( !hasExternalListSource(), "OListBoxModel::loadData: cannot load from DB when I have an external list source!" );
// pre-requisites: // PRE1: connection
Reference< XConnection > xConnection; // is the active connection of our form
Reference< XPropertySet > xFormProps( m_xCursor, UNO_QUERY ); if ( xFormProps.is() )
xFormProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection;
// PRE2: list source
OUString sListSource; // if our list source type is no value list, we need to concatenate // the single list source elements
::std::for_each(
m_aListSourceValues.begin(),
m_aListSourceValues.end(),
AppendRowSetValueString( sListSource )
);
// outta here if we don't have all pre-requisites if ( !xConnection.is() || sListSource.isEmpty() )
{
clearBoundValues(); return;
}
bool bExecute = false; switch (m_eListSourceType)
{ case ListSourceType_TABLEFIELDS: // don't work with a statement here, the fields will be collected below break;
// do we have a bound column if yes we have to select it // and the displayed column is the first column otherwise we act as a combobox
OUString aFieldName;
OUString aBoundFieldName;
if ( !!aBoundColumn && ( *aBoundColumn >= 0 ) && xFieldsByIndex.is() )
{ if ( *aBoundColumn >= xFieldsByIndex->getCount() ) break;
if (bExecute)
{ if ( !_bForce && !m_aListRowSet.isDirty() )
{ // if none of the settings of the row set changed, compared to the last // invocation of loadData, then don't re-fill the list. Instead, assume // the list entries are the same.
m_nNULLPos = nNULLPosBackup;
m_nBoundColumnType = nBoundColumnTypeBackup; return;
}
xListCursor.reset( m_aListRowSet.execute() );
}
} catch(const SQLException& eSQL)
{
onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST)); return;
} catch(const Exception&)
{ return;
}
// Fill display and value lists
ValueList aDisplayList, aValueList; bool bUseNULL = hasField() && !isRequired();
// empty BoundColumn is treated as BoundColumn==0, if(!aBoundColumn)
aBoundColumn = 0;
switch (m_eListSourceType)
{ #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS case ListSourceType_SQL: case ListSourceType_SQLPASSTHROUGH: case ListSourceType_TABLE: case ListSourceType_QUERY:
{ // Get field of the ResultSet's 1st column
Reference<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
DBG_ASSERT(xSupplyCols.is(), "OListBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
Reference<XIndexAccess> xColumns; if (xSupplyCols.is())
{
xColumns.set(xSupplyCols->getColumns(), UNO_QUERY);
DBG_ASSERT(xColumns.is(), "OListBoxModel::loadData : no columns supplied by the row set !");
}
Reference< XPropertySet > xDataField; if ( xColumns.is() )
xColumns->getByIndex(0) >>= xDataField; if ( !xDataField.is() ) return;
// Get the field of BoundColumn of the ResultSet
m_nBoundColumnType = DataType::SQLNULL; if ( *aBoundColumn >= 0 )
{ try
{
Reference< XPropertySet > xBoundField( xColumns->getByIndex( *aBoundColumn ), UNO_QUERY_THROW );
OSL_VERIFY( xBoundField->getPropertyValue(u"Type"_ustr) >>= m_nBoundColumnType );
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("forms.component");
}
} elseif ( *aBoundColumn == -1)
m_nBoundColumnType = DataType::SMALLINT;
// If the LB is bound to a field and empty entries are valid, we remember the position // for an empty entry
SAL_INFO( "forms.component", "OListBoxModel::loadData: string collection" );
OUString aStr;
sal_Int16 entryPos = 0;
ORowSetValue aBoundValue;
Reference< XRow > xCursorRow( xListCursor, UNO_QUERY_THROW ); while ( xListCursor->next() && ( entryPos++ < SHRT_MAX ) ) // SHRT_MAX is the maximum number of entries
{
aStr = aValueFormatter.getFormattedValue();
aDisplayList.emplace_back(aStr );
if(*aBoundColumn >= 0)
aBoundValue.fill( *aBoundColumn + 1, m_nBoundColumnType, xCursorRow ); else // -1 because getRow() is 1-indexed, but ListBox positions are 0-indexed
aBoundValue = static_cast<sal_Int16>(xListCursor->getRow()-1);
aValueList.push_back( aBoundValue );
if ( m_nNULLPos == -1 && aBoundValue.isNull() )
m_nNULLPos = sal_Int16( aDisplayList.size() - 1 ); if ( bUseNULL && ( m_nNULLPos == -1 ) && aStr.isEmpty() ) // There is already a non-NULL entry with empty display string; // adding another one for NULL would make things confusing, // so back off.
bUseNULL = false;
}
} break; #endif case ListSourceType_TABLEFIELDS:
{
Reference<XNameAccess> xFieldNames = getTableFields(xConnection, sListSource); if (xFieldNames.is())
{ const css::uno::Sequence<OUString> seqNames = xFieldNames->getElementNames();
::std::copy(
seqNames.begin(),
seqNames.end(),
::std::insert_iterator< ValueList >( aDisplayList, aDisplayList.end() )
); if(*aBoundColumn == -1)
{ // the type of i matters! It will be the type of the ORowSetValue pushed to aValueList! for(size_t i=0; i < aDisplayList.size(); ++i)
{
aValueList.emplace_back(sal_Int16(i));
}
} else
{
aValueList = aDisplayList;
}
}
} break; default:
SAL_WARN( "forms.component", "OListBoxModel::loadData: unreachable!" ); break;
}
} catch(const SQLException& eSQL)
{
onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST)); return;
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("forms.component"); return;
}
void OListBoxModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
{ // list boxes which are bound to a db column don't have multi selection // - this would be unable to reflect in the db column if ( hasField() )
{
setFastPropertyValue( PROPERTY_ID_MULTISELECTION, css::uno::Any(false) );
}
if ( !hasExternalListSource() )
impl_refreshDbEntryList( false );
}
namespace
{ // The type of how we should transfer our selection to external value bindings enum ExchangeType
{
eIndexList, /// as list of indexes of selected entries
eIndex, /// as index of the selected entry
eEntryList, /// as list of string representations of selected *display* entries
eEntry, /// as string representation of the selected *display* entry
eValueList, /// as list of string representations of selected values
eValue /// as string representation of the selected value
};
ExchangeType lcl_getCurrentExchangeType( const Type& _rExchangeType )
{ switch ( _rExchangeType.getTypeClass() )
{ case TypeClass_ANY: return eValue; case TypeClass_STRING: return eEntry; case TypeClass_LONG: return eIndex; case TypeClass_SEQUENCE:
{
Type aElementType = ::comphelper::getSequenceElementType( _rExchangeType ); switch ( aElementType.getTypeClass() )
{ case TypeClass_ANY: return eValueList; case TypeClass_STRING: return eEntryList; case TypeClass_LONG: return eIndexList; default: break;
} break;
} default: break;
}
SAL_WARN( "forms.component", "lcl_getCurrentExchangeType: unsupported (unexpected) exchange type!" ); return eEntry;
}
}
case eEntryList:
{ // we can retrieve a string list from the binding for multiple selection
Sequence< OUString > aSelectEntries;
OSL_VERIFY( _rExternalValue >>= aSelectEntries );
::std::set< sal_Int16 > aSelectionSet;
// find the selection entries in our item list for (OUString const& selectEntry : aSelectEntries)
{ int idx = 0; for(const OUString& s : getStringItemList())
{ if (s==selectEntry)
aSelectionSet.insert(idx);
++idx;
}
}
// copy the indexes to the sequence
aSelectIndexes = comphelper::containerToSequence( aSelectionSet );
} break;
case eEntry:
{
OUString sStringToSelect;
OSL_VERIFY( _rExternalValue >>= sStringToSelect );
::std::set< sal_Int16 > aSelectionSet; int idx = 0; for(const OUString& s : getStringItemList())
{ if (s==sStringToSelect)
aSelectionSet.insert(idx);
++idx;
}
Any lcl_getSingleSelectedEntryTyped( const Sequence< sal_Int16 >& _rSelectSequence, const Sequence<Any>& _rTypedList )
{
Any aReturn;
// by definition, multiple selected entries are transferred as NULL if the // binding does not support lists if ( _rSelectSequence.getLength() <= 1 )
{ if ( _rSelectSequence.getLength() == 1 )
{
sal_Int32 nIndex = _rSelectSequence[0]; if (0 <= nIndex && nIndex < _rTypedList.getLength())
aReturn = _rTypedList[nIndex];
}
}
return aReturn;
}
Any lcl_getSingleSelectedEntry( const Sequence< sal_Int16 >& _rSelectSequence, const std::vector< OUString >& _rStringList )
{
Any aReturn;
// by definition, multiple selected entries are transferred as NULL if the // binding does not support string lists if ( _rSelectSequence.getLength() <= 1 )
{
OUString sSelectedEntry;
Any lcl_getSingleSelectedEntryAny( const Sequence< sal_Int16 >& _rSelectSequence, const ValueList& _rStringList )
{
Any aReturn;
// by definition, multiple selected entries are transferred as NULL if the // binding does not support string lists if ( _rSelectSequence.getLength() <= 1 )
{ if ( _rSelectSequence.getLength() == 1 )
aReturn = ExtractAnyFromValueList_Safe( _rStringList )( _rSelectSequence[0] );
}
Any OListBoxModel::translateControlValueToValidatableValue( ) const
{
OSL_PRECOND( hasValidator(), "OListBoxModel::translateControlValueToValidatableValue: no validator, so why should I?" ); return getCurrentFormComponentValue();
}
Any OListBoxModel::getCurrentSingleValue() const
{
Any aCurrentValue;
// update the selection here if ( hasExternalValueBinding( ) )
transferExternalValueToControl( _rInstanceLock ); else
{ if ( hasField() )
{ // TODO: update the selection in case we're bound to a database column
} else
{ if ( m_aDefaultSelectSeq.hasElements() )
setControlValue( Any( m_aDefaultSelectSeq ), eOther );
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.