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