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