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