Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : #include "ComboBox.hxx"
22 : #include "property.hxx"
23 : #include "property.hrc"
24 : #include "services.hxx"
25 :
26 : #include "frm_resource.hxx"
27 : #include "frm_resource.hrc"
28 : #include "BaseListBox.hxx"
29 :
30 : #include <com/sun/star/sdb/SQLErrorEvent.hpp>
31 : #include <com/sun/star/sdbc/XRowSet.hpp>
32 : #include <com/sun/star/sdbc/DataType.hpp>
33 : #include <com/sun/star/container/XIndexAccess.hpp>
34 : #include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp>
35 : #include <com/sun/star/sdb/XQueriesSupplier.hpp>
36 : #include <com/sun/star/util/NumberFormat.hpp>
37 : #include <com/sun/star/sdbc/XConnection.hpp>
38 : #include <com/sun/star/sdb/SQLContext.hpp>
39 : #include <com/sun/star/sdb/CommandType.hpp>
40 :
41 : #include <comphelper/numbers.hxx>
42 : #include <comphelper/basicio.hxx>
43 : #include <comphelper/processfactory.hxx>
44 : #include <connectivity/dbtools.hxx>
45 : #include <connectivity/dbconversion.hxx>
46 : #include <cppuhelper/queryinterface.hxx>
47 : #include <rtl/ustrbuf.hxx>
48 : #include <tools/debug.hxx>
49 : #include <tools/diagnose_ex.h>
50 : #include <unotools/sharedunocomponent.hxx>
51 :
52 : #include <limits.h>
53 :
54 : using namespace dbtools;
55 :
56 :
57 : namespace frm
58 : {
59 : using namespace ::com::sun::star::uno;
60 : using namespace ::com::sun::star::sdb;
61 : using namespace ::com::sun::star::sdbc;
62 : using namespace ::com::sun::star::sdbcx;
63 : using namespace ::com::sun::star::beans;
64 : using namespace ::com::sun::star::container;
65 : using namespace ::com::sun::star::form;
66 : using namespace ::com::sun::star::awt;
67 : using namespace ::com::sun::star::io;
68 : using namespace ::com::sun::star::lang;
69 : using namespace ::com::sun::star::util;
70 : using namespace ::com::sun::star::form::binding;
71 :
72 :
73 : // class OComboBoxModel
74 22 : Sequence<Type> OComboBoxModel::_getTypes()
75 : {
76 : return ::comphelper::concatSequences(
77 : OBoundControlModel::_getTypes(),
78 : OEntryListHelper::getTypes(),
79 : OErrorBroadcaster::getTypes()
80 22 : );
81 : }
82 :
83 : // XServiceInfo
84 :
85 28 : StringSequence SAL_CALL OComboBoxModel::getSupportedServiceNames() throw(RuntimeException, std::exception)
86 : {
87 28 : StringSequence aSupported = OBoundControlModel::getSupportedServiceNames();
88 :
89 28 : sal_Int32 nOldLen = aSupported.getLength();
90 28 : aSupported.realloc( nOldLen + 9 );
91 28 : OUString* pStoreTo = aSupported.getArray() + nOldLen;
92 :
93 28 : *pStoreTo++ = BINDABLE_CONTROL_MODEL;
94 28 : *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
95 28 : *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
96 :
97 28 : *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
98 28 : *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
99 :
100 28 : *pStoreTo++ = FRM_SUN_COMPONENT_COMBOBOX;
101 28 : *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_COMBOBOX;
102 28 : *pStoreTo++ = BINDABLE_DATABASE_COMBO_BOX;
103 :
104 28 : *pStoreTo++ = FRM_COMPONENT_COMBOBOX;
105 :
106 28 : return aSupported;
107 : }
108 :
109 :
110 9054 : Any SAL_CALL OComboBoxModel::queryAggregation(const Type& _rType) throw (RuntimeException, std::exception)
111 : {
112 9054 : Any aReturn = OBoundControlModel::queryAggregation( _rType );
113 9054 : if ( !aReturn.hasValue() )
114 368 : aReturn = OEntryListHelper::queryInterface( _rType );
115 9054 : if ( !aReturn.hasValue() )
116 329 : aReturn = OErrorBroadcaster::queryInterface( _rType );
117 9054 : return aReturn;
118 : }
119 :
120 :
121 :
122 30 : OComboBoxModel::OComboBoxModel(const Reference<XComponentContext>& _rxFactory)
123 : :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_COMBOBOX, FRM_SUN_CONTROL_COMBOBOX, true, true, true )
124 : // use the old control name for compatibility reasons
125 : ,OEntryListHelper( (OControlModel&)*this )
126 : ,OErrorBroadcaster( OComponentHelper::rBHelper )
127 : ,m_aListRowSet()
128 : ,m_eListSourceType(ListSourceType_TABLE)
129 30 : ,m_bEmptyIsNull(true)
130 : {
131 30 : m_nClassId = FormComponentType::COMBOBOX;
132 30 : initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT );
133 30 : }
134 :
135 :
136 1 : OComboBoxModel::OComboBoxModel( const OComboBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
137 : :OBoundControlModel( _pOriginal, _rxFactory )
138 : ,OEntryListHelper( *_pOriginal, (OControlModel&)*this )
139 : ,OErrorBroadcaster( OComponentHelper::rBHelper )
140 : ,m_aListRowSet()
141 : ,m_aListSource( _pOriginal->m_aListSource )
142 : ,m_aDefaultText( _pOriginal->m_aDefaultText )
143 : ,m_eListSourceType( _pOriginal->m_eListSourceType )
144 1 : ,m_bEmptyIsNull( _pOriginal->m_bEmptyIsNull )
145 : {
146 1 : }
147 :
148 :
149 87 : OComboBoxModel::~OComboBoxModel()
150 : {
151 29 : if (!OComponentHelper::rBHelper.bDisposed)
152 : {
153 0 : acquire();
154 0 : dispose();
155 : }
156 :
157 58 : }
158 :
159 : // XCloneable
160 :
161 1 : IMPLEMENT_DEFAULT_CLONING( OComboBoxModel )
162 :
163 :
164 29 : void OComboBoxModel::disposing()
165 : {
166 29 : OBoundControlModel::disposing();
167 29 : OEntryListHelper::disposing();
168 29 : OErrorBroadcaster::disposing();
169 29 : m_xFormatter = NULL;
170 29 : }
171 :
172 :
173 5274 : void OComboBoxModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
174 : {
175 5274 : switch (_nHandle)
176 : {
177 : case PROPERTY_ID_LISTSOURCETYPE:
178 99 : _rValue <<= m_eListSourceType;
179 99 : break;
180 :
181 : case PROPERTY_ID_LISTSOURCE:
182 99 : _rValue <<= m_aListSource;
183 99 : break;
184 :
185 : case PROPERTY_ID_EMPTY_IS_NULL:
186 99 : _rValue <<= m_bEmptyIsNull;
187 99 : break;
188 :
189 : case PROPERTY_ID_DEFAULT_TEXT:
190 100 : _rValue <<= m_aDefaultText;
191 100 : break;
192 :
193 : case PROPERTY_ID_STRINGITEMLIST:
194 133 : _rValue <<= getStringItemList();
195 133 : break;
196 :
197 : default:
198 4744 : OBoundControlModel::getFastPropertyValue(_rValue, _nHandle);
199 : }
200 5274 : }
201 :
202 :
203 220 : void OComboBoxModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
204 : throw (Exception, std::exception)
205 : {
206 220 : switch (_nHandle)
207 : {
208 : case PROPERTY_ID_LISTSOURCETYPE :
209 : DBG_ASSERT(_rValue.getValueType().equals(::cppu::UnoType<ListSourceType>::get()),
210 : "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
211 11 : _rValue >>= m_eListSourceType;
212 11 : break;
213 :
214 : case PROPERTY_ID_LISTSOURCE :
215 : DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING,
216 : "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
217 11 : _rValue >>= m_aListSource;
218 : // The ListSource has changed -> reload
219 11 : if (ListSourceType_VALUELIST != m_eListSourceType)
220 : {
221 8 : if ( m_xCursor.is() && !hasField() && !hasExternalListSource() )
222 : // combo box is already connected to a database, and no external list source
223 : // data source changed -> refresh
224 0 : loadData( false );
225 : }
226 11 : break;
227 :
228 : case PROPERTY_ID_EMPTY_IS_NULL :
229 : DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN,
230 : "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
231 11 : _rValue >>= m_bEmptyIsNull;
232 11 : break;
233 :
234 : case PROPERTY_ID_DEFAULT_TEXT :
235 : DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING,
236 : "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
237 27 : _rValue >>= m_aDefaultText;
238 27 : resetNoBroadcast();
239 27 : break;
240 :
241 : case PROPERTY_ID_STRINGITEMLIST:
242 : {
243 33 : ControlModelLock aLock( *this );
244 33 : setNewStringItemList( _rValue, aLock );
245 : // FIXME: this is bogus. setNewStringItemList expects a guard which has the *only*
246 : // lock to the mutex, but setFastPropertyValue_NoBroadcast is already called with
247 : // a lock - so we effectively has two locks here, of which setNewStringItemList can
248 : // only control one.
249 : }
250 33 : break;
251 :
252 : default:
253 127 : OBoundControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
254 : }
255 220 : }
256 :
257 :
258 836 : sal_Bool OComboBoxModel::convertFastPropertyValue(
259 : Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
260 : throw (IllegalArgumentException)
261 : {
262 836 : bool bModified(false);
263 836 : switch (_nHandle)
264 : {
265 : case PROPERTY_ID_LISTSOURCETYPE :
266 58 : bModified = tryPropertyValueEnum(_rConvertedValue, _rOldValue, _rValue, m_eListSourceType);
267 58 : break;
268 :
269 : case PROPERTY_ID_LISTSOURCE :
270 58 : bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aListSource);
271 58 : break;
272 :
273 : case PROPERTY_ID_EMPTY_IS_NULL :
274 58 : bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bEmptyIsNull);
275 58 : break;
276 :
277 : case PROPERTY_ID_DEFAULT_TEXT :
278 75 : bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aDefaultText);
279 75 : break;
280 :
281 : case PROPERTY_ID_STRINGITEMLIST:
282 80 : bModified = convertNewListSourceProperty( _rConvertedValue, _rOldValue, _rValue );
283 80 : break;
284 :
285 : default:
286 507 : bModified = OBoundControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
287 507 : break;
288 : }
289 836 : return bModified;
290 : }
291 :
292 :
293 32 : void OComboBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const
294 : {
295 32 : BEGIN_DESCRIBE_PROPERTIES( 6, OBoundControlModel )
296 32 : DECL_PROP1(TABINDEX, sal_Int16, BOUND);
297 32 : DECL_PROP1(LISTSOURCETYPE, ListSourceType, BOUND);
298 32 : DECL_PROP1(LISTSOURCE, OUString, BOUND);
299 32 : DECL_BOOL_PROP1(EMPTY_IS_NULL, BOUND);
300 32 : DECL_PROP1(DEFAULT_TEXT, OUString, BOUND);
301 32 : DECL_PROP1(STRINGITEMLIST, Sequence< OUString >,BOUND);
302 : END_DESCRIBE_PROPERTIES();
303 32 : }
304 :
305 :
306 32 : void OComboBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
307 : {
308 32 : OBoundControlModel::describeAggregateProperties( _rAggregateProps );
309 :
310 : // superseded properties:
311 32 : RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST );
312 32 : }
313 :
314 :
315 2 : OUString SAL_CALL OComboBoxModel::getServiceName() throw(RuntimeException, std::exception)
316 : {
317 2 : return OUString(FRM_COMPONENT_COMBOBOX); // old (non-sun) name for compatibility !
318 : }
319 :
320 :
321 1 : void SAL_CALL OComboBoxModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
322 : throw(css::io::IOException, RuntimeException, std::exception)
323 : {
324 1 : OBoundControlModel::write(_rxOutStream);
325 :
326 : // Version
327 : // Version 0x0002: EmptyIsNull
328 : // Version 0x0003: ListSource->Seq
329 : // Version 0x0004: DefaultText
330 : // Version 0x0005: HelpText
331 1 : _rxOutStream->writeShort(0x0006);
332 :
333 : // Mask for Any
334 1 : sal_uInt16 nAnyMask = 0;
335 1 : if (m_aBoundColumn.getValueType().getTypeClass() == TypeClass_SHORT)
336 0 : nAnyMask |= BOUNDCOLUMN;
337 1 : _rxOutStream << nAnyMask;
338 :
339 1 : StringSequence aListSourceSeq(&m_aListSource, 1);
340 1 : _rxOutStream << aListSourceSeq;
341 1 : _rxOutStream << (sal_Int16)m_eListSourceType;
342 :
343 1 : if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
344 : {
345 0 : sal_Int16 nBoundColumn = 0;
346 0 : m_aBoundColumn >>= nBoundColumn;
347 0 : _rxOutStream << nBoundColumn;
348 : }
349 :
350 1 : _rxOutStream << m_bEmptyIsNull;
351 1 : _rxOutStream << m_aDefaultText;
352 1 : writeHelpTextCompatibly(_rxOutStream);
353 :
354 : // from version 0x0006 : common properties
355 1 : writeCommonProperties(_rxOutStream);
356 1 : }
357 :
358 :
359 1 : void SAL_CALL OComboBoxModel::read(const Reference<css::io::XObjectInputStream>& _rxInStream) throw(css::io::IOException, RuntimeException, std::exception)
360 : {
361 1 : OBoundControlModel::read(_rxInStream);
362 1 : ControlModelLock aLock( *this );
363 :
364 : // since we are "overwriting" the StringItemList of our aggregate (means we have
365 : // an own place to store the value, instead of relying on our aggregate storing it),
366 : // we need to respect what the aggregate just read for the StringItemList property.
367 : try
368 : {
369 1 : if ( m_xAggregateSet.is() )
370 1 : setNewStringItemList( m_xAggregateSet->getPropertyValue( PROPERTY_STRINGITEMLIST ), aLock );
371 : }
372 0 : catch( const Exception& )
373 : {
374 : OSL_FAIL( "OComboBoxModel::read: caught an exception while examining the aggregate's string item list!" );
375 : }
376 :
377 : // Version
378 1 : sal_uInt16 nVersion = _rxInStream->readShort();
379 : DBG_ASSERT(nVersion > 0, "OComboBoxModel::read : version 0 ? this should never have been written !");
380 :
381 1 : if (nVersion > 0x0006)
382 : {
383 : OSL_FAIL("OComboBoxModel::read : invalid (means unknown) version !");
384 0 : m_aListSource.clear();
385 0 : m_aBoundColumn <<= (sal_Int16)0;
386 0 : m_aDefaultText.clear();
387 0 : m_eListSourceType = ListSourceType_TABLE;
388 0 : m_bEmptyIsNull = true;
389 0 : defaultCommonProperties();
390 1 : return;
391 : }
392 :
393 : // Mask for Any
394 : sal_uInt16 nAnyMask;
395 1 : _rxInStream >> nAnyMask;
396 :
397 : // ListSource
398 1 : if (nVersion < 0x0003)
399 : {
400 0 : _rxInStream >> m_aListSource;
401 : }
402 : else // nVersion == 4
403 : {
404 1 : m_aListSource.clear();
405 1 : StringSequence aListSource;
406 1 : _rxInStream >> aListSource;
407 1 : const OUString* pToken = aListSource.getConstArray();
408 1 : sal_Int32 nLen = aListSource.getLength();
409 2 : for (sal_Int32 i = 0; i < nLen; ++i, ++pToken)
410 2 : m_aListSource += *pToken;
411 : }
412 :
413 : sal_Int16 nListSourceType;
414 1 : _rxInStream >> nListSourceType;
415 1 : m_eListSourceType = (ListSourceType)nListSourceType;
416 :
417 1 : if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
418 : {
419 : sal_Int16 nValue;
420 0 : _rxInStream >> nValue;
421 0 : m_aBoundColumn <<= nValue;
422 : }
423 :
424 1 : if (nVersion > 0x0001)
425 : {
426 : bool bNull;
427 1 : _rxInStream >> bNull;
428 1 : m_bEmptyIsNull = bNull;
429 : }
430 :
431 1 : if (nVersion > 0x0003) // nVersion == 4
432 1 : _rxInStream >> m_aDefaultText;
433 :
434 : // StringList must be emptied if a ListSource is set.
435 : // This can be the case if we save in alive mode.
436 2 : if ( !m_aListSource.isEmpty()
437 1 : && !hasExternalListSource()
438 : )
439 : {
440 0 : setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, makeAny( StringSequence() ) );
441 : }
442 :
443 1 : if (nVersion > 0x0004)
444 1 : readHelpTextCompatibly(_rxInStream);
445 :
446 1 : if (nVersion > 0x0005)
447 1 : readCommonProperties(_rxInStream);
448 :
449 : // After reading in, display the default values
450 1 : if ( !getControlSource().isEmpty() )
451 : {
452 : // (not if we don't have a control source - the "State" property acts like it is persistent, then
453 1 : resetNoBroadcast();
454 1 : }
455 : }
456 :
457 :
458 0 : void OComboBoxModel::loadData( bool _bForce )
459 : {
460 : DBG_ASSERT(m_eListSourceType != ListSourceType_VALUELIST, "OComboBoxModel::loadData : do not call for a value list !");
461 : DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::loadData: cannot load from DB when I have an external list source!" );
462 :
463 0 : if ( hasExternalListSource() )
464 0 : return;
465 :
466 : // Get Connection
467 0 : Reference<XRowSet> xForm(m_xCursor, UNO_QUERY);
468 0 : if (!xForm.is())
469 0 : return;
470 0 : Reference<XConnection> xConnection = getConnection(xForm);
471 0 : if (!xConnection.is())
472 0 : return;
473 :
474 0 : Reference<XServiceInfo> xServiceInfo(xConnection, UNO_QUERY);
475 0 : if (!xServiceInfo.is() || !xServiceInfo->supportsService(SRV_SDB_CONNECTION))
476 : {
477 : OSL_FAIL("OComboBoxModel::loadData : invalid connection !");
478 0 : return;
479 : }
480 :
481 0 : if (m_aListSource.isEmpty() || m_eListSourceType == ListSourceType_VALUELIST)
482 0 : return;
483 :
484 0 : ::utl::SharedUNOComponent< XResultSet > xListCursor;
485 : try
486 : {
487 0 : m_aListRowSet.setConnection( xConnection );
488 :
489 0 : bool bExecuteRowSet( false );
490 0 : switch (m_eListSourceType)
491 : {
492 : case ListSourceType_TABLEFIELDS:
493 : // don't work with a statement here, the fields will be collected below
494 0 : break;
495 : case ListSourceType_TABLE:
496 : {
497 : // does the bound field belong to the table ?
498 : // if we use an alias for the bound field, we won't find it
499 : // in that case we use the first field of the table
500 :
501 0 : Reference<XNameAccess> xFieldsByName = getTableFields(xConnection, m_aListSource);
502 0 : Reference<XIndexAccess> xFieldsByIndex(xFieldsByName, UNO_QUERY);
503 :
504 0 : OUString aFieldName;
505 0 : if ( xFieldsByName.is() && xFieldsByName->hasByName( getControlSource() ) )
506 : {
507 0 : aFieldName = getControlSource();
508 : }
509 : else
510 : {
511 : // otherwise look for the alias
512 0 : Reference<XPropertySet> xFormProp(xForm,UNO_QUERY);
513 0 : Reference< XColumnsSupplier > xSupplyFields;
514 0 : xFormProp->getPropertyValue("SingleSelectQueryComposer") >>= xSupplyFields;
515 :
516 : // search the field
517 : DBG_ASSERT(xSupplyFields.is(), "OComboBoxModel::loadData : invalid query composer !");
518 :
519 0 : Reference< XNameAccess > xFieldNames = xSupplyFields->getColumns();
520 0 : if ( xFieldNames->hasByName( getControlSource() ) )
521 : {
522 0 : Reference< XPropertySet > xComposerFieldAsSet;
523 0 : xFieldNames->getByName( getControlSource() ) >>= xComposerFieldAsSet;
524 0 : if (hasProperty(PROPERTY_FIELDSOURCE, xComposerFieldAsSet))
525 0 : xComposerFieldAsSet->getPropertyValue(PROPERTY_FIELDSOURCE) >>= aFieldName;
526 0 : }
527 : }
528 :
529 0 : if (aFieldName.isEmpty())
530 0 : break;
531 :
532 0 : Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
533 : OSL_ENSURE(xMeta.is(),"No database meta data!");
534 0 : if ( xMeta.is() )
535 : {
536 0 : OUString aQuote = xMeta->getIdentifierQuoteString();
537 :
538 0 : OUString sCatalog, sSchema, sTable;
539 0 : qualifiedNameComponents( xMeta, m_aListSource, sCatalog, sSchema, sTable, eInDataManipulation );
540 :
541 0 : OUStringBuffer aStatement;
542 0 : aStatement.appendAscii( "SELECT DISTINCT " );
543 0 : aStatement.append ( quoteName( aQuote, aFieldName ) );
544 0 : aStatement.appendAscii( " FROM " );
545 0 : aStatement.append ( composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable ) );
546 :
547 0 : m_aListRowSet.setEscapeProcessing( false );
548 0 : m_aListRowSet.setCommand( aStatement.makeStringAndClear() );
549 0 : bExecuteRowSet = true;
550 0 : }
551 0 : } break;
552 : case ListSourceType_QUERY:
553 : {
554 0 : m_aListRowSet.setCommandFromQuery( m_aListSource );
555 0 : bExecuteRowSet = true;
556 : }
557 0 : break;
558 :
559 : default:
560 : {
561 0 : m_aListRowSet.setEscapeProcessing( ListSourceType_SQLPASSTHROUGH != m_eListSourceType );
562 0 : m_aListRowSet.setCommand( m_aListSource );
563 0 : bExecuteRowSet = true;
564 : }
565 : }
566 :
567 0 : if ( bExecuteRowSet )
568 : {
569 0 : if ( !_bForce && !m_aListRowSet.isDirty() )
570 : {
571 : // if none of the settings of the row set changed, compared to the last
572 : // invocation of loadData, then don't re-fill the list. Instead, assume
573 : // the list entries are the same.
574 0 : return;
575 : }
576 0 : xListCursor.reset( m_aListRowSet.execute() );
577 : }
578 : }
579 0 : catch(const SQLException& eSQL)
580 : {
581 0 : onError(eSQL, FRM_RES_STRING(RID_BASELISTBOX_ERROR_FILLLIST));
582 0 : return;
583 : }
584 0 : catch( const Exception& )
585 : {
586 : DBG_UNHANDLED_EXCEPTION();
587 0 : return;
588 : }
589 :
590 0 : ::std::vector< OUString > aStringList;
591 0 : aStringList.reserve(16);
592 : try
593 : {
594 : OSL_ENSURE( xListCursor.is() || ( ListSourceType_TABLEFIELDS == m_eListSourceType ),
595 : "OComboBoxModel::loadData: logic error!" );
596 0 : if ( !xListCursor.is() && ( ListSourceType_TABLEFIELDS != m_eListSourceType ) )
597 0 : return;
598 :
599 0 : switch (m_eListSourceType)
600 : {
601 : case ListSourceType_SQL:
602 : case ListSourceType_SQLPASSTHROUGH:
603 : case ListSourceType_TABLE:
604 : case ListSourceType_QUERY:
605 : {
606 : // The XDatabaseVariant of the first column
607 0 : Reference<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
608 : DBG_ASSERT(xSupplyCols.is(), "OComboBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
609 0 : Reference<XIndexAccess> xColumns;
610 0 : if (xSupplyCols.is())
611 : {
612 0 : xColumns = Reference<XIndexAccess>(xSupplyCols->getColumns(), UNO_QUERY);
613 : DBG_ASSERT(xColumns.is(), "OComboBoxModel::loadData : no columns supplied by the row set !");
614 : }
615 0 : Reference< XPropertySet > xDataField;
616 0 : if ( xColumns.is() )
617 0 : xColumns->getByIndex(0) >>= xDataField;
618 0 : if ( !xDataField.is() )
619 0 : return;
620 :
621 0 : ::dbtools::FormattedColumnValue aValueFormatter( getContext(), xForm, xDataField );
622 :
623 : // Fill Lists
624 0 : sal_Int16 i = 0;
625 : // At the moment by definition the list cursor is positioned _before_ the first row
626 0 : while (xListCursor->next() && (i++<SHRT_MAX)) // Set max. count
627 : {
628 0 : aStringList.push_back( aValueFormatter.getFormattedValue() );
629 0 : }
630 : }
631 0 : break;
632 : case ListSourceType_TABLEFIELDS:
633 : {
634 0 : Reference<XNameAccess> xFieldNames = getTableFields(xConnection, m_aListSource);
635 0 : if (xFieldNames.is())
636 : {
637 0 : StringSequence seqNames = xFieldNames->getElementNames();
638 0 : sal_Int32 nFieldsCount = seqNames.getLength();
639 0 : const OUString* pustrNames = seqNames.getConstArray();
640 :
641 0 : for (sal_Int32 k=0; k<nFieldsCount; ++k)
642 0 : aStringList.push_back(pustrNames[k]);
643 0 : }
644 : }
645 0 : break;
646 : default:
647 : OSL_FAIL( "OComboBoxModel::loadData: unreachable!" );
648 0 : break;
649 : }
650 : }
651 0 : catch(const SQLException& eSQL)
652 : {
653 0 : onError(eSQL, FRM_RES_STRING(RID_BASELISTBOX_ERROR_FILLLIST));
654 0 : return;
655 : }
656 0 : catch( const Exception& )
657 : {
658 : DBG_UNHANDLED_EXCEPTION();
659 0 : return;
660 : }
661 :
662 : // Create StringSequence for ListBox
663 0 : StringSequence aStringSeq(aStringList.size());
664 0 : OUString* pStringAry = aStringSeq.getArray();
665 0 : for (sal_Int32 i = 0; i<aStringSeq.getLength(); ++i)
666 0 : pStringAry[i] = aStringList[i];
667 :
668 : // Set String-Sequence at ListBox
669 0 : setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, makeAny( aStringSeq ) );
670 : }
671 :
672 :
673 0 : void OComboBoxModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
674 : {
675 0 : Reference<XPropertySet> xField = getField();
676 0 : if ( xField.is() )
677 0 : m_pValueFormatter.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) );
678 0 : getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= m_aDesignModeStringItems;
679 :
680 : // Only load data if a ListSource was supplied
681 0 : if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() )
682 0 : loadData( false );
683 0 : }
684 :
685 :
686 0 : void OComboBoxModel::onDisconnectedDbColumn()
687 : {
688 0 : m_pValueFormatter.reset();
689 :
690 : // reset the string item list
691 0 : if ( !hasExternalListSource() )
692 0 : setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, makeAny( m_aDesignModeStringItems ) );
693 :
694 0 : m_aListRowSet.dispose();
695 0 : }
696 :
697 :
698 0 : void SAL_CALL OComboBoxModel::reloaded( const EventObject& aEvent ) throw(RuntimeException, std::exception)
699 : {
700 0 : OBoundControlModel::reloaded(aEvent);
701 :
702 : // reload data if we have a list source
703 0 : if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() )
704 0 : loadData( false );
705 0 : }
706 :
707 :
708 30 : void OComboBoxModel::resetNoBroadcast()
709 : {
710 30 : OBoundControlModel::resetNoBroadcast();
711 30 : m_aLastKnownValue.clear();
712 30 : }
713 :
714 :
715 0 : bool OComboBoxModel::commitControlValueToDbColumn( bool _bPostReset )
716 : {
717 0 : Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
718 :
719 0 : OUString sNewValue;
720 0 : aNewValue >>= sNewValue;
721 :
722 0 : bool bModified = ( aNewValue != m_aLastKnownValue );
723 0 : if ( bModified )
724 : {
725 0 : if ( !aNewValue.hasValue()
726 0 : || ( sNewValue.isEmpty() // an empty string
727 0 : && m_bEmptyIsNull // which should be interpreted as NULL
728 : )
729 : )
730 : {
731 0 : m_xColumnUpdate->updateNull();
732 : }
733 : else
734 : {
735 : try
736 : {
737 : OSL_PRECOND( m_pValueFormatter.get(), "OComboBoxModel::commitControlValueToDbColumn: no value formatter!" );
738 0 : if ( m_pValueFormatter.get() )
739 : {
740 0 : if ( !m_pValueFormatter->setFormattedValue( sNewValue ) )
741 0 : return false;
742 : }
743 : else
744 0 : m_xColumnUpdate->updateString( sNewValue );
745 : }
746 0 : catch ( const Exception& )
747 : {
748 0 : return false;
749 : }
750 : }
751 :
752 0 : m_aLastKnownValue = aNewValue;
753 : }
754 :
755 : // add the new value to the list
756 0 : bool bAddToList = bModified && !_bPostReset;
757 : // (only if this is not the "commit" triggered by a "reset")
758 :
759 0 : if ( bAddToList )
760 : {
761 0 : StringSequence aStringItemList;
762 0 : if ( getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aStringItemList )
763 : {
764 0 : const OUString* pStringItems = aStringItemList.getConstArray();
765 : sal_Int32 i;
766 0 : for (i=0; i<aStringItemList.getLength(); ++i, ++pStringItems)
767 : {
768 0 : if ( pStringItems->equals( sNewValue ) )
769 0 : break;
770 : }
771 :
772 : // not found -> add
773 0 : if (i >= aStringItemList.getLength())
774 : {
775 0 : sal_Int32 nOldLen = aStringItemList.getLength();
776 0 : aStringItemList.realloc( nOldLen + 1 );
777 0 : aStringItemList.getArray()[ nOldLen ] = sNewValue;
778 :
779 0 : setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, makeAny( aStringItemList ) );
780 : }
781 0 : }
782 : }
783 :
784 0 : return true;
785 : }
786 :
787 : // XPropertiesChangeListener
788 :
789 0 : Any OComboBoxModel::translateDbColumnToControlValue()
790 : {
791 : OSL_PRECOND( m_pValueFormatter.get(), "OComboBoxModel::translateDbColumnToControlValue: no value formatter!" );
792 0 : if ( m_pValueFormatter.get() )
793 : {
794 0 : OUString sValue( m_pValueFormatter->getFormattedValue() );
795 0 : if ( sValue.isEmpty()
796 0 : && m_pValueFormatter->getColumn().is()
797 0 : && m_pValueFormatter->getColumn()->wasNull()
798 : )
799 : {
800 0 : m_aLastKnownValue.clear();
801 : }
802 : else
803 : {
804 :
805 0 : m_aLastKnownValue <<= sValue;
806 0 : }
807 : }
808 : else
809 0 : m_aLastKnownValue.clear();
810 :
811 0 : return m_aLastKnownValue.hasValue() ? m_aLastKnownValue : makeAny( OUString() );
812 : // (m_aLastKnownValue is alllowed to be VOID, the control value isn't)
813 : }
814 :
815 :
816 30 : Any OComboBoxModel::getDefaultForReset() const
817 : {
818 30 : return makeAny( m_aDefaultText );
819 : }
820 :
821 :
822 37 : void OComboBoxModel::stringItemListChanged( ControlModelLock& /*_rInstanceLock*/ )
823 : {
824 37 : if ( m_xAggregateSet.is() )
825 37 : m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, makeAny( getStringItemList() ) );
826 37 : }
827 :
828 :
829 3 : void OComboBoxModel::connectedExternalListSource( )
830 : {
831 : // TODO?
832 3 : }
833 :
834 :
835 4 : void OComboBoxModel::disconnectedExternalListSource( )
836 : {
837 : // TODO?
838 4 : }
839 :
840 :
841 0 : void OComboBoxModel::refreshInternalEntryList()
842 : {
843 : DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::refreshInternalEntryList: invalid call!" );
844 :
845 0 : if ( !hasExternalListSource( )
846 0 : && ( m_eListSourceType != ListSourceType_VALUELIST )
847 0 : && ( m_xCursor.is() )
848 : )
849 : {
850 0 : loadData( true );
851 : }
852 0 : }
853 :
854 :
855 2 : void SAL_CALL OComboBoxModel::disposing( const EventObject& _rSource ) throw ( RuntimeException, std::exception )
856 : {
857 2 : if ( !OEntryListHelper::handleDisposing( _rSource ) )
858 2 : OBoundControlModel::disposing( _rSource );
859 2 : }
860 :
861 :
862 : //= OComboBoxControl
863 :
864 22 : OComboBoxControl::OComboBoxControl(const Reference<XComponentContext>& _rxContext)
865 22 : :OBoundControl(_rxContext, VCL_CONTROL_COMBOBOX)
866 : {
867 22 : }
868 :
869 :
870 1 : StringSequence SAL_CALL OComboBoxControl::getSupportedServiceNames() throw(RuntimeException, std::exception)
871 : {
872 1 : StringSequence aSupported = OBoundControl::getSupportedServiceNames();
873 1 : aSupported.realloc(aSupported.getLength() + 2);
874 :
875 1 : OUString* pArray = aSupported.getArray();
876 1 : pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_COMBOBOX;
877 1 : pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_COMBOBOX;
878 1 : return aSupported;
879 : }
880 :
881 : }
882 :
883 : extern "C" SAL_DLLPUBLIC_EXPORT ::com::sun::star::uno::XInterface* SAL_CALL
884 30 : com_sun_star_form_OComboBoxModel_get_implementation(::com::sun::star::uno::XComponentContext* component,
885 : ::com::sun::star::uno::Sequence<css::uno::Any> const &)
886 : {
887 30 : return cppu::acquire(new frm::OComboBoxModel(component));
888 : }
889 :
890 : extern "C" SAL_DLLPUBLIC_EXPORT ::com::sun::star::uno::XInterface* SAL_CALL
891 22 : com_sun_star_form_OComboBoxControl_get_implementation(::com::sun::star::uno::XComponentContext* component,
892 : ::com::sun::star::uno::Sequence<css::uno::Any> const &)
893 : {
894 22 : return cppu::acquire(new frm::OComboBoxControl(component));
895 : }
896 :
897 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|