xref: /trunk/main/forms/source/component/ComboBox.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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