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 //------------------------------------------------------------------ 83 InterfaceRef SAL_CALL OComboBoxModel_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory) 84 { 85 return (*new OComboBoxModel(_rxFactory)); 86 } 87 88 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------ 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 //------------------------------------------------------------------ 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 //------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 191 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 309 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 //------------------------------------------------------------------------------ 322 void OComboBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const 323 { 324 OBoundControlModel::describeAggregateProperties( _rAggregateProps ); 325 326 // superseded properties: 327 RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST ); 328 } 329 330 //------------------------------------------------------------------------------ 331 ::rtl::OUString SAL_CALL OComboBoxModel::getServiceName() 332 { 333 return FRM_COMPONENT_COMBOBOX; // old (non-sun) name for compatibility ! 334 } 335 336 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 724 void OComboBoxModel::resetNoBroadcast() 725 { 726 OBoundControlModel::resetNoBroadcast(); 727 m_aLastKnownValue.clear(); 728 } 729 730 //----------------------------------------------------------------------------- 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 //------------------------------------------------------------------------------ 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 //------------------------------------------------------------------------------ 832 Any OComboBoxModel::getDefaultForReset() const 833 { 834 return makeAny( m_aDefaultText ); 835 } 836 837 //-------------------------------------------------------------------- 838 void OComboBoxModel::stringItemListChanged( ControlModelLock& /*_rInstanceLock*/ ) 839 { 840 if ( m_xAggregateSet.is() ) 841 m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, makeAny( getStringItemList() ) ); 842 } 843 844 //-------------------------------------------------------------------- 845 void OComboBoxModel::connectedExternalListSource( ) 846 { 847 // TODO? 848 } 849 850 //-------------------------------------------------------------------- 851 void OComboBoxModel::disconnectedExternalListSource( ) 852 { 853 // TODO? 854 } 855 856 //-------------------------------------------------------------------- 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 //-------------------------------------------------------------------- 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 //------------------------------------------------------------------ 882 InterfaceRef SAL_CALL OComboBoxControl_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory) 883 { 884 return *(new OComboBoxControl(_rxFactory)); 885 } 886 887 //------------------------------------------------------------------------------ 888 OComboBoxControl::OComboBoxControl(const Reference<XMultiServiceFactory>& _rxFactory) 889 :OBoundControl(_rxFactory, VCL_CONTROL_COMBOBOX) 890 { 891 } 892 893 //------------------------------------------------------------------------------ 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