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