1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 21 #include <fmcontrolbordermanager.hxx> 22 #include <fmcontrollayout.hxx> 23 #include <formcontroller.hxx> 24 #include <formfeaturedispatcher.hxx> 25 #include <fmdocumentclassification.hxx> 26 #include <formcontrolling.hxx> 27 #include <fmprop.hxx> 28 #include <svx/dialmgr.hxx> 29 #include <svx/strings.hrc> 30 #include <fmservs.hxx> 31 #include <svx/fmtools.hxx> 32 #include <fmurl.hxx> 33 34 #include <com/sun/star/awt/FocusChangeReason.hpp> 35 #include <com/sun/star/awt/XCheckBox.hpp> 36 #include <com/sun/star/awt/XComboBox.hpp> 37 #include <com/sun/star/awt/XListBox.hpp> 38 #include <com/sun/star/awt/XVclWindowPeer.hpp> 39 #include <com/sun/star/awt/TabController.hpp> 40 #include <com/sun/star/beans/PropertyAttribute.hpp> 41 #include <com/sun/star/container/XIdentifierReplace.hpp> 42 #include <com/sun/star/form/TabulatorCycle.hpp> 43 #include <com/sun/star/form/validation/XValidatableFormComponent.hpp> 44 #include <com/sun/star/form/XBoundComponent.hpp> 45 #include <com/sun/star/form/XBoundControl.hpp> 46 #include <com/sun/star/form/XGridControl.hpp> 47 #include <com/sun/star/form/XLoadable.hpp> 48 #include <com/sun/star/form/XReset.hpp> 49 #include <com/sun/star/form/control/FilterControl.hpp> 50 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> 51 #include <com/sun/star/lang/NoSupportException.hpp> 52 #include <com/sun/star/sdb/ParametersRequest.hpp> 53 #include <com/sun/star/sdb/RowChangeAction.hpp> 54 #include <com/sun/star/sdb/SQLFilterOperator.hpp> 55 #include <com/sun/star/sdb/XInteractionSupplyParameters.hpp> 56 #include <com/sun/star/sdbc/ColumnValue.hpp> 57 #include <com/sun/star/sdbc/DataType.hpp> 58 #include <com/sun/star/task/InteractionHandler.hpp> 59 #include <com/sun/star/util/XURLTransformer.hpp> 60 #include <com/sun/star/form/runtime/FormOperations.hpp> 61 #include <com/sun/star/form/runtime/FormFeature.hpp> 62 #include <com/sun/star/container/XContainer.hpp> 63 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp> 64 #include <com/sun/star/util/NumberFormatter.hpp> 65 #include <com/sun/star/sdb/SQLContext.hpp> 66 #include <com/sun/star/sdb/XColumn.hpp> 67 68 #include <comphelper/enumhelper.hxx> 69 #include <comphelper/interaction.hxx> 70 #include <comphelper/processfactory.hxx> 71 #include <comphelper/property.hxx> 72 #include <comphelper/sequence.hxx> 73 #include <comphelper/flagguard.hxx> 74 #include <comphelper/types.hxx> 75 #include <cppuhelper/queryinterface.hxx> 76 #include <cppuhelper/supportsservice.hxx> 77 #include <cppuhelper/typeprovider.hxx> 78 #include <connectivity/IParseContext.hxx> 79 #include <connectivity/dbtools.hxx> 80 #include <connectivity/sqlparse.hxx> 81 #include <toolkit/controls/unocontrol.hxx> 82 #include <toolkit/helper/vclunohelper.hxx> 83 #include <tools/debug.hxx> 84 #include <tools/diagnose_ex.h> 85 #include <vcl/svapp.hxx> 86 #include <vcl/settings.hxx> 87 #include <vcl/window.hxx> 88 #include <osl/mutex.hxx> 89 #include <sal/log.hxx> 90 91 #include <algorithm> 92 #include <iterator> 93 94 #include <o3tl/functional.hxx> 95 96 using namespace ::com::sun::star; 97 using namespace ::comphelper; 98 using namespace ::connectivity; 99 using namespace ::dbtools; 100 101 102 css::uno::Reference< css::uno::XInterface > 103 FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB ) 104 { 105 return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) ); 106 } 107 108 namespace svxform 109 { 110 111 using ::com::sun::star::sdb::XColumn; 112 using ::com::sun::star::awt::XControl; 113 using ::com::sun::star::awt::TabController; 114 using ::com::sun::star::awt::XToolkit; 115 using ::com::sun::star::awt::XWindowPeer; 116 using ::com::sun::star::form::XGrid; 117 using ::com::sun::star::beans::XPropertySet; 118 using ::com::sun::star::uno::UNO_SET_THROW; 119 using ::com::sun::star::uno::UNO_QUERY_THROW; 120 using ::com::sun::star::container::XIndexAccess; 121 using ::com::sun::star::uno::Exception; 122 using ::com::sun::star::uno::XInterface; 123 using ::com::sun::star::uno::UNO_QUERY; 124 using ::com::sun::star::uno::Sequence; 125 using ::com::sun::star::uno::Reference; 126 using ::com::sun::star::beans::XPropertySetInfo; 127 using ::com::sun::star::beans::PropertyValue; 128 using ::com::sun::star::uno::RuntimeException; 129 using ::com::sun::star::lang::IndexOutOfBoundsException; 130 using ::com::sun::star::sdb::XInteractionSupplyParameters; 131 using ::com::sun::star::awt::XTextComponent; 132 using ::com::sun::star::awt::XTextListener; 133 using ::com::sun::star::uno::Any; 134 using ::com::sun::star::frame::XDispatch; 135 using ::com::sun::star::lang::XMultiServiceFactory; 136 using ::com::sun::star::uno::Type; 137 using ::com::sun::star::lang::IllegalArgumentException; 138 using ::com::sun::star::sdbc::XConnection; 139 using ::com::sun::star::sdbc::XRowSet; 140 using ::com::sun::star::sdbc::XDatabaseMetaData; 141 using ::com::sun::star::util::XNumberFormatsSupplier; 142 using ::com::sun::star::util::NumberFormatter; 143 using ::com::sun::star::util::XNumberFormatter; 144 using ::com::sun::star::sdbcx::XColumnsSupplier; 145 using ::com::sun::star::container::XNameAccess; 146 using ::com::sun::star::lang::EventObject; 147 using ::com::sun::star::beans::Property; 148 using ::com::sun::star::container::XEnumeration; 149 using ::com::sun::star::form::XFormComponent; 150 using ::com::sun::star::form::runtime::XFormOperations; 151 using ::com::sun::star::form::runtime::FilterEvent; 152 using ::com::sun::star::form::runtime::XFilterControllerListener; 153 using ::com::sun::star::awt::XControlContainer; 154 using ::com::sun::star::container::XIdentifierReplace; 155 using ::com::sun::star::lang::WrappedTargetException; 156 using ::com::sun::star::form::XFormControllerListener; 157 using ::com::sun::star::awt::XWindow; 158 using ::com::sun::star::sdbc::XResultSet; 159 using ::com::sun::star::awt::XControlModel; 160 using ::com::sun::star::awt::XTabControllerModel; 161 using ::com::sun::star::beans::PropertyChangeEvent; 162 using ::com::sun::star::form::validation::XValidatableFormComponent; 163 using ::com::sun::star::form::XLoadable; 164 using ::com::sun::star::form::XBoundControl; 165 using ::com::sun::star::beans::XPropertyChangeListener; 166 using ::com::sun::star::awt::TextEvent; 167 using ::com::sun::star::form::XBoundComponent; 168 using ::com::sun::star::awt::XCheckBox; 169 using ::com::sun::star::awt::XComboBox; 170 using ::com::sun::star::awt::XListBox; 171 using ::com::sun::star::awt::ItemEvent; 172 using ::com::sun::star::util::XModifyListener; 173 using ::com::sun::star::form::XReset; 174 using ::com::sun::star::frame::XDispatchProviderInterception; 175 using ::com::sun::star::form::XGridControl; 176 using ::com::sun::star::awt::XVclWindowPeer; 177 using ::com::sun::star::form::validation::XValidator; 178 using ::com::sun::star::awt::FocusEvent; 179 using ::com::sun::star::sdb::SQLContext; 180 using ::com::sun::star::container::XChild; 181 using ::com::sun::star::form::TabulatorCycle_RECORDS; 182 using ::com::sun::star::container::ContainerEvent; 183 using ::com::sun::star::lang::DisposedException; 184 using ::com::sun::star::lang::Locale; 185 using ::com::sun::star::lang::NoSupportException; 186 using ::com::sun::star::sdb::RowChangeEvent; 187 using ::com::sun::star::frame::XStatusListener; 188 using ::com::sun::star::frame::XDispatchProviderInterceptor; 189 using ::com::sun::star::sdb::SQLErrorEvent; 190 using ::com::sun::star::form::DatabaseParameterEvent; 191 using ::com::sun::star::sdb::ParametersRequest; 192 using ::com::sun::star::task::XInteractionRequest; 193 using ::com::sun::star::util::URL; 194 using ::com::sun::star::frame::FeatureStateEvent; 195 using ::com::sun::star::form::runtime::XFormControllerContext; 196 using ::com::sun::star::task::InteractionHandler; 197 using ::com::sun::star::task::XInteractionHandler; 198 using ::com::sun::star::form::runtime::FormOperations; 199 using ::com::sun::star::container::XContainer; 200 using ::com::sun::star::sdbc::SQLWarning; 201 202 namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue; 203 namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute; 204 namespace FocusChangeReason = ::com::sun::star::awt::FocusChangeReason; 205 namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction; 206 namespace FormFeature = ::com::sun::star::form::runtime::FormFeature; 207 208 struct ColumnInfo 209 { 210 // information about the column itself 211 Reference< XColumn > xColumn; 212 sal_Int32 nNullable; 213 bool bAutoIncrement; 214 bool bReadOnly; 215 OUString sName; 216 217 // information about the control(s) bound to this column 218 219 /// the first control which is bound to the given column, and which requires input 220 Reference< XControl > xFirstControlWithInputRequired; 221 /** the first grid control which contains a column which is bound to the given database column, and requires 222 input 223 */ 224 Reference< XGrid > xFirstGridWithInputRequiredColumn; 225 /** if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position 226 of the grid column which is actually bound 227 */ 228 sal_Int32 nRequiredGridColumn; 229 230 ColumnInfo() 231 :xColumn() 232 ,nNullable( ColumnValue::NULLABLE_UNKNOWN ) 233 ,bAutoIncrement( false ) 234 ,bReadOnly( false ) 235 ,sName() 236 ,xFirstControlWithInputRequired() 237 ,xFirstGridWithInputRequiredColumn() 238 ,nRequiredGridColumn( -1 ) 239 { 240 } 241 }; 242 243 class ColumnInfoCache 244 { 245 public: 246 explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier ); 247 248 size_t getColumnCount() const { return m_aColumns.size(); } 249 const ColumnInfo& getColumnInfo( size_t _pos ); 250 251 bool controlsInitialized() const { return m_bControlsInitialized; } 252 void initializeControls( const Sequence< Reference< XControl > >& _rControls ); 253 void deinitializeControls(); 254 255 private: 256 typedef ::std::vector< ColumnInfo > ColumnInfos; 257 ColumnInfos m_aColumns; 258 bool m_bControlsInitialized; 259 }; 260 261 262 ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier ) 263 :m_aColumns() 264 ,m_bControlsInitialized( false ) 265 { 266 try 267 { 268 m_aColumns.clear(); 269 270 Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW ); 271 sal_Int32 nColumnCount = xColumns->getCount(); 272 m_aColumns.reserve( nColumnCount ); 273 274 Reference< XPropertySet > xColumnProps; 275 for ( sal_Int32 i = 0; i < nColumnCount; ++i ) 276 { 277 ColumnInfo aColInfo; 278 aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW ); 279 280 xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW ); 281 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable ); 282 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement ); 283 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName ); 284 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly ); 285 286 m_aColumns.push_back( aColInfo ); 287 } 288 } 289 catch( const Exception& ) 290 { 291 DBG_UNHANDLED_EXCEPTION("svx"); 292 } 293 } 294 295 296 namespace 297 { 298 bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField ) 299 { 300 Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY ); 301 return ( xNormBoundField == _rxNormDBField ); 302 } 303 304 bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel ) 305 { 306 bool bInputRequired = false; 307 OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired ); 308 return bInputRequired; 309 } 310 311 void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo ) 312 { 313 _rColInfo.xFirstControlWithInputRequired.clear(); 314 _rColInfo.xFirstGridWithInputRequiredColumn.clear(); 315 _rColInfo.nRequiredGridColumn = -1; 316 } 317 } 318 319 320 void ColumnInfoCache::deinitializeControls() 321 { 322 for (auto& rCol : m_aColumns) 323 { 324 lcl_resetColumnControlInfo( rCol ); 325 } 326 m_bControlsInitialized = false; 327 } 328 329 330 void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls ) 331 { 332 try 333 { 334 // for every of our known columns, find the controls which are bound to this column 335 for (auto& rCol : m_aColumns) 336 { 337 OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is() 338 && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" ); 339 340 lcl_resetColumnControlInfo( rCol ); 341 342 Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW ); 343 344 const Reference< XControl >* pControl( _rControls.getConstArray() ); 345 const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() ); 346 for ( ; pControl != pControlEnd; ++pControl ) 347 { 348 if ( !pControl->is() ) 349 continue; 350 351 Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW ); 352 Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW ); 353 354 // special handling for grid controls 355 Reference< XGrid > xGrid( *pControl, UNO_QUERY ); 356 if ( xGrid.is() ) 357 { 358 Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW ); 359 sal_Int32 gridColCount = xGridColAccess->getCount(); 360 sal_Int32 gridCol = 0; 361 for ( gridCol = 0; gridCol < gridColCount; ++gridCol ) 362 { 363 Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW ); 364 365 if ( !lcl_isBoundTo( xGridColumnModel, xNormColumn ) 366 || !lcl_isInputRequired( xGridColumnModel ) 367 ) 368 continue; // with next grid column 369 370 break; 371 } 372 373 if ( gridCol < gridColCount ) 374 { 375 // found a grid column which is bound to the given 376 rCol.xFirstGridWithInputRequiredColumn = xGrid; 377 rCol.nRequiredGridColumn = gridCol; 378 break; 379 } 380 381 continue; // with next control 382 } 383 384 if ( !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD ) 385 || !lcl_isBoundTo( xModel, xNormColumn ) 386 || !lcl_isInputRequired( xModel ) 387 ) 388 continue; // with next control 389 390 break; 391 } 392 393 if ( pControl == pControlEnd ) 394 // did not find a control which is bound to this particular column, and for which the input is required 395 continue; // with next DB column 396 397 rCol.xFirstControlWithInputRequired = *pControl; 398 } 399 } 400 catch( const Exception& ) 401 { 402 DBG_UNHANDLED_EXCEPTION("svx"); 403 } 404 405 m_bControlsInitialized = true; 406 } 407 408 409 const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos ) 410 { 411 if ( _pos >= m_aColumns.size() ) 412 throw IndexOutOfBoundsException(); 413 414 return m_aColumns[ _pos ]; 415 } 416 417 class OParameterContinuation : public OInteraction< XInteractionSupplyParameters > 418 { 419 Sequence< PropertyValue > m_aValues; 420 421 public: 422 OParameterContinuation() { } 423 424 const Sequence< PropertyValue >& getValues() const { return m_aValues; } 425 426 // XInteractionSupplyParameters 427 virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override; 428 }; 429 430 431 void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues ) 432 { 433 m_aValues = _rValues; 434 } 435 436 437 // FmXAutoControl 438 439 struct FmFieldInfo 440 { 441 OUString aFieldName; 442 Reference< XPropertySet > xField; 443 Reference< XTextComponent > xText; 444 445 FmFieldInfo(const Reference< XPropertySet >& _xField, const Reference< XTextComponent >& _xText) 446 :xField(_xField) 447 ,xText(_xText) 448 {xField->getPropertyValue(FM_PROP_NAME) >>= aFieldName;} 449 }; 450 451 class FmXAutoControl: public UnoControl 452 453 { 454 public: 455 FmXAutoControl() :UnoControl() 456 { 457 } 458 459 virtual OUString GetComponentServiceName() override {return OUString("Edit");} 460 virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) override; 461 462 protected: 463 virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override; 464 }; 465 466 467 void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) 468 { 469 UnoControl::createPeer( rxToolkit, rParentPeer ); 470 471 Reference< XTextComponent > xText(getPeer() , UNO_QUERY); 472 if (xText.is()) 473 { 474 xText->setText(SvxResId(RID_STR_AUTOFIELD)); 475 xText->setEditable(false); 476 } 477 } 478 479 480 void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) 481 { 482 // these properties are ignored 483 if (rPropName == FM_PROP_TEXT) 484 return; 485 486 UnoControl::ImplSetPeerProperty( rPropName, rVal ); 487 } 488 489 490 IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void ) 491 { 492 activateTabOrder(); 493 } 494 495 496 struct UpdateAllListeners 497 { 498 bool operator()( const Reference< XDispatch >& _rxDispatcher ) const 499 { 500 static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners(); 501 // the return is a dummy only so we can use this struct in a lambda expression 502 return true; 503 } 504 }; 505 506 IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void ) 507 { 508 ::osl::MutexGuard aGuard( m_aMutex ); 509 for (const auto& rFeature : m_aInvalidFeatures) 510 { 511 DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature ); 512 if ( aDispatcherPos != m_aFeatureDispatchers.end() ) 513 { 514 // TODO: for the real and actual listener notifications, we should release 515 // our mutex 516 UpdateAllListeners( )( aDispatcherPos->second ); 517 } 518 } 519 } 520 521 FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB ) 522 :FormController_BASE( m_aMutex ) 523 ,OPropertySetHelper( FormController_BASE::rBHelper ) 524 ,OSQLParserClient( _rxORB ) 525 ,m_xComponentContext( _rxORB ) 526 ,m_aActivateListeners(m_aMutex) 527 ,m_aModifyListeners(m_aMutex) 528 ,m_aErrorListeners(m_aMutex) 529 ,m_aDeleteListeners(m_aMutex) 530 ,m_aRowSetApproveListeners(m_aMutex) 531 ,m_aParameterListeners(m_aMutex) 532 ,m_aFilterListeners(m_aMutex) 533 ,m_xFormOperations() 534 ,m_aMode( OUString( "DataMode" ) ) 535 ,m_aLoadEvent( LINK( this, FormController, OnLoad ) ) 536 ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) ) 537 ,m_aActivationEvent( LINK( this, FormController, OnActivated ) ) 538 ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) ) 539 ,m_nCurrentFilterPosition(-1) 540 ,m_bCurrentRecordModified(false) 541 ,m_bCurrentRecordNew(false) 542 ,m_bLocked(false) 543 ,m_bDBConnection(false) 544 ,m_bCycle(false) 545 ,m_bCanInsert(false) 546 ,m_bCanUpdate(false) 547 ,m_bCommitLock(false) 548 ,m_bModified(false) 549 ,m_bControlsSorted(false) 550 ,m_bFiltering(false) 551 ,m_bAttachEvents(true) 552 ,m_bDetachEvents(true) 553 ,m_bAttemptedHandlerCreation( false ) 554 ,m_bSuspendFilterTextListening( false ) 555 { 556 557 osl_atomic_increment(&m_refCount); 558 { 559 m_xTabController = TabController::create( m_xComponentContext ); 560 m_xAggregate.set( m_xTabController, UNO_QUERY_THROW ); 561 m_xAggregate->setDelegator( *this ); 562 } 563 osl_atomic_decrement(&m_refCount); 564 565 m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST ); 566 m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) ); 567 568 m_aFeatureInvalidationTimer.SetTimeout( 200 ); 569 m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) ); 570 } 571 572 573 FormController::~FormController() 574 { 575 { 576 ::osl::MutexGuard aGuard( m_aMutex ); 577 578 m_aLoadEvent.CancelPendingCall(); 579 m_aToggleEvent.CancelPendingCall(); 580 m_aActivationEvent.CancelPendingCall(); 581 m_aDeactivationEvent.CancelPendingCall(); 582 583 if ( m_aTabActivationIdle.IsActive() ) 584 m_aTabActivationIdle.Stop(); 585 } 586 587 if ( m_aFeatureInvalidationTimer.IsActive() ) 588 m_aFeatureInvalidationTimer.Stop(); 589 590 disposeAllFeaturesAndDispatchers(); 591 592 if ( m_xFormOperations.is() ) 593 m_xFormOperations->dispose(); 594 m_xFormOperations.clear(); 595 596 // release of aggregation 597 if ( m_xAggregate.is() ) 598 { 599 m_xAggregate->setDelegator( nullptr ); 600 m_xAggregate.clear(); 601 } 602 } 603 604 605 void SAL_CALL FormController::acquire() throw () 606 { 607 FormController_BASE::acquire(); 608 } 609 610 611 void SAL_CALL FormController::release() throw () 612 { 613 FormController_BASE::release(); 614 } 615 616 617 Any SAL_CALL FormController::queryInterface( const Type& _rType ) 618 { 619 Any aRet = FormController_BASE::queryInterface( _rType ); 620 if ( !aRet.hasValue() ) 621 aRet = OPropertySetHelper::queryInterface( _rType ); 622 if ( !aRet.hasValue() ) 623 aRet = m_xAggregate->queryAggregation( _rType ); 624 return aRet; 625 } 626 627 628 Sequence< sal_Int8 > SAL_CALL FormController::getImplementationId() 629 { 630 return css::uno::Sequence<sal_Int8>(); 631 } 632 633 Sequence< Type > SAL_CALL FormController::getTypes( ) 634 { 635 return comphelper::concatSequences( 636 FormController_BASE::getTypes(), 637 ::cppu::OPropertySetHelper::getTypes() 638 ); 639 } 640 641 // XServiceInfo 642 sal_Bool SAL_CALL FormController::supportsService(const OUString& ServiceName) 643 { 644 return cppu::supportsService(this, ServiceName); 645 } 646 647 OUString SAL_CALL FormController::getImplementationName() 648 { 649 return OUString("org.openoffice.comp.svx.FormController"); 650 } 651 652 Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames() 653 { 654 // service names which are supported only, but cannot be used to created an 655 // instance at a service factory 656 Sequence<OUString> aNonCreatableServiceNames { "com.sun.star.form.FormControllerDispatcher" }; 657 658 // services which can be used to created an instance at a service factory 659 Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() ); 660 return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames ); 661 } 662 663 664 sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/) 665 { 666 return true; 667 } 668 669 670 void SAL_CALL FormController::resetted(const EventObject& rEvent) 671 { 672 ::osl::MutexGuard aGuard(m_aMutex); 673 if (getCurrentControl().is() && (getCurrentControl()->getModel() == rEvent.Source)) 674 m_bModified = false; 675 } 676 677 678 Sequence< OUString> const & FormController::getSupportedServiceNames_Static() 679 { 680 static Sequence< OUString> const aServices 681 { 682 "com.sun.star.form.runtime.FormController", 683 "com.sun.star.awt.control.TabController" 684 }; 685 return aServices; 686 } 687 688 689 namespace 690 { 691 struct ResetComponentText 692 { 693 void operator()( const Reference< XTextComponent >& _rxText ) 694 { 695 _rxText->setText( OUString() ); 696 } 697 }; 698 699 struct RemoveComponentTextListener 700 { 701 explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener ) 702 :m_xListener( _rxListener ) 703 { 704 } 705 706 void operator()( const Reference< XTextComponent >& _rxText ) 707 { 708 _rxText->removeTextListener( m_xListener ); 709 } 710 711 private: 712 Reference< XTextListener > m_xListener; 713 }; 714 } 715 716 717 void FormController::impl_setTextOnAllFilter_throw() 718 { 719 m_bSuspendFilterTextListening = true; 720 ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening ); 721 722 // reset the text for all controls 723 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() ); 724 725 if ( m_aFilterRows.empty() ) 726 // nothing to do anymore 727 return; 728 729 if ( m_nCurrentFilterPosition < 0 ) 730 return; 731 732 // set the text for all filters 733 OSL_ENSURE( m_aFilterRows.size() > static_cast<size_t>(m_nCurrentFilterPosition), 734 "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" ); 735 736 if ( static_cast<size_t>(m_nCurrentFilterPosition) < m_aFilterRows.size() ) 737 { 738 FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ]; 739 for (const auto& rEntry : rRow) 740 { 741 rEntry.first->setText( rEntry.second ); 742 } 743 } 744 } 745 // OPropertySetHelper 746 747 sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any & /*rOldValue*/, 748 sal_Int32 /*nHandle*/, const Any& /*rValue*/ ) 749 { 750 return false; 751 } 752 753 754 void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ ) 755 { 756 } 757 758 759 void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const 760 { 761 switch (nHandle) 762 { 763 case FM_ATTR_FILTER: 764 { 765 OUStringBuffer aFilter; 766 Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY))); 767 if (xConnection.is()) 768 { 769 Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) ); 770 Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext); 771 xFormatter->attachNumberFormatsSupplier(xFormatSupplier); 772 773 // now add the filter rows 774 try 775 { 776 for (const FmFilterRow& rRow : m_aFilterRows) 777 { 778 if ( rRow.empty() ) 779 continue; 780 781 OUStringBuffer aRowFilter; 782 for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition ) 783 { 784 // get the field of the controls map 785 Reference< XControl > xControl( condition->first, UNO_QUERY_THROW ); 786 Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW ); 787 Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW ); 788 789 OUString sFilterValue( condition->second ); 790 791 OUString sErrorMsg, sCriteria; 792 const std::shared_ptr< OSQLParseNode > pParseNode = 793 predicateTree( sErrorMsg, sFilterValue, xFormatter, xField ); 794 OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" ); 795 if ( pParseNode != nullptr ) 796 { 797 // don't use a parse context here, we need it unlocalized 798 pParseNode->parseNodeToStr( sCriteria, xConnection ); 799 if ( condition != rRow.begin() ) 800 aRowFilter.append( " AND " ); 801 aRowFilter.append( sCriteria ); 802 } 803 } 804 if ( !aRowFilter.isEmpty() ) 805 { 806 if ( !aFilter.isEmpty() ) 807 aFilter.append( " OR " ); 808 809 aFilter.append( "( " ); 810 aFilter.append( aRowFilter.makeStringAndClear() ); 811 aFilter.append( " )" ); 812 } 813 } 814 } 815 catch( const Exception& ) 816 { 817 DBG_UNHANDLED_EXCEPTION("svx"); 818 aFilter.setLength(0); 819 } 820 } 821 rValue <<= aFilter.makeStringAndClear(); 822 } 823 break; 824 825 case FM_ATTR_FORM_OPERATIONS: 826 rValue <<= m_xFormOperations; 827 break; 828 } 829 } 830 831 832 Reference< XPropertySetInfo > FormController::getPropertySetInfo() 833 { 834 static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); 835 return xInfo; 836 } 837 838 839 void FormController::fillProperties( 840 Sequence< Property >& /* [out] */ _rProps, 841 Sequence< Property >& /* [out] */ /*_rAggregateProps*/ 842 ) const 843 { 844 _rProps.realloc(2); 845 sal_Int32 nPos = 0; 846 Property* pDesc = _rProps.getArray(); 847 848 pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER, 849 cppu::UnoType<OUString>::get(), 850 PropertyAttribute::READONLY); 851 pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS, 852 cppu::UnoType<XFormOperations>::get(), 853 PropertyAttribute::READONLY); 854 } 855 856 857 ::cppu::IPropertyArrayHelper& FormController::getInfoHelper() 858 { 859 return *getArrayHelper(); 860 } 861 862 // XFilterController 863 864 void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener ) 865 { 866 m_aFilterListeners.addInterface( Listener ); 867 } 868 869 870 void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener ) 871 { 872 m_aFilterListeners.removeInterface( Listener ); 873 } 874 875 876 ::sal_Int32 SAL_CALL FormController::getFilterComponents() 877 { 878 ::osl::MutexGuard aGuard( m_aMutex ); 879 impl_checkDisposed_throw(); 880 881 return m_aFilterComponents.size(); 882 } 883 884 885 ::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms() 886 { 887 ::osl::MutexGuard aGuard( m_aMutex ); 888 impl_checkDisposed_throw(); 889 890 return m_aFilterRows.size(); 891 } 892 893 894 void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression ) 895 { 896 ::osl::MutexGuard aGuard( m_aMutex ); 897 impl_checkDisposed_throw(); 898 899 if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) ) 900 throw IndexOutOfBoundsException( OUString(), *this ); 901 902 Reference< XTextComponent > xText( m_aFilterComponents[ Component ] ); 903 xText->setText( PredicateExpression ); 904 905 FmFilterRow& rFilterRow = m_aFilterRows[ Term ]; 906 if ( !PredicateExpression.isEmpty() ) 907 rFilterRow[ xText ] = PredicateExpression; 908 else 909 rFilterRow.erase( xText ); 910 } 911 912 913 Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component ) 914 { 915 ::osl::MutexGuard aGuard( m_aMutex ); 916 impl_checkDisposed_throw(); 917 918 if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) ) 919 throw IndexOutOfBoundsException( OUString(), *this ); 920 921 return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY ); 922 } 923 924 925 Sequence< Sequence< OUString > > FormController::getPredicateExpressions() 926 { 927 ::osl::MutexGuard aGuard( m_aMutex ); 928 impl_checkDisposed_throw(); 929 930 Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() ); 931 sal_Int32 termIndex = 0; 932 for (const FmFilterRow& rRow : m_aFilterRows) 933 { 934 Sequence< OUString > aConjunction( m_aFilterComponents.size() ); 935 sal_Int32 componentIndex = 0; 936 for (const auto& rComp : m_aFilterComponents) 937 { 938 FmFilterRow::const_iterator predicate = rRow.find( rComp ); 939 if ( predicate != rRow.end() ) 940 aConjunction[ componentIndex ] = predicate->second; 941 ++componentIndex; 942 } 943 944 aExpressions[ termIndex ] = aConjunction; 945 ++termIndex; 946 } 947 948 return aExpressions; 949 } 950 951 952 void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term ) 953 { 954 // SYNCHRONIZED --> 955 ::osl::ClearableMutexGuard aGuard( m_aMutex ); 956 impl_checkDisposed_throw(); 957 958 if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) ) 959 throw IndexOutOfBoundsException( OUString(), *this ); 960 961 // if the to-be-deleted row is our current row, we need to shift 962 if ( Term == m_nCurrentFilterPosition ) 963 { 964 if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) ) 965 ++m_nCurrentFilterPosition; 966 else 967 --m_nCurrentFilterPosition; 968 } 969 970 FmFilterRows::iterator pos = m_aFilterRows.begin() + Term; 971 m_aFilterRows.erase( pos ); 972 973 // adjust m_nCurrentFilterPosition if the removed row preceded it 974 if ( Term < m_nCurrentFilterPosition ) 975 --m_nCurrentFilterPosition; 976 977 SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ), 978 "svx.form", "FormController::removeDisjunctiveTerm: inconsistency!" ); 979 980 // update the texts in the filter controls 981 impl_setTextOnAllFilter_throw(); 982 983 FilterEvent aEvent; 984 aEvent.Source = *this; 985 aEvent.DisjunctiveTerm = Term; 986 aGuard.clear(); 987 // <-- SYNCHRONIZED 988 989 m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent ); 990 } 991 992 993 void SAL_CALL FormController::appendEmptyDisjunctiveTerm() 994 { 995 // SYNCHRONIZED --> 996 ::osl::ClearableMutexGuard aGuard( m_aMutex ); 997 impl_checkDisposed_throw(); 998 999 impl_appendEmptyFilterRow( aGuard ); 1000 // <-- SYNCHRONIZED 1001 } 1002 1003 1004 ::sal_Int32 SAL_CALL FormController::getActiveTerm() 1005 { 1006 ::osl::MutexGuard aGuard( m_aMutex ); 1007 impl_checkDisposed_throw(); 1008 1009 return m_nCurrentFilterPosition; 1010 } 1011 1012 1013 void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm ) 1014 { 1015 ::osl::MutexGuard aGuard( m_aMutex ); 1016 impl_checkDisposed_throw(); 1017 1018 if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) ) 1019 throw IndexOutOfBoundsException( OUString(), *this ); 1020 1021 if ( ActiveTerm == getActiveTerm() ) 1022 return; 1023 1024 m_nCurrentFilterPosition = ActiveTerm; 1025 impl_setTextOnAllFilter_throw(); 1026 } 1027 1028 // XElementAccess 1029 1030 sal_Bool SAL_CALL FormController::hasElements() 1031 { 1032 ::osl::MutexGuard aGuard( m_aMutex ); 1033 return !m_aChildren.empty(); 1034 } 1035 1036 1037 Type SAL_CALL FormController::getElementType() 1038 { 1039 return cppu::UnoType<XFormController>::get(); 1040 1041 } 1042 1043 // XEnumerationAccess 1044 1045 Reference< XEnumeration > SAL_CALL FormController::createEnumeration() 1046 { 1047 ::osl::MutexGuard aGuard( m_aMutex ); 1048 return new ::comphelper::OEnumerationByIndex(this); 1049 } 1050 1051 // XIndexAccess 1052 1053 sal_Int32 SAL_CALL FormController::getCount() 1054 { 1055 ::osl::MutexGuard aGuard( m_aMutex ); 1056 return m_aChildren.size(); 1057 } 1058 1059 1060 Any SAL_CALL FormController::getByIndex(sal_Int32 Index) 1061 { 1062 ::osl::MutexGuard aGuard( m_aMutex ); 1063 if (Index < 0 || 1064 Index >= static_cast<sal_Int32>(m_aChildren.size())) 1065 throw IndexOutOfBoundsException(); 1066 1067 return makeAny( m_aChildren[ Index ] ); 1068 } 1069 1070 // EventListener 1071 1072 void SAL_CALL FormController::disposing(const EventObject& e) 1073 { 1074 // has the container been disposed 1075 ::osl::MutexGuard aGuard( m_aMutex ); 1076 Reference< XControlContainer > xContainer(e.Source, UNO_QUERY); 1077 if (xContainer.is()) 1078 { 1079 setContainer(Reference< XControlContainer > ()); 1080 } 1081 else 1082 { 1083 // has a control been disposed 1084 Reference< XControl > xControl(e.Source, UNO_QUERY); 1085 if (xControl.is()) 1086 { 1087 if (getContainer().is()) 1088 removeControl(xControl); 1089 } 1090 } 1091 } 1092 1093 // OComponentHelper 1094 1095 void FormController::disposeAllFeaturesAndDispatchers() 1096 { 1097 for (auto& rDispatcher : m_aFeatureDispatchers) 1098 { 1099 try 1100 { 1101 ::comphelper::disposeComponent( rDispatcher.second ); 1102 } 1103 catch( const Exception& ) 1104 { 1105 DBG_UNHANDLED_EXCEPTION("svx"); 1106 } 1107 } 1108 m_aFeatureDispatchers.clear(); 1109 } 1110 1111 1112 void FormController::disposing() 1113 { 1114 EventObject aEvt( *this ); 1115 1116 // if we're still active, simulate a "deactivated" event 1117 if ( m_xActiveControl.is() ) 1118 m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt ); 1119 1120 // notify all our listeners 1121 m_aActivateListeners.disposeAndClear(aEvt); 1122 m_aModifyListeners.disposeAndClear(aEvt); 1123 m_aErrorListeners.disposeAndClear(aEvt); 1124 m_aDeleteListeners.disposeAndClear(aEvt); 1125 m_aRowSetApproveListeners.disposeAndClear(aEvt); 1126 m_aParameterListeners.disposeAndClear(aEvt); 1127 m_aFilterListeners.disposeAndClear(aEvt); 1128 1129 removeBoundFieldListener(); 1130 stopFiltering(); 1131 1132 m_aControlBorderManager.restoreAll(); 1133 1134 m_aFilterRows.clear(); 1135 1136 ::osl::MutexGuard aGuard( m_aMutex ); 1137 m_xActiveControl = nullptr; 1138 implSetCurrentControl( nullptr ); 1139 1140 // clean up our children 1141 for (const auto& rpChild : m_aChildren) 1142 { 1143 // search the position of the model within the form 1144 Reference< XFormComponent > xForm(rpChild->getModel(), UNO_QUERY); 1145 sal_uInt32 nPos = m_xModelAsIndex->getCount(); 1146 Reference< XFormComponent > xTemp; 1147 for( ; nPos; ) 1148 { 1149 1150 m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp; 1151 if ( xForm.get() == xTemp.get() ) 1152 { 1153 Reference< XInterface > xIfc( rpChild, UNO_QUERY ); 1154 m_xModelAsManager->detach( nPos, xIfc ); 1155 break; 1156 } 1157 } 1158 1159 Reference< XComponent > (rpChild, UNO_QUERY)->dispose(); 1160 } 1161 m_aChildren.clear(); 1162 1163 disposeAllFeaturesAndDispatchers(); 1164 1165 if ( m_xFormOperations.is() ) 1166 m_xFormOperations->dispose(); 1167 m_xFormOperations.clear(); 1168 1169 if (m_bDBConnection) 1170 unload(); 1171 1172 setContainer( nullptr ); 1173 setModel( nullptr ); 1174 setParent( nullptr ); 1175 1176 ::comphelper::disposeComponent( m_xComposer ); 1177 1178 m_bDBConnection = false; 1179 } 1180 1181 1182 namespace 1183 { 1184 bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp ) 1185 { 1186 bool bDoUse = false; 1187 if ( !( _rDynamicColorProp >>= bDoUse ) ) 1188 { 1189 DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm ); 1190 return ControlLayouter::useDynamicBorderColor( eDocType ); 1191 } 1192 return bDoUse; 1193 } 1194 } 1195 1196 1197 void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt) 1198 { 1199 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1200 if ( evt.PropertyName == FM_PROP_BOUNDFIELD ) 1201 { 1202 Reference<XPropertySet> xOldBound; 1203 evt.OldValue >>= xOldBound; 1204 if ( !xOldBound.is() && evt.NewValue.hasValue() ) 1205 { 1206 Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY); 1207 Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false); 1208 if ( xControl.is() ) 1209 { 1210 startControlModifyListening( xControl ); 1211 Reference<XPropertySet> xProp(xControlModel,UNO_QUERY); 1212 if ( xProp.is() ) 1213 xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this); 1214 } 1215 } 1216 } 1217 else 1218 { 1219 bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED); 1220 bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW); 1221 if (bModifiedChanged || bNewChanged) 1222 { 1223 ::osl::MutexGuard aGuard( m_aMutex ); 1224 if (bModifiedChanged) 1225 m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue); 1226 else 1227 m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue); 1228 1229 // toggle the locking 1230 if (m_bLocked != determineLockState()) 1231 { 1232 m_bLocked = !m_bLocked; 1233 setLocks(); 1234 if (isListeningForChanges()) 1235 startListening(); 1236 else 1237 stopListening(); 1238 } 1239 1240 if ( bNewChanged ) 1241 m_aToggleEvent.Call(); 1242 1243 if (!m_bCurrentRecordModified) 1244 m_bModified = false; 1245 } 1246 else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER ) 1247 { 1248 bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue ); 1249 if ( bEnable ) 1250 { 1251 m_aControlBorderManager.enableDynamicBorderColor(); 1252 if ( m_xActiveControl.is() ) 1253 m_aControlBorderManager.focusGained( m_xActiveControl.get() ); 1254 } 1255 else 1256 { 1257 m_aControlBorderManager.disableDynamicBorderColor(); 1258 } 1259 } 1260 } 1261 } 1262 1263 1264 bool FormController::replaceControl( const Reference< XControl >& _rxExistentControl, const Reference< XControl >& _rxNewControl ) 1265 { 1266 bool bSuccess = false; 1267 try 1268 { 1269 Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY ); 1270 DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" ); 1271 if ( xContainer.is() ) 1272 { 1273 // look up the ID of _rxExistentControl 1274 Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() ); 1275 const sal_Int32* pIdentifiers = aIdentifiers.getConstArray(); 1276 const sal_Int32* pIdentifiersEnd = aIdentifiers.getConstArray() + aIdentifiers.getLength(); 1277 for ( ; pIdentifiers != pIdentifiersEnd; ++pIdentifiers ) 1278 { 1279 Reference< XControl > xCheck( xContainer->getByIdentifier( *pIdentifiers ), UNO_QUERY ); 1280 if ( xCheck == _rxExistentControl ) 1281 break; 1282 } 1283 DBG_ASSERT( pIdentifiers != pIdentifiersEnd, "FormController::replaceControl: did not find the control in the container!" ); 1284 if ( pIdentifiers != pIdentifiersEnd ) 1285 { 1286 bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() ); 1287 bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() ); 1288 1289 if ( bReplacedWasActive ) 1290 { 1291 m_xActiveControl = nullptr; 1292 implSetCurrentControl( nullptr ); 1293 } 1294 else if ( bReplacedWasCurrent ) 1295 { 1296 implSetCurrentControl( _rxNewControl ); 1297 } 1298 1299 // carry over the model 1300 _rxNewControl->setModel( _rxExistentControl->getModel() ); 1301 1302 xContainer->replaceByIdentifer( *pIdentifiers, makeAny( _rxNewControl ) ); 1303 bSuccess = true; 1304 1305 if ( bReplacedWasActive ) 1306 { 1307 Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY ); 1308 if ( xControlWindow.is() ) 1309 xControlWindow->setFocus(); 1310 } 1311 } 1312 } 1313 } 1314 catch( const Exception& ) 1315 { 1316 DBG_UNHANDLED_EXCEPTION("svx"); 1317 } 1318 1319 Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl ); 1320 ::comphelper::disposeComponent( xDisposeIt ); 1321 return bSuccess; 1322 } 1323 1324 1325 void FormController::toggleAutoFields(bool bAutoFields) 1326 { 1327 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1328 1329 1330 Sequence< Reference< XControl > > aControlsCopy( m_aControls ); 1331 const Reference< XControl >* pControls = aControlsCopy.getConstArray(); 1332 sal_Int32 nControls = aControlsCopy.getLength(); 1333 1334 if (bAutoFields) 1335 { 1336 // as we don't want new controls to be attached to the scripting environment 1337 // we change attach flags 1338 m_bAttachEvents = false; 1339 for (sal_Int32 i = nControls; i > 0;) 1340 { 1341 Reference< XControl > xControl = pControls[--i]; 1342 if (xControl.is()) 1343 { 1344 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY); 1345 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)) 1346 { 1347 // does the model use a bound field ? 1348 Reference< XPropertySet > xField; 1349 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField; 1350 1351 // is it a autofield? 1352 if ( xField.is() 1353 && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField ) 1354 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) ) 1355 ) 1356 { 1357 replaceControl( xControl, new FmXAutoControl() ); 1358 } 1359 } 1360 } 1361 } 1362 m_bAttachEvents = true; 1363 } 1364 else 1365 { 1366 m_bDetachEvents = false; 1367 for (sal_Int32 i = nControls; i > 0;) 1368 { 1369 Reference< XControl > xControl = pControls[--i]; 1370 if (xControl.is()) 1371 { 1372 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY); 1373 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)) 1374 { 1375 // does the model use a bound field ? 1376 Reference< XPropertySet > xField; 1377 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField; 1378 1379 // is it a autofield? 1380 if ( xField.is() 1381 && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField ) 1382 && ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) ) 1383 ) 1384 { 1385 OUString sServiceName; 1386 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName ); 1387 Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY ); 1388 replaceControl( xControl, xNewControl ); 1389 } 1390 } 1391 } 1392 } 1393 m_bDetachEvents = true; 1394 } 1395 } 1396 1397 1398 IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void) 1399 { 1400 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1401 1402 toggleAutoFields(m_bCurrentRecordNew); 1403 } 1404 1405 // XTextListener 1406 1407 void SAL_CALL FormController::textChanged(const TextEvent& e) 1408 { 1409 // SYNCHRONIZED --> 1410 ::osl::ClearableMutexGuard aGuard( m_aMutex ); 1411 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1412 if ( !m_bFiltering ) 1413 { 1414 impl_onModify(); 1415 return; 1416 } 1417 1418 if ( m_bSuspendFilterTextListening ) 1419 return; 1420 1421 Reference< XTextComponent > xText(e.Source,UNO_QUERY); 1422 OUString aText = xText->getText(); 1423 1424 if ( m_aFilterRows.empty() ) 1425 appendEmptyDisjunctiveTerm(); 1426 1427 // find the current row 1428 if ( ( static_cast<size_t>(m_nCurrentFilterPosition) >= m_aFilterRows.size() ) || ( m_nCurrentFilterPosition < 0 ) ) 1429 { 1430 OSL_ENSURE( false, "FormController::textChanged: m_nCurrentFilterPosition is wrong!" ); 1431 return; 1432 } 1433 1434 FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ]; 1435 1436 // do we have a new filter 1437 if (!aText.isEmpty()) 1438 rRow[xText] = aText; 1439 else 1440 { 1441 // do we have the control in the row 1442 FmFilterRow::iterator iter = rRow.find(xText); 1443 // erase the entry out of the row 1444 if (iter != rRow.end()) 1445 rRow.erase(iter); 1446 } 1447 1448 // multiplex the event to our FilterControllerListeners 1449 FilterEvent aEvent; 1450 aEvent.Source = *this; 1451 aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin(); 1452 aEvent.DisjunctiveTerm = getActiveTerm(); 1453 aEvent.PredicateExpression = aText; 1454 1455 aGuard.clear(); 1456 // <-- SYNCHRONIZED 1457 1458 // notify the changed filter expression 1459 m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent ); 1460 } 1461 1462 // XItemListener 1463 1464 void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/) 1465 { 1466 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1467 impl_onModify(); 1468 } 1469 1470 // XModificationBroadcaster 1471 1472 void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l) 1473 { 1474 ::osl::MutexGuard aGuard( m_aMutex ); 1475 impl_checkDisposed_throw(); 1476 m_aModifyListeners.addInterface( l ); 1477 } 1478 1479 1480 void FormController::removeModifyListener(const Reference< XModifyListener > & l) 1481 { 1482 ::osl::MutexGuard aGuard( m_aMutex ); 1483 impl_checkDisposed_throw(); 1484 m_aModifyListeners.removeInterface( l ); 1485 } 1486 1487 // XModificationListener 1488 1489 void FormController::modified( const EventObject& _rEvent ) 1490 { 1491 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1492 1493 try 1494 { 1495 if ( _rEvent.Source != m_xActiveControl ) 1496 { // let this control grab the focus 1497 // (this case may happen if somebody moves the scroll wheel of the mouse over a control 1498 // which does not have the focus) 1499 // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com 1500 1501 // also, it happens when an image control gets a new image by double-clicking it 1502 // #i88458# / 2009-01-12 / frank.schoenheit@sun.com 1503 Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW ); 1504 xControlWindow->setFocus(); 1505 } 1506 } 1507 catch( const Exception& ) 1508 { 1509 DBG_UNHANDLED_EXCEPTION("svx"); 1510 } 1511 1512 impl_onModify(); 1513 } 1514 1515 1516 void FormController::impl_checkDisposed_throw() const 1517 { 1518 if ( impl_isDisposed_nofail() ) 1519 throw DisposedException( OUString(), *const_cast< FormController* >( this ) ); 1520 } 1521 1522 1523 void FormController::impl_onModify() 1524 { 1525 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1526 1527 { 1528 ::osl::MutexGuard aGuard( m_aMutex ); 1529 if ( !m_bModified ) 1530 m_bModified = true; 1531 } 1532 1533 EventObject aEvt(static_cast<cppu::OWeakObject*>(this)); 1534 m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt ); 1535 } 1536 1537 1538 void FormController::impl_addFilterRow( const FmFilterRow& _row ) 1539 { 1540 m_aFilterRows.push_back( _row ); 1541 1542 if ( m_aFilterRows.size() == 1 ) 1543 { // that's the first row ever 1544 OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" ); 1545 m_nCurrentFilterPosition = 0; 1546 } 1547 } 1548 1549 1550 void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify ) 1551 { 1552 // SYNCHRONIZED --> 1553 impl_addFilterRow( FmFilterRow() ); 1554 1555 // notify the listeners 1556 FilterEvent aEvent; 1557 aEvent.Source = *this; 1558 aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1; 1559 _rClearBeforeNotify.clear(); 1560 // <-- SYNCHRONIZED 1561 m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent ); 1562 } 1563 1564 1565 bool FormController::determineLockState() const 1566 { 1567 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1568 // a.) in filter mode we are always locked 1569 // b.) if we have no valid model or our model (a result set) is not alive -> we're locked 1570 // c.) if we are inserting everything is OK and we are not locked 1571 // d.) if are not updatable or on invalid position 1572 Reference< XResultSet > xResultSet(m_xModelAsIndex, UNO_QUERY); 1573 if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet)) 1574 return true; 1575 else 1576 return !(m_bCanInsert && m_bCurrentRecordNew) 1577 && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate); 1578 } 1579 1580 // FocusListener 1581 1582 void FormController::focusGained(const FocusEvent& e) 1583 { 1584 // SYNCHRONIZED --> 1585 ::osl::ClearableMutexGuard aGuard( m_aMutex ); 1586 impl_checkDisposed_throw(); 1587 1588 m_aControlBorderManager.focusGained( e.Source ); 1589 1590 Reference< XControl > xControl(e.Source, UNO_QUERY); 1591 if (m_bDBConnection) 1592 { 1593 // do we need to keep the locking of the commit 1594 // we hold the lock as long as the control differs from the current 1595 // otherwise we disabled the lock 1596 m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get(); 1597 if (m_bCommitLock) 1598 return; 1599 1600 // when do we have to commit a value to form or a filter 1601 // a.) if the current value is modified 1602 // b.) there must be a current control 1603 // c.) and it must be different from the new focus owning control or 1604 // d.) the focus is moving around (so we have only one control) 1605 1606 if ( ( m_bModified || m_bFiltering ) 1607 && m_xCurrentControl.is() 1608 && ( ( xControl.get() != m_xCurrentControl.get() ) 1609 || ( ( e.FocusFlags & FocusChangeReason::AROUND ) 1610 && ( m_bCycle || m_bFiltering ) 1611 ) 1612 ) 1613 ) 1614 { 1615 // check the old control if the content is ok 1616 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1617 Reference< XBoundControl > xLockingTest(m_xCurrentControl, UNO_QUERY); 1618 bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock(); 1619 assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?"); 1620 // normally, a locked control should not be modified, so probably my bModified must 1621 // have been set from a different context, which I would not understand ... 1622 #endif 1623 DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set"); 1624 // first the control ask if it supports the IFace 1625 Reference< XBoundComponent > xBound(m_xCurrentControl, UNO_QUERY); 1626 if (!xBound.is() && m_xCurrentControl.is()) 1627 xBound.set(m_xCurrentControl->getModel(), UNO_QUERY); 1628 1629 // lock if we lose the focus during commit 1630 m_bCommitLock = true; 1631 1632 // commit unsuccessful, reset focus 1633 if (xBound.is() && !xBound->commit()) 1634 { 1635 // the commit failed and we don't commit again until the current control 1636 // which couldn't be commit gains the focus again 1637 Reference< XWindow > xWindow(m_xCurrentControl, UNO_QUERY); 1638 if (xWindow.is()) 1639 xWindow->setFocus(); 1640 return; 1641 } 1642 else 1643 { 1644 m_bModified = false; 1645 m_bCommitLock = false; 1646 } 1647 } 1648 1649 if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is()) 1650 { 1651 SQLErrorEvent aErrorEvent; 1652 OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" ); 1653 // should have been created in setModel 1654 try 1655 { 1656 if ( e.FocusFlags & FocusChangeReason::FORWARD ) 1657 { 1658 if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) ) 1659 m_xFormOperations->execute( FormFeature::MoveToNext ); 1660 } 1661 else // backward 1662 { 1663 if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) ) 1664 m_xFormOperations->execute( FormFeature::MoveToPrevious ); 1665 } 1666 } 1667 catch ( const Exception& ) 1668 { 1669 // don't handle this any further. That's an ... admissible error. 1670 DBG_UNHANDLED_EXCEPTION("svx"); 1671 } 1672 } 1673 } 1674 1675 // still one and the same control 1676 if ( ( m_xActiveControl == xControl ) 1677 && ( xControl == m_xCurrentControl ) 1678 ) 1679 { 1680 DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected"); 1681 return; 1682 } 1683 1684 bool bActivated = !m_xActiveControl.is() && xControl.is(); 1685 1686 m_xActiveControl = xControl; 1687 1688 implSetCurrentControl( xControl ); 1689 SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form", "implSetCurrentControl did nonsense!" ); 1690 1691 if ( bActivated ) 1692 { 1693 // (asynchronously) call activation handlers 1694 m_aActivationEvent.Call(); 1695 1696 // call modify listeners 1697 if ( m_bModified ) 1698 m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) ); 1699 } 1700 1701 // invalidate all features which depend on the currently focused control 1702 if ( m_bDBConnection && !m_bFiltering ) 1703 implInvalidateCurrentControlDependentFeatures(); 1704 1705 if ( !m_xCurrentControl.is() ) 1706 return; 1707 1708 // control gets focus, then possibly in the visible range 1709 Reference< XFormControllerContext > xContext( m_xFormControllerContext ); 1710 Reference< XControl > xCurrentControl( m_xCurrentControl ); 1711 aGuard.clear(); 1712 // <-- SYNCHRONIZED 1713 1714 if ( xContext.is() ) 1715 xContext->makeVisible( xCurrentControl ); 1716 } 1717 1718 1719 IMPL_LINK_NOARG( FormController, OnActivated, void*, void ) 1720 { 1721 EventObject aEvent; 1722 aEvent.Source = *this; 1723 m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent ); 1724 } 1725 1726 1727 IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void ) 1728 { 1729 EventObject aEvent; 1730 aEvent.Source = *this; 1731 m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent ); 1732 } 1733 1734 1735 void FormController::focusLost(const FocusEvent& e) 1736 { 1737 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1738 1739 m_aControlBorderManager.focusLost( e.Source ); 1740 1741 Reference< XWindowPeer > xNext(e.NextFocus, UNO_QUERY); 1742 Reference< XControl > xNextControl = isInList(xNext); 1743 if (!xNextControl.is()) 1744 { 1745 m_xActiveControl = nullptr; 1746 m_aDeactivationEvent.Call(); 1747 } 1748 } 1749 1750 1751 void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ ) 1752 { 1753 // not interested in 1754 } 1755 1756 1757 void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ ) 1758 { 1759 // not interested in 1760 } 1761 1762 1763 void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent ) 1764 { 1765 m_aControlBorderManager.mouseEntered( _rEvent.Source ); 1766 } 1767 1768 1769 void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent ) 1770 { 1771 m_aControlBorderManager.mouseExited( _rEvent.Source ); 1772 } 1773 1774 1775 void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource ) 1776 { 1777 Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), false, false ) ); 1778 Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY ); 1779 1780 OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" ); 1781 1782 if ( xControl.is() && xValidatable.is() ) 1783 m_aControlBorderManager.validityChanged( xControl, xValidatable ); 1784 } 1785 1786 1787 void FormController::setModel(const Reference< XTabControllerModel > & Model) 1788 { 1789 ::osl::MutexGuard aGuard( m_aMutex ); 1790 impl_checkDisposed_throw(); 1791 1792 DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !"); 1793 1794 try 1795 { 1796 // disconnect from the old model 1797 if (m_xModelAsIndex.is()) 1798 { 1799 if (m_bDBConnection) 1800 { 1801 // we are currently working on the model 1802 EventObject aEvt(m_xModelAsIndex); 1803 unloaded(aEvt); 1804 } 1805 1806 Reference< XLoadable > xForm(m_xModelAsIndex, UNO_QUERY); 1807 if (xForm.is()) 1808 xForm->removeLoadListener(this); 1809 1810 Reference< XSQLErrorBroadcaster > xBroadcaster(m_xModelAsIndex, UNO_QUERY); 1811 if (xBroadcaster.is()) 1812 xBroadcaster->removeSQLErrorListener(this); 1813 1814 Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(m_xModelAsIndex, UNO_QUERY); 1815 if (xParamBroadcaster.is()) 1816 xParamBroadcaster->removeParameterListener(this); 1817 1818 } 1819 1820 disposeAllFeaturesAndDispatchers(); 1821 1822 if ( m_xFormOperations.is() ) 1823 m_xFormOperations->dispose(); 1824 m_xFormOperations.clear(); 1825 1826 // set the new model wait for the load event 1827 if (m_xTabController.is()) 1828 m_xTabController->setModel(Model); 1829 m_xModelAsIndex.set(Model, UNO_QUERY); 1830 m_xModelAsManager.set(Model, UNO_QUERY); 1831 1832 // only if both ifaces exit, the controller will work successful 1833 if (!m_xModelAsIndex.is() || !m_xModelAsManager.is()) 1834 { 1835 m_xModelAsManager = nullptr; 1836 m_xModelAsIndex = nullptr; 1837 } 1838 1839 if (m_xModelAsIndex.is()) 1840 { 1841 // re-create m_xFormOperations 1842 m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this ); 1843 m_xFormOperations->setFeatureInvalidation( this ); 1844 1845 // adding load and ui interaction listeners 1846 Reference< XLoadable > xForm(Model, UNO_QUERY); 1847 if (xForm.is()) 1848 xForm->addLoadListener(this); 1849 1850 Reference< XSQLErrorBroadcaster > xBroadcaster(Model, UNO_QUERY); 1851 if (xBroadcaster.is()) 1852 xBroadcaster->addSQLErrorListener(this); 1853 1854 Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(Model, UNO_QUERY); 1855 if (xParamBroadcaster.is()) 1856 xParamBroadcaster->addParameterListener(this); 1857 1858 // well, is the database already loaded? 1859 // then we have to simulate a load event 1860 Reference< XLoadable > xCursor(m_xModelAsIndex, UNO_QUERY); 1861 if (xCursor.is() && xCursor->isLoaded()) 1862 { 1863 EventObject aEvt(xCursor); 1864 loaded(aEvt); 1865 } 1866 1867 Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY ); 1868 Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() ); 1869 if ( xPropInfo.is() 1870 && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) 1871 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) 1872 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) 1873 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) 1874 ) 1875 { 1876 bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder( 1877 xModelProps.get(), xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) ); 1878 if ( bEnableDynamicControlBorder ) 1879 m_aControlBorderManager.enableDynamicBorderColor(); 1880 else 1881 m_aControlBorderManager.disableDynamicBorderColor(); 1882 1883 Color nColor; 1884 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor ) 1885 m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor ); 1886 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor ) 1887 m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor ); 1888 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor ) 1889 m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor ); 1890 } 1891 } 1892 } 1893 catch( const Exception& ) 1894 { 1895 DBG_UNHANDLED_EXCEPTION("svx"); 1896 } 1897 } 1898 1899 1900 Reference< XTabControllerModel > FormController::getModel() 1901 { 1902 ::osl::MutexGuard aGuard( m_aMutex ); 1903 impl_checkDisposed_throw(); 1904 1905 DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !"); 1906 if (!m_xTabController.is()) 1907 return Reference< XTabControllerModel > (); 1908 return m_xTabController->getModel(); 1909 } 1910 1911 1912 void FormController::addToEventAttacher(const Reference< XControl > & xControl) 1913 { 1914 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1915 OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" ); 1916 if ( !xControl.is() ) 1917 return; /* throw IllegalArgumentException(); */ 1918 1919 // register at the event attacher 1920 Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY); 1921 if (xComp.is() && m_xModelAsIndex.is()) 1922 { 1923 // and look for the position of the ControlModel in it 1924 sal_uInt32 nPos = m_xModelAsIndex->getCount(); 1925 Reference< XFormComponent > xTemp; 1926 for( ; nPos; ) 1927 { 1928 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp; 1929 if (xComp.get() == xTemp.get()) 1930 { 1931 m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), makeAny(xControl) ); 1932 break; 1933 } 1934 } 1935 } 1936 } 1937 1938 1939 void FormController::removeFromEventAttacher(const Reference< XControl > & xControl) 1940 { 1941 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1942 OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" ); 1943 if ( !xControl.is() ) 1944 return; /* throw IllegalArgumentException(); */ 1945 1946 // register at the event attacher 1947 Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY); 1948 if ( xComp.is() && m_xModelAsIndex.is() ) 1949 { 1950 // and look for the position of the ControlModel in it 1951 sal_uInt32 nPos = m_xModelAsIndex->getCount(); 1952 Reference< XFormComponent > xTemp; 1953 for( ; nPos; ) 1954 { 1955 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp; 1956 if (xComp.get() == xTemp.get()) 1957 { 1958 m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) ); 1959 break; 1960 } 1961 } 1962 } 1963 } 1964 1965 1966 void FormController::setContainer(const Reference< XControlContainer > & xContainer) 1967 { 1968 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 1969 Reference< XTabControllerModel > xTabModel(getModel()); 1970 DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined"); 1971 // if we have a new container we need a model 1972 DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !"); 1973 1974 ::osl::MutexGuard aGuard( m_aMutex ); 1975 Reference< XContainer > xCurrentContainer; 1976 if (m_xTabController.is()) 1977 xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY); 1978 if (xCurrentContainer.is()) 1979 { 1980 xCurrentContainer->removeContainerListener(this); 1981 1982 if ( m_aTabActivationIdle.IsActive() ) 1983 m_aTabActivationIdle.Stop(); 1984 1985 // clear the filter map 1986 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) ); 1987 m_aFilterComponents.clear(); 1988 1989 // collecting the controls 1990 const Reference< XControl >* pControls = m_aControls.getConstArray(); 1991 const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); 1992 while ( pControls != pControlsEnd ) 1993 implControlRemoved( *pControls++, true ); 1994 1995 // make database-specific things 1996 if (m_bDBConnection && isListeningForChanges()) 1997 stopListening(); 1998 1999 m_aControls.realloc( 0 ); 2000 } 2001 2002 if (m_xTabController.is()) 2003 m_xTabController->setContainer(xContainer); 2004 2005 // What controls belong to the container? 2006 if (xContainer.is() && xTabModel.is()) 2007 { 2008 Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels(); 2009 const Reference< XControlModel > * pModels = aModels.getConstArray(); 2010 Sequence< Reference< XControl > > aAllControls = xContainer->getControls(); 2011 2012 sal_Int32 nCount = aModels.getLength(); 2013 m_aControls = Sequence< Reference< XControl > >( nCount ); 2014 Reference< XControl > * pControls = m_aControls.getArray(); 2015 2016 // collecting the controls 2017 sal_Int32 i, j; 2018 for (i = 0, j = 0; i < nCount; ++i, ++pModels ) 2019 { 2020 Reference< XControl > xControl = findControl( aAllControls, *pModels, false, true ); 2021 if ( xControl.is() ) 2022 { 2023 pControls[j++] = xControl; 2024 implControlInserted( xControl, true ); 2025 } 2026 } 2027 2028 // not every model had an associated control 2029 if (j != i) 2030 m_aControls.realloc(j); 2031 2032 // listen at the container 2033 Reference< XContainer > xNewContainer(xContainer, UNO_QUERY); 2034 if (xNewContainer.is()) 2035 xNewContainer->addContainerListener(this); 2036 2037 // make database-specific things 2038 if (m_bDBConnection) 2039 { 2040 m_bLocked = determineLockState(); 2041 setLocks(); 2042 if (!isLocked()) 2043 startListening(); 2044 } 2045 } 2046 // the controls are in the right order 2047 m_bControlsSorted = true; 2048 } 2049 2050 2051 Reference< XControlContainer > FormController::getContainer() 2052 { 2053 ::osl::MutexGuard aGuard( m_aMutex ); 2054 impl_checkDisposed_throw(); 2055 2056 DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !"); 2057 if (!m_xTabController.is()) 2058 return Reference< XControlContainer > (); 2059 return m_xTabController->getContainer(); 2060 } 2061 2062 2063 Sequence< Reference< XControl > > FormController::getControls() 2064 { 2065 ::osl::MutexGuard aGuard( m_aMutex ); 2066 impl_checkDisposed_throw(); 2067 2068 if (!m_bControlsSorted) 2069 { 2070 Reference< XTabControllerModel > xModel = getModel(); 2071 if (!xModel.is()) 2072 return m_aControls; 2073 2074 Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels(); 2075 const Reference< XControlModel > * pModels = aControlModels.getConstArray(); 2076 sal_Int32 nModels = aControlModels.getLength(); 2077 2078 Sequence< Reference< XControl > > aNewControls(nModels); 2079 2080 Reference< XControl > * pControls = aNewControls.getArray(); 2081 Reference< XControl > xControl; 2082 2083 // rearrange the controls according to the tab order sequence 2084 sal_Int32 j = 0; 2085 for (sal_Int32 i = 0; i < nModels; ++i, ++pModels ) 2086 { 2087 xControl = findControl( m_aControls, *pModels, true, true ); 2088 if ( xControl.is() ) 2089 pControls[j++] = xControl; 2090 } 2091 2092 // not every model had an associated control 2093 if ( j != nModels ) 2094 aNewControls.realloc( j ); 2095 2096 m_aControls = aNewControls; 2097 m_bControlsSorted = true; 2098 } 2099 return m_aControls; 2100 } 2101 2102 2103 void FormController::autoTabOrder() 2104 { 2105 ::osl::MutexGuard aGuard( m_aMutex ); 2106 impl_checkDisposed_throw(); 2107 2108 DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !"); 2109 if (m_xTabController.is()) 2110 m_xTabController->autoTabOrder(); 2111 } 2112 2113 2114 void FormController::activateTabOrder() 2115 { 2116 ::osl::MutexGuard aGuard( m_aMutex ); 2117 impl_checkDisposed_throw(); 2118 2119 DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !"); 2120 if (m_xTabController.is()) 2121 m_xTabController->activateTabOrder(); 2122 } 2123 2124 2125 void FormController::setControlLock(const Reference< XControl > & xControl) 2126 { 2127 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2128 bool bLocked = isLocked(); 2129 2130 // It is locked 2131 // a. if the entire record is locked 2132 // b. if the associated field is locked 2133 Reference< XBoundControl > xBound(xControl, UNO_QUERY); 2134 if (xBound.is() && 2135 ( (bLocked && bLocked != bool(xBound->getLock())) || 2136 !bLocked)) // always uncheck individual fields when unlocking 2137 { 2138 // there is a data source 2139 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY); 2140 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)) 2141 { 2142 // what about the ReadOnly and Enable properties 2143 bool bTouch = true; 2144 if (::comphelper::hasProperty(FM_PROP_ENABLED, xSet)) 2145 bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED)); 2146 if (::comphelper::hasProperty(FM_PROP_READONLY, xSet)) 2147 bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY)); 2148 2149 if (bTouch) 2150 { 2151 Reference< XPropertySet > xField; 2152 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField; 2153 if (xField.is()) 2154 { 2155 if (bLocked) 2156 xBound->setLock(bLocked); 2157 else 2158 { 2159 try 2160 { 2161 Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY); 2162 if (aVal.hasValue() && ::comphelper::getBOOL(aVal)) 2163 xBound->setLock(true); 2164 else 2165 xBound->setLock(bLocked); 2166 } 2167 catch( const Exception& ) 2168 { 2169 DBG_UNHANDLED_EXCEPTION("svx"); 2170 } 2171 2172 } 2173 } 2174 } 2175 } 2176 } 2177 } 2178 2179 2180 void FormController::setLocks() 2181 { 2182 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2183 // lock/unlock all controls connected to a data source 2184 const Reference< XControl >* pControls = m_aControls.getConstArray(); 2185 const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); 2186 while ( pControls != pControlsEnd ) 2187 setControlLock( *pControls++ ); 2188 } 2189 2190 2191 namespace 2192 { 2193 bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener ) 2194 { 2195 bool bShould = false; 2196 2197 Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY ); 2198 if ( xBound.is() ) 2199 { 2200 bShould = true; 2201 } 2202 else if ( _rxControl.is() ) 2203 { 2204 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY ); 2205 if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) ) 2206 { 2207 Reference< XPropertySet > xField; 2208 xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField; 2209 bShould = xField.is(); 2210 2211 if ( !bShould && _rxBoundFieldListener.is() ) 2212 xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener ); 2213 } 2214 } 2215 2216 return bShould; 2217 } 2218 } 2219 2220 2221 void FormController::startControlModifyListening(const Reference< XControl > & xControl) 2222 { 2223 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2224 2225 bool bModifyListening = lcl_shouldListenForModifications( xControl, this ); 2226 2227 // artificial while 2228 while ( bModifyListening ) 2229 { 2230 Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY); 2231 if (xMod.is()) 2232 { 2233 xMod->addModifyListener(this); 2234 break; 2235 } 2236 2237 // all the text to prematurely recognize a modified 2238 Reference< XTextComponent > xText(xControl, UNO_QUERY); 2239 if (xText.is()) 2240 { 2241 xText->addTextListener(this); 2242 break; 2243 } 2244 2245 Reference< XCheckBox > xBox(xControl, UNO_QUERY); 2246 if (xBox.is()) 2247 { 2248 xBox->addItemListener(this); 2249 break; 2250 } 2251 2252 Reference< XComboBox > xCbBox(xControl, UNO_QUERY); 2253 if (xCbBox.is()) 2254 { 2255 xCbBox->addItemListener(this); 2256 break; 2257 } 2258 2259 Reference< XListBox > xListBox(xControl, UNO_QUERY); 2260 if (xListBox.is()) 2261 { 2262 xListBox->addItemListener(this); 2263 break; 2264 } 2265 break; 2266 } 2267 } 2268 2269 2270 void FormController::stopControlModifyListening(const Reference< XControl > & xControl) 2271 { 2272 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2273 2274 bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr ); 2275 2276 // artificial while 2277 while (bModifyListening) 2278 { 2279 Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY); 2280 if (xMod.is()) 2281 { 2282 xMod->removeModifyListener(this); 2283 break; 2284 } 2285 // all the text to prematurely recognize a modified 2286 Reference< XTextComponent > xText(xControl, UNO_QUERY); 2287 if (xText.is()) 2288 { 2289 xText->removeTextListener(this); 2290 break; 2291 } 2292 2293 Reference< XCheckBox > xBox(xControl, UNO_QUERY); 2294 if (xBox.is()) 2295 { 2296 xBox->removeItemListener(this); 2297 break; 2298 } 2299 2300 Reference< XComboBox > xCbBox(xControl, UNO_QUERY); 2301 if (xCbBox.is()) 2302 { 2303 xCbBox->removeItemListener(this); 2304 break; 2305 } 2306 2307 Reference< XListBox > xListBox(xControl, UNO_QUERY); 2308 if (xListBox.is()) 2309 { 2310 xListBox->removeItemListener(this); 2311 break; 2312 } 2313 break; 2314 } 2315 } 2316 2317 2318 void FormController::startListening() 2319 { 2320 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2321 m_bModified = false; 2322 2323 // now register at bound fields 2324 const Reference< XControl >* pControls = m_aControls.getConstArray(); 2325 const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); 2326 while ( pControls != pControlsEnd ) 2327 startControlModifyListening( *pControls++ ); 2328 } 2329 2330 2331 void FormController::stopListening() 2332 { 2333 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2334 m_bModified = false; 2335 2336 // now register at bound fields 2337 const Reference< XControl >* pControls = m_aControls.getConstArray(); 2338 const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); 2339 while ( pControls != pControlsEnd ) 2340 stopControlModifyListening( *pControls++ ); 2341 } 2342 2343 2344 Reference< XControl > FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const 2345 { 2346 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2347 DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" ); 2348 2349 Reference< XControl >* pControls = _rControls.getArray(); 2350 Reference< XControlModel > xModel; 2351 for ( sal_Int32 i = 0, nCount = _rControls.getLength(); i < nCount; ++i, ++pControls ) 2352 { 2353 if ( pControls->is() ) 2354 { 2355 xModel = (*pControls)->getModel(); 2356 if ( xModel.get() == xCtrlModel.get() ) 2357 { 2358 Reference< XControl > xControl( *pControls ); 2359 if ( _bRemove ) 2360 ::comphelper::removeElementAt( _rControls, i ); 2361 else if ( _bOverWrite ) 2362 pControls->clear(); 2363 return xControl; 2364 } 2365 } 2366 } 2367 return Reference< XControl > (); 2368 } 2369 2370 2371 void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher ) 2372 { 2373 Reference< XWindow > xWindow( _rxControl, UNO_QUERY ); 2374 if ( xWindow.is() ) 2375 { 2376 xWindow->addFocusListener( this ); 2377 xWindow->addMouseListener( this ); 2378 2379 if ( _bAddToEventAttacher ) 2380 addToEventAttacher( _rxControl ); 2381 } 2382 2383 // add a dispatch interceptor to the control (if supported) 2384 Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY ); 2385 if ( xInterception.is() ) 2386 createInterceptor( xInterception ); 2387 2388 if ( _rxControl.is() ) 2389 { 2390 Reference< XControlModel > xModel( _rxControl->getModel() ); 2391 2392 // we want to know about the reset of the model of our controls 2393 // (for correctly resetting m_bModified) 2394 Reference< XReset > xReset( xModel, UNO_QUERY ); 2395 if ( xReset.is() ) 2396 xReset->addResetListener( this ); 2397 2398 // and we want to know about the validity, to visually indicate it 2399 Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY ); 2400 if ( xValidatable.is() ) 2401 { 2402 xValidatable->addFormComponentValidityListener( this ); 2403 m_aControlBorderManager.validityChanged( _rxControl, xValidatable ); 2404 } 2405 } 2406 2407 } 2408 2409 2410 void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher ) 2411 { 2412 Reference< XWindow > xWindow( _rxControl, UNO_QUERY ); 2413 if ( xWindow.is() ) 2414 { 2415 xWindow->removeFocusListener( this ); 2416 xWindow->removeMouseListener( this ); 2417 2418 if ( _bRemoveFromEventAttacher ) 2419 removeFromEventAttacher( _rxControl ); 2420 } 2421 2422 Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY); 2423 if ( xInterception.is() ) 2424 deleteInterceptor( xInterception ); 2425 2426 if ( _rxControl.is() ) 2427 { 2428 Reference< XControlModel > xModel( _rxControl->getModel() ); 2429 2430 Reference< XReset > xReset( xModel, UNO_QUERY ); 2431 if ( xReset.is() ) 2432 xReset->removeResetListener( this ); 2433 2434 Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY ); 2435 if ( xValidatable.is() ) 2436 xValidatable->removeFormComponentValidityListener( this ); 2437 } 2438 } 2439 2440 2441 void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl ) 2442 { 2443 if ( m_xCurrentControl.get() == _rxControl.get() ) 2444 return; 2445 2446 Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY ); 2447 if ( xGridControl.is() ) 2448 xGridControl->removeGridControlListener( this ); 2449 2450 m_xCurrentControl = _rxControl; 2451 2452 xGridControl.set( m_xCurrentControl, UNO_QUERY ); 2453 if ( xGridControl.is() ) 2454 xGridControl->addGridControlListener( this ); 2455 } 2456 2457 2458 void FormController::insertControl(const Reference< XControl > & xControl) 2459 { 2460 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2461 m_bControlsSorted = false; 2462 m_aControls.realloc(m_aControls.getLength() + 1); 2463 m_aControls.getArray()[m_aControls.getLength() - 1] = xControl; 2464 2465 if (m_pColumnInfoCache) 2466 m_pColumnInfoCache->deinitializeControls(); 2467 2468 implControlInserted( xControl, m_bAttachEvents ); 2469 2470 if (m_bDBConnection && !m_bFiltering) 2471 setControlLock(xControl); 2472 2473 if (isListeningForChanges() && m_bAttachEvents) 2474 startControlModifyListening( xControl ); 2475 } 2476 2477 2478 void FormController::removeControl(const Reference< XControl > & xControl) 2479 { 2480 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2481 const Reference< XControl >* pControls = m_aControls.getConstArray(); 2482 const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); 2483 while ( pControls != pControlsEnd ) 2484 { 2485 if ( xControl.get() == (*pControls++).get() ) 2486 { 2487 ::comphelper::removeElementAt( m_aControls, pControls - m_aControls.getConstArray() - 1 ); 2488 break; 2489 } 2490 } 2491 2492 FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl ); 2493 if ( componentPos != m_aFilterComponents.end() ) 2494 m_aFilterComponents.erase( componentPos ); 2495 2496 implControlRemoved( xControl, m_bDetachEvents ); 2497 2498 if ( isListeningForChanges() && m_bDetachEvents ) 2499 stopControlModifyListening( xControl ); 2500 } 2501 2502 // XLoadListener 2503 2504 void FormController::loaded(const EventObject& rEvent) 2505 { 2506 OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" ); 2507 2508 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2509 ::osl::MutexGuard aGuard( m_aMutex ); 2510 Reference< XRowSet > xForm(rEvent.Source, UNO_QUERY); 2511 // do we have a connected data source 2512 if (xForm.is() && getConnection(xForm).is()) 2513 { 2514 Reference< XPropertySet > xSet(xForm, UNO_QUERY); 2515 if (xSet.is()) 2516 { 2517 Any aVal = xSet->getPropertyValue(FM_PROP_CYCLE); 2518 sal_Int32 aVal2 = 0; 2519 ::cppu::enum2int(aVal2,aVal); 2520 m_bCycle = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS; 2521 m_bCanUpdate = canUpdate(xSet); 2522 m_bCanInsert = canInsert(xSet); 2523 m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)); 2524 m_bCurrentRecordNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)); 2525 2526 startFormListening( xSet, false ); 2527 2528 // set the locks for the current controls 2529 if (getContainer().is()) 2530 { 2531 m_aLoadEvent.Call(); 2532 } 2533 } 2534 else 2535 { 2536 m_bCanInsert = m_bCanUpdate = m_bCycle = false; 2537 m_bCurrentRecordModified = false; 2538 m_bCurrentRecordNew = false; 2539 m_bLocked = false; 2540 } 2541 m_bDBConnection = true; 2542 } 2543 else 2544 { 2545 m_bDBConnection = false; 2546 m_bCanInsert = m_bCanUpdate = m_bCycle = false; 2547 m_bCurrentRecordModified = false; 2548 m_bCurrentRecordNew = false; 2549 m_bLocked = false; 2550 } 2551 2552 Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY ); 2553 m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr ); 2554 2555 updateAllDispatchers(); 2556 } 2557 2558 2559 void FormController::updateAllDispatchers() const 2560 { 2561 ::std::for_each( 2562 m_aFeatureDispatchers.begin(), 2563 m_aFeatureDispatchers.end(), 2564 [] (const DispatcherContainer::value_type& dispatcher) { 2565 UpdateAllListeners()(dispatcher.second); 2566 }); 2567 } 2568 2569 2570 IMPL_LINK_NOARG(FormController, OnLoad, void*, void) 2571 { 2572 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2573 m_bLocked = determineLockState(); 2574 2575 setLocks(); 2576 2577 if (!m_bLocked) 2578 startListening(); 2579 2580 // just one exception toggle the auto values 2581 if (m_bCurrentRecordNew) 2582 toggleAutoFields(true); 2583 } 2584 2585 2586 void FormController::unloaded(const EventObject& /*rEvent*/) 2587 { 2588 ::osl::MutexGuard aGuard( m_aMutex ); 2589 impl_checkDisposed_throw(); 2590 2591 updateAllDispatchers(); 2592 } 2593 2594 2595 void FormController::reloading(const EventObject& /*aEvent*/) 2596 { 2597 ::osl::MutexGuard aGuard( m_aMutex ); 2598 impl_checkDisposed_throw(); 2599 2600 // do the same like in unloading 2601 // just one exception toggle the auto values 2602 m_aToggleEvent.CancelPendingCall(); 2603 unload(); 2604 } 2605 2606 2607 void FormController::reloaded(const EventObject& aEvent) 2608 { 2609 ::osl::MutexGuard aGuard( m_aMutex ); 2610 impl_checkDisposed_throw(); 2611 2612 loaded(aEvent); 2613 } 2614 2615 2616 void FormController::unloading(const EventObject& /*aEvent*/) 2617 { 2618 ::osl::MutexGuard aGuard( m_aMutex ); 2619 impl_checkDisposed_throw(); 2620 2621 unload(); 2622 } 2623 2624 2625 void FormController::unload() 2626 { 2627 ::osl::MutexGuard aGuard( m_aMutex ); 2628 impl_checkDisposed_throw(); 2629 2630 m_aLoadEvent.CancelPendingCall(); 2631 2632 // be sure not to have autofields 2633 if (m_bCurrentRecordNew) 2634 toggleAutoFields(false); 2635 2636 // remove bound field listing again 2637 removeBoundFieldListener(); 2638 2639 if (m_bDBConnection && isListeningForChanges()) 2640 stopListening(); 2641 2642 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY ); 2643 if ( m_bDBConnection && xSet.is() ) 2644 stopFormListening( xSet, false ); 2645 2646 m_bDBConnection = false; 2647 m_bCanInsert = m_bCanUpdate = m_bCycle = false; 2648 m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false; 2649 2650 m_pColumnInfoCache.reset(); 2651 } 2652 2653 2654 void FormController::removeBoundFieldListener() 2655 { 2656 const Reference< XControl >* pControls = m_aControls.getConstArray(); 2657 const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); 2658 while ( pControls != pControlsEnd ) 2659 { 2660 Reference< XPropertySet > xProp( *pControls++, UNO_QUERY ); 2661 if ( xProp.is() ) 2662 xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this ); 2663 } 2664 } 2665 2666 2667 void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly ) 2668 { 2669 try 2670 { 2671 if ( m_bCanInsert || m_bCanUpdate ) // form can be modified 2672 { 2673 _rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this ); 2674 _rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this ); 2675 2676 if ( !_bPropertiesOnly ) 2677 { 2678 // set the Listener for UI interaction 2679 Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY ); 2680 if ( xApprove.is() ) 2681 xApprove->addRowSetApproveListener( this ); 2682 2683 // listener for row set changes 2684 Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY ); 2685 if ( xRowSet.is() ) 2686 xRowSet->addRowSetListener( this ); 2687 } 2688 } 2689 2690 Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo(); 2691 if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) ) 2692 _rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this ); 2693 } 2694 catch( const Exception& ) 2695 { 2696 DBG_UNHANDLED_EXCEPTION("svx"); 2697 } 2698 } 2699 2700 2701 void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly ) 2702 { 2703 try 2704 { 2705 if ( m_bCanInsert || m_bCanUpdate ) 2706 { 2707 _rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this ); 2708 _rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this ); 2709 2710 if ( !_bPropertiesOnly ) 2711 { 2712 Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY ); 2713 if (xApprove.is()) 2714 xApprove->removeRowSetApproveListener(this); 2715 2716 Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY ); 2717 if ( xRowSet.is() ) 2718 xRowSet->removeRowSetListener( this ); 2719 } 2720 } 2721 2722 Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo(); 2723 if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) ) 2724 _rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this ); 2725 } 2726 catch( const Exception& ) 2727 { 2728 DBG_UNHANDLED_EXCEPTION("svx"); 2729 } 2730 } 2731 2732 // css::sdbc::XRowSetListener 2733 2734 void FormController::cursorMoved(const EventObject& /*event*/) 2735 { 2736 ::osl::MutexGuard aGuard( m_aMutex ); 2737 impl_checkDisposed_throw(); 2738 2739 // toggle the locking ? 2740 if (m_bLocked != determineLockState()) 2741 { 2742 m_bLocked = !m_bLocked; 2743 setLocks(); 2744 if (isListeningForChanges()) 2745 startListening(); 2746 else 2747 stopListening(); 2748 } 2749 2750 // neither the current control nor the current record are modified anymore 2751 m_bCurrentRecordModified = m_bModified = false; 2752 } 2753 2754 2755 void FormController::rowChanged(const EventObject& /*event*/) 2756 { 2757 // not interested in ... 2758 } 2759 2760 void FormController::rowSetChanged(const EventObject& /*event*/) 2761 { 2762 // not interested in ... 2763 } 2764 2765 2766 // XContainerListener 2767 2768 void SAL_CALL FormController::elementInserted(const ContainerEvent& evt) 2769 { 2770 ::osl::MutexGuard aGuard( m_aMutex ); 2771 impl_checkDisposed_throw(); 2772 2773 Reference< XControl > xControl( evt.Element, UNO_QUERY ); 2774 if ( !xControl.is() ) 2775 return; 2776 2777 Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY); 2778 if (xModel.is() && m_xModelAsIndex == xModel->getParent()) 2779 { 2780 insertControl(xControl); 2781 2782 if ( m_aTabActivationIdle.IsActive() ) 2783 m_aTabActivationIdle.Stop(); 2784 2785 m_aTabActivationIdle.Start(); 2786 } 2787 // are we in filtermode and a XModeSelector has inserted an element 2788 else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is()) 2789 { 2790 xModel.set(evt.Source, UNO_QUERY); 2791 if (xModel.is() && m_xModelAsIndex == xModel->getParent()) 2792 { 2793 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY); 2794 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)) 2795 { 2796 // does the model use a bound field ? 2797 Reference< XPropertySet > xField; 2798 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField; 2799 2800 Reference< XTextComponent > xText(xControl, UNO_QUERY); 2801 // may we filter the field? 2802 if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) && 2803 ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE))) 2804 { 2805 m_aFilterComponents.push_back( xText ); 2806 xText->addTextListener( this ); 2807 } 2808 } 2809 } 2810 } 2811 } 2812 2813 2814 void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt) 2815 { 2816 // simulate an elementRemoved 2817 ContainerEvent aRemoveEvent( evt ); 2818 aRemoveEvent.Element = evt.ReplacedElement; 2819 aRemoveEvent.ReplacedElement = Any(); 2820 elementRemoved( aRemoveEvent ); 2821 2822 // simulate an elementInserted 2823 ContainerEvent aInsertEvent( evt ); 2824 aInsertEvent.ReplacedElement = Any(); 2825 elementInserted( aInsertEvent ); 2826 } 2827 2828 2829 void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt) 2830 { 2831 ::osl::MutexGuard aGuard( m_aMutex ); 2832 impl_checkDisposed_throw(); 2833 2834 Reference< XControl > xControl; 2835 evt.Element >>= xControl; 2836 if (!xControl.is()) 2837 return; 2838 2839 Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY); 2840 if (xModel.is() && m_xModelAsIndex == xModel->getParent()) 2841 { 2842 removeControl(xControl); 2843 // Do not recalculate TabOrder, because it must already work internally! 2844 } 2845 // are we in filtermode and a XModeSelector has inserted an element 2846 else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is()) 2847 { 2848 FilterComponents::iterator componentPos = ::std::find( 2849 m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl ); 2850 if ( componentPos != m_aFilterComponents.end() ) 2851 m_aFilterComponents.erase( componentPos ); 2852 } 2853 } 2854 2855 2856 Reference< XControl > FormController::isInList(const Reference< XWindowPeer > & xPeer) const 2857 { 2858 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 2859 const Reference< XControl >* pControls = m_aControls.getConstArray(); 2860 2861 sal_uInt32 nCtrls = m_aControls.getLength(); 2862 for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls ) 2863 { 2864 if ( pControls->is() ) 2865 { 2866 Reference< XVclWindowPeer > xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY); 2867 if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) ) 2868 return *pControls; 2869 } 2870 } 2871 return Reference< XControl > (); 2872 } 2873 2874 2875 void FormController::activateFirst() 2876 { 2877 ::osl::MutexGuard aGuard( m_aMutex ); 2878 impl_checkDisposed_throw(); 2879 2880 DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !"); 2881 if (m_xTabController.is()) 2882 m_xTabController->activateFirst(); 2883 } 2884 2885 2886 void FormController::activateLast() 2887 { 2888 ::osl::MutexGuard aGuard( m_aMutex ); 2889 impl_checkDisposed_throw(); 2890 2891 DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !"); 2892 if (m_xTabController.is()) 2893 m_xTabController->activateLast(); 2894 } 2895 2896 // XFormController 2897 2898 Reference< XFormOperations > SAL_CALL FormController::getFormOperations() 2899 { 2900 ::osl::MutexGuard aGuard( m_aMutex ); 2901 impl_checkDisposed_throw(); 2902 2903 return m_xFormOperations; 2904 } 2905 2906 2907 Reference< XControl> SAL_CALL FormController::getCurrentControl() 2908 { 2909 ::osl::MutexGuard aGuard( m_aMutex ); 2910 impl_checkDisposed_throw(); 2911 return m_xCurrentControl; 2912 } 2913 2914 2915 void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l) 2916 { 2917 ::osl::MutexGuard aGuard( m_aMutex ); 2918 impl_checkDisposed_throw(); 2919 m_aActivateListeners.addInterface(l); 2920 } 2921 2922 void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l) 2923 { 2924 ::osl::MutexGuard aGuard( m_aMutex ); 2925 impl_checkDisposed_throw(); 2926 m_aActivateListeners.removeInterface(l); 2927 } 2928 2929 2930 void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController ) 2931 { 2932 ::osl::MutexGuard aGuard( m_aMutex ); 2933 impl_checkDisposed_throw(); 2934 2935 if ( !ChildController.is() ) 2936 throw IllegalArgumentException( OUString(), *this, 1 ); 2937 // TODO: (localized) error message 2938 2939 // the parent of our (to-be-)child must be our own model 2940 Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY ); 2941 if ( !xFormOfChild.is() ) 2942 throw IllegalArgumentException( OUString(), *this, 1 ); 2943 // TODO: (localized) error message 2944 2945 if ( xFormOfChild->getParent() != m_xModelAsIndex ) 2946 throw IllegalArgumentException( OUString(), *this, 1 ); 2947 // TODO: (localized) error message 2948 2949 m_aChildren.push_back( ChildController ); 2950 ChildController->setParent( *this ); 2951 2952 // search the position of the model within the form 2953 sal_uInt32 nPos = m_xModelAsIndex->getCount(); 2954 Reference< XFormComponent > xTemp; 2955 for( ; nPos; ) 2956 { 2957 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp; 2958 if ( xFormOfChild == xTemp ) 2959 { 2960 m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), makeAny( ChildController) ); 2961 break; 2962 } 2963 } 2964 } 2965 2966 2967 Reference< XFormControllerContext > SAL_CALL FormController::getContext() 2968 { 2969 ::osl::MutexGuard aGuard( m_aMutex ); 2970 impl_checkDisposed_throw(); 2971 return m_xFormControllerContext; 2972 } 2973 2974 2975 void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context ) 2976 { 2977 ::osl::MutexGuard aGuard( m_aMutex ); 2978 impl_checkDisposed_throw(); 2979 m_xFormControllerContext = _context; 2980 } 2981 2982 2983 Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler() 2984 { 2985 ::osl::MutexGuard aGuard( m_aMutex ); 2986 impl_checkDisposed_throw(); 2987 return m_xInteractionHandler; 2988 } 2989 2990 2991 void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler ) 2992 { 2993 ::osl::MutexGuard aGuard( m_aMutex ); 2994 impl_checkDisposed_throw(); 2995 m_xInteractionHandler = _interactionHandler; 2996 } 2997 2998 2999 void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos) 3000 { 3001 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 3002 // create the composer 3003 Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY); 3004 Reference< XConnection > xConnection(getConnection(xForm)); 3005 if (xForm.is()) 3006 { 3007 try 3008 { 3009 Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW ); 3010 m_xComposer.set( 3011 xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), 3012 UNO_QUERY_THROW ); 3013 3014 Reference< XPropertySet > xSet( xForm, UNO_QUERY ); 3015 OUString sStatement = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) ); 3016 OUString sFilter = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) ); 3017 m_xComposer->setElementaryQuery( sStatement ); 3018 m_xComposer->setFilter( sFilter ); 3019 } 3020 catch( const Exception& ) 3021 { 3022 DBG_UNHANDLED_EXCEPTION("svx"); 3023 } 3024 } 3025 3026 if (m_xComposer.is()) 3027 { 3028 Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter(); 3029 3030 // ok, we receive the list of filters as sequence of fieldnames, value 3031 // now we have to transform the fieldname into UI names, that could be a label of the field or 3032 // a aliasname or the fieldname itself 3033 3034 // first adjust the field names if necessary 3035 Reference< XNameAccess > xQueryColumns = 3036 Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns(); 3037 3038 for (auto& rFieldInfo : rFieldInfos) 3039 { 3040 if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) ) 3041 { 3042 if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() ) 3043 rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName; 3044 } 3045 } 3046 3047 Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData()); 3048 // now transfer the filters into Value/TextComponent pairs 3049 ::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers()); 3050 3051 // need to parse criteria localized 3052 Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true)); 3053 Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext); 3054 xFormatter->attachNumberFormatsSupplier(xFormatSupplier); 3055 Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale(); 3056 const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() ); 3057 /* FIXME: casting this to sal_Char is plain wrong and of course only 3058 * works for ASCII separators, but 3059 * pParseNode->parseNodeToPredicateStr() expects a sal_Char. Fix it 3060 * there. */ 3061 sal_Char cDecimalSeparator = static_cast<sal_Char>(rLocaleWrapper.getNumDecimalSep()[0]); 3062 SAL_WARN_IF( static_cast<sal_Unicode>(cDecimalSeparator) != rLocaleWrapper.getNumDecimalSep()[0], 3063 "svx.form", "FormController::setFilter: wrong cast of decimal separator to sal_Char!"); 3064 3065 // retrieving the filter 3066 const Sequence < PropertyValue >* pRow = aFilterRows.getConstArray(); 3067 for (sal_Int32 i = 0, nLen = aFilterRows.getLength(); i < nLen; ++i) 3068 { 3069 FmFilterRow aRow; 3070 3071 // search a field for the given name 3072 const PropertyValue* pRefValues = pRow[i].getConstArray(); 3073 for (sal_Int32 j = 0, nLen1 = pRow[i].getLength(); j < nLen1; j++) 3074 { 3075 // look for the text component 3076 Reference< XPropertySet > xField; 3077 try 3078 { 3079 Reference< XPropertySet > xSet; 3080 OUString aRealName; 3081 3082 // first look with the given name 3083 if (xQueryColumns->hasByName(pRefValues[j].Name)) 3084 { 3085 xQueryColumns->getByName(pRefValues[j].Name) >>= xSet; 3086 3087 // get the RealName 3088 xSet->getPropertyValue("RealName") >>= aRealName; 3089 3090 // compare the condition field name and the RealName 3091 if (aCompare(aRealName, pRefValues[j].Name)) 3092 xField = xSet; 3093 } 3094 if (!xField.is()) 3095 { 3096 // no we have to check every column to find the realname 3097 Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY); 3098 for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++) 3099 { 3100 xColumnsByIndex->getByIndex(n) >>= xSet; 3101 xSet->getPropertyValue("RealName") >>= aRealName; 3102 if (aCompare(aRealName, pRefValues[j].Name)) 3103 { 3104 // get the column by its alias 3105 xField = xSet; 3106 break; 3107 } 3108 } 3109 } 3110 if (!xField.is()) 3111 continue; 3112 } 3113 catch (const Exception&) 3114 { 3115 continue; 3116 } 3117 3118 // find the text component 3119 for (const auto& rFieldInfo : rFieldInfos) 3120 { 3121 // we found the field so insert a new entry to the filter row 3122 if (rFieldInfo.xField == xField) 3123 { 3124 // do we already have the control ? 3125 if (aRow.find(rFieldInfo.xText) != aRow.end()) 3126 { 3127 OUString aCompText = aRow[rFieldInfo.xText]; 3128 aCompText += " "; 3129 OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And); 3130 aCompText += OUString(aVal.getStr(),aVal.getLength(),RTL_TEXTENCODING_ASCII_US); 3131 aCompText += " "; 3132 aCompText += ::comphelper::getString(pRefValues[j].Value); 3133 aRow[rFieldInfo.xText] = aCompText; 3134 } 3135 else 3136 { 3137 OUString sPredicate,sErrorMsg; 3138 pRefValues[j].Value >>= sPredicate; 3139 std::shared_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField); 3140 if ( pParseNode != nullptr ) 3141 { 3142 OUString sCriteria; 3143 switch (pRefValues[j].Handle) 3144 { 3145 case css::sdb::SQLFilterOperator::EQUAL: 3146 sCriteria += "="; 3147 break; 3148 case css::sdb::SQLFilterOperator::NOT_EQUAL: 3149 sCriteria += "!="; 3150 break; 3151 case css::sdb::SQLFilterOperator::LESS: 3152 sCriteria += "<"; 3153 break; 3154 case css::sdb::SQLFilterOperator::GREATER: 3155 sCriteria += ">"; 3156 break; 3157 case css::sdb::SQLFilterOperator::LESS_EQUAL: 3158 sCriteria += "<="; 3159 break; 3160 case css::sdb::SQLFilterOperator::GREATER_EQUAL: 3161 sCriteria += ">="; 3162 break; 3163 case css::sdb::SQLFilterOperator::LIKE: 3164 sCriteria += "LIKE "; 3165 break; 3166 case css::sdb::SQLFilterOperator::NOT_LIKE: 3167 sCriteria += "NOT LIKE "; 3168 break; 3169 case css::sdb::SQLFilterOperator::SQLNULL: 3170 sCriteria += "IS NULL"; 3171 break; 3172 case css::sdb::SQLFilterOperator::NOT_SQLNULL: 3173 sCriteria += "IS NOT NULL"; 3174 break; 3175 } 3176 pParseNode->parseNodeToPredicateStr( sCriteria 3177 ,xConnection 3178 ,xFormatter 3179 ,xField 3180 ,OUString() 3181 ,aAppLocale 3182 ,cDecimalSeparator 3183 ,getParseContext()); 3184 aRow[rFieldInfo.xText] = sCriteria; 3185 } 3186 } 3187 } 3188 } 3189 } 3190 3191 if (aRow.empty()) 3192 continue; 3193 3194 impl_addFilterRow( aRow ); 3195 } 3196 } 3197 3198 // now set the filter controls 3199 for (const auto& rFieldInfo : rFieldInfos) 3200 { 3201 m_aFilterComponents.push_back( rFieldInfo.xText ); 3202 } 3203 } 3204 3205 3206 void FormController::startFiltering() 3207 { 3208 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 3209 3210 Reference< XConnection > xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) ); 3211 if ( !xConnection.is() ) 3212 // nothing to do - can't filter a form which is not connected 3213 return; 3214 3215 // stop listening for controls 3216 if (isListeningForChanges()) 3217 stopListening(); 3218 3219 m_bFiltering = true; 3220 3221 // as we don't want new controls to be attached to the scripting environment 3222 // we change attach flags 3223 m_bAttachEvents = false; 3224 3225 // exchanging the controls for the current form 3226 Sequence< Reference< XControl > > aControlsCopy( m_aControls ); 3227 const Reference< XControl >* pControls = aControlsCopy.getConstArray(); 3228 sal_Int32 nControlCount = aControlsCopy.getLength(); 3229 3230 // the control we have to activate after replacement 3231 Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats(xConnection, true); 3232 Reference< XNumberFormatter > xFormatter = NumberFormatter::create(m_xComponentContext); 3233 xFormatter->attachNumberFormatsSupplier(xFormatSupplier); 3234 3235 // structure for storing the field info 3236 ::std::vector<FmFieldInfo> aFieldInfos; 3237 3238 for (sal_Int32 i = nControlCount; i > 0;) 3239 { 3240 Reference< XControl > xControl = pControls[--i]; 3241 if (xControl.is()) 3242 { 3243 // no events for the control anymore 3244 removeFromEventAttacher(xControl); 3245 3246 // do we have a mode selector 3247 Reference< XModeSelector > xSelector(xControl, UNO_QUERY); 3248 if (xSelector.is()) 3249 { 3250 xSelector->setMode( "FilterMode" ); 3251 3252 // listening for new controls of the selector 3253 Reference< XContainer > xContainer(xSelector, UNO_QUERY); 3254 if (xContainer.is()) 3255 xContainer->addContainerListener(this); 3256 3257 Reference< XEnumerationAccess > xElementAccess(xSelector, UNO_QUERY); 3258 if (xElementAccess.is()) 3259 { 3260 Reference< XEnumeration > xEnumeration(xElementAccess->createEnumeration()); 3261 Reference< XControl > xSubControl; 3262 while (xEnumeration->hasMoreElements()) 3263 { 3264 xEnumeration->nextElement() >>= xSubControl; 3265 if (xSubControl.is()) 3266 { 3267 Reference< XPropertySet > xSet(xSubControl->getModel(), UNO_QUERY); 3268 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)) 3269 { 3270 // does the model use a bound field ? 3271 Reference< XPropertySet > xField; 3272 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField; 3273 3274 Reference< XTextComponent > xText(xSubControl, UNO_QUERY); 3275 // may we filter the field? 3276 if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) && 3277 ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE))) 3278 { 3279 aFieldInfos.emplace_back(xField, xText); 3280 xText->addTextListener(this); 3281 } 3282 } 3283 } 3284 } 3285 } 3286 continue; 3287 } 3288 3289 Reference< XPropertySet > xModel( xControl->getModel(), UNO_QUERY ); 3290 if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel)) 3291 { 3292 // does the model use a bound field ? 3293 Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD); 3294 Reference< XPropertySet > xField; 3295 aVal >>= xField; 3296 3297 // may we filter the field? 3298 3299 if ( xField.is() 3300 && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField ) 3301 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) ) 3302 ) 3303 { 3304 // create a filter control 3305 Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat( 3306 m_xComponentContext, 3307 VCLUnoHelper::GetInterface( getDialogParentWindow() ), 3308 xFormatter, 3309 xModel); 3310 3311 if ( replaceControl( xControl, xFilterControl ) ) 3312 { 3313 Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY ); 3314 aFieldInfos.emplace_back( xField, xFilterText ); 3315 xFilterText->addTextListener(this); 3316 } 3317 } 3318 } 3319 else 3320 { 3321 // unsubscribe from EventManager 3322 } 3323 } 3324 } 3325 3326 // we have all filter controls now, so the next step is to read the filters from the form 3327 // resolve all aliases and set the current filter to the according structure 3328 setFilter(aFieldInfos); 3329 3330 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY ); 3331 if ( xSet.is() ) 3332 stopFormListening( xSet, true ); 3333 3334 impl_setTextOnAllFilter_throw(); 3335 3336 // lock all controls which are not used for filtering 3337 m_bLocked = determineLockState(); 3338 setLocks(); 3339 m_bAttachEvents = true; 3340 } 3341 3342 3343 void FormController::stopFiltering() 3344 { 3345 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 3346 if ( !m_bFiltering ) // #104693# OJ 3347 { // nothing to do 3348 return; 3349 } 3350 3351 m_bFiltering = false; 3352 m_bDetachEvents = false; 3353 3354 ::comphelper::disposeComponent(m_xComposer); 3355 3356 // exchanging the controls for the current form 3357 Sequence< Reference< XControl > > aControlsCopy( m_aControls ); 3358 const Reference< XControl > * pControls = aControlsCopy.getConstArray(); 3359 sal_Int32 nControlCount = aControlsCopy.getLength(); 3360 3361 // clear the filter control map 3362 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) ); 3363 m_aFilterComponents.clear(); 3364 3365 for ( sal_Int32 i = nControlCount; i > 0; ) 3366 { 3367 Reference< XControl > xControl = pControls[--i]; 3368 if (xControl.is()) 3369 { 3370 // now enable event handling again 3371 addToEventAttacher(xControl); 3372 3373 Reference< XModeSelector > xSelector(xControl, UNO_QUERY); 3374 if (xSelector.is()) 3375 { 3376 xSelector->setMode( "DataMode" ); 3377 3378 // listening for new controls of the selector 3379 Reference< XContainer > xContainer(xSelector, UNO_QUERY); 3380 if (xContainer.is()) 3381 xContainer->removeContainerListener(this); 3382 continue; 3383 } 3384 3385 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY); 3386 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)) 3387 { 3388 // does the model use a bound field ? 3389 Reference< XPropertySet > xField; 3390 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField; 3391 3392 // may we filter the field? 3393 if ( xField.is() 3394 && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField ) 3395 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) ) 3396 ) 3397 { 3398 OUString sServiceName; 3399 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName ); 3400 Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY ); 3401 replaceControl( xControl, xNewControl ); 3402 } 3403 } 3404 } 3405 } 3406 3407 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY ); 3408 if ( xSet.is() ) 3409 startFormListening( xSet, true ); 3410 3411 m_bDetachEvents = true; 3412 3413 m_aFilterRows.clear(); 3414 m_nCurrentFilterPosition = -1; 3415 3416 // release the locks if possible 3417 // lock all controls which are not used for filtering 3418 m_bLocked = determineLockState(); 3419 setLocks(); 3420 3421 // restart listening for control modifications 3422 if (isListeningForChanges()) 3423 startListening(); 3424 } 3425 3426 // XModeSelector 3427 3428 void FormController::setMode(const OUString& Mode) 3429 { 3430 ::osl::MutexGuard aGuard( m_aMutex ); 3431 impl_checkDisposed_throw(); 3432 3433 if (!supportsMode(Mode)) 3434 throw NoSupportException(); 3435 3436 if (Mode == m_aMode) 3437 return; 3438 3439 m_aMode = Mode; 3440 3441 if ( Mode == "FilterMode" ) 3442 startFiltering(); 3443 else 3444 stopFiltering(); 3445 3446 for (const auto& rChild : m_aChildren) 3447 { 3448 Reference< XModeSelector > xMode(rChild, UNO_QUERY); 3449 if ( xMode.is() ) 3450 xMode->setMode(Mode); 3451 } 3452 } 3453 3454 3455 OUString SAL_CALL FormController::getMode() 3456 { 3457 ::osl::MutexGuard aGuard( m_aMutex ); 3458 impl_checkDisposed_throw(); 3459 3460 return m_aMode; 3461 } 3462 3463 3464 Sequence< OUString > SAL_CALL FormController::getSupportedModes() 3465 { 3466 ::osl::MutexGuard aGuard( m_aMutex ); 3467 impl_checkDisposed_throw(); 3468 3469 static Sequence< OUString > const aModes 3470 { 3471 "DataMode", 3472 "FilterMode" 3473 }; 3474 return aModes; 3475 } 3476 3477 3478 sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode) 3479 { 3480 ::osl::MutexGuard aGuard( m_aMutex ); 3481 impl_checkDisposed_throw(); 3482 3483 Sequence< OUString > aModes(getSupportedModes()); 3484 const OUString* pModes = aModes.getConstArray(); 3485 for (sal_Int32 i = aModes.getLength(); i > 0; ) 3486 { 3487 if (pModes[--i] == Mode) 3488 return true; 3489 } 3490 return false; 3491 } 3492 3493 3494 vcl::Window* FormController::getDialogParentWindow() 3495 { 3496 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 3497 vcl::Window* pParentWindow = nullptr; 3498 try 3499 { 3500 Reference< XControl > xContainerControl( getContainer(), UNO_QUERY_THROW ); 3501 Reference< XWindowPeer > xContainerPeer( xContainerControl->getPeer(), UNO_QUERY_THROW ); 3502 pParentWindow = VCLUnoHelper::GetWindow( xContainerPeer ).get(); 3503 } 3504 catch( const Exception& ) 3505 { 3506 DBG_UNHANDLED_EXCEPTION("svx"); 3507 } 3508 return pParentWindow; 3509 } 3510 3511 bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel ) 3512 { 3513 try 3514 { 3515 Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY ); 3516 Reference< XEnumeration > xControlEnumeration; 3517 if ( xControlEnumAcc.is() ) 3518 xControlEnumeration = xControlEnumAcc->createEnumeration(); 3519 OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" ); 3520 if ( !xControlEnumeration.is() ) 3521 // assume all valid 3522 return true; 3523 3524 Reference< XValidatableFormComponent > xValidatable; 3525 while ( xControlEnumeration->hasMoreElements() ) 3526 { 3527 if ( !( xControlEnumeration->nextElement() >>= xValidatable ) ) 3528 // control does not support validation 3529 continue; 3530 3531 if ( xValidatable->isValid() ) 3532 continue; 3533 3534 Reference< XValidator > xValidator( xValidatable->getValidator() ); 3535 OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" ); 3536 if ( !xValidator.is() ) 3537 // this violates the interface definition of css.form.validation.XValidatableFormComponent ... 3538 continue; 3539 3540 _rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() ); 3541 _rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY); 3542 return false; 3543 } 3544 } 3545 catch( const Exception& ) 3546 { 3547 DBG_UNHANDLED_EXCEPTION("svx"); 3548 } 3549 return true; 3550 } 3551 3552 3553 Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel ) 3554 { 3555 try 3556 { 3557 Sequence< Reference< XControl > > aControls( getControls() ); 3558 3559 for ( auto const & control : aControls ) 3560 { 3561 OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" ); 3562 if ( control.is() ) 3563 { 3564 if ( control->getModel() == _rxModel ) 3565 return control; 3566 } 3567 } 3568 OSL_FAIL( "FormController::locateControl: did not find a control for this model!" ); 3569 } 3570 catch( const Exception& ) 3571 { 3572 DBG_UNHANDLED_EXCEPTION("svx"); 3573 } 3574 return nullptr; 3575 } 3576 3577 3578 namespace 3579 { 3580 void displayErrorSetFocus( const OUString& _rMessage, const Reference< XControl >& _rxFocusControl, vcl::Window* _pDialogParent ) 3581 { 3582 SQLContext aError; 3583 aError.Message = SvxResId(RID_STR_WRITEERROR); 3584 aError.Details = _rMessage; 3585 displayException( aError, _pDialogParent ); 3586 3587 if ( _rxFocusControl.is() ) 3588 { 3589 Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY ); 3590 OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" ); 3591 if ( xControlWindow.is() ) 3592 xControlWindow->setFocus(); 3593 } 3594 } 3595 3596 bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm ) 3597 { 3598 try 3599 { 3600 static const char s_sFormsCheckRequiredFields[] = "FormsCheckRequiredFields"; 3601 3602 // first, check whether the form has a property telling us the answer 3603 // this allows people to use the XPropertyContainer interface of a form to control 3604 // the behaviour on a per-form basis. 3605 Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW ); 3606 Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() ); 3607 if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) ) 3608 { 3609 bool bShouldValidate = true; 3610 OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate ); 3611 return bShouldValidate; 3612 } 3613 3614 // next, check the data source which created the connection 3615 Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW ); 3616 Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY ); 3617 if ( !xDataSource.is() ) 3618 // seldom (but possible): this is not a connection created by a data source 3619 return true; 3620 3621 Reference< XPropertySet > xDataSourceSettings( 3622 xDataSource->getPropertyValue("Settings"), 3623 UNO_QUERY_THROW ); 3624 3625 bool bShouldValidate = true; 3626 OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate ); 3627 return bShouldValidate; 3628 } 3629 catch( const Exception& ) 3630 { 3631 DBG_UNHANDLED_EXCEPTION("svx"); 3632 } 3633 3634 return true; 3635 } 3636 } 3637 3638 // XRowSetApproveListener 3639 3640 sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent) 3641 { 3642 ::osl::ClearableMutexGuard aGuard( m_aMutex ); 3643 impl_checkDisposed_throw(); 3644 3645 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners); 3646 bool bValid = true; 3647 if (aIter.hasMoreElements()) 3648 { 3649 RowChangeEvent aEvt( _rEvent ); 3650 aEvt.Source = *this; 3651 bValid = static_cast<XRowSetApproveListener*>(aIter.next())->approveRowChange(aEvt); 3652 } 3653 3654 if ( !bValid ) 3655 return bValid; 3656 3657 if ( ( _rEvent.Action != RowChangeAction::INSERT ) 3658 && ( _rEvent.Action != RowChangeAction::UPDATE ) 3659 ) 3660 return bValid; 3661 3662 // if some of the control models are bound to validators, check them 3663 OUString sInvalidityExplanation; 3664 Reference< XControlModel > xInvalidModel; 3665 if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) ) 3666 { 3667 Reference< XControl > xControl( locateControl( xInvalidModel ) ); 3668 aGuard.clear(); 3669 displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow() ); 3670 return false; 3671 } 3672 3673 // check values on NULL and required flag 3674 if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) ) 3675 return true; 3676 3677 OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!"); 3678 if (!m_pColumnInfoCache) 3679 return true; 3680 3681 try 3682 { 3683 if ( !m_pColumnInfoCache->controlsInitialized() ) 3684 m_pColumnInfoCache->initializeControls( getControls() ); 3685 3686 size_t colCount = m_pColumnInfoCache->getColumnCount(); 3687 for ( size_t col = 0; col < colCount; ++col ) 3688 { 3689 const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col ); 3690 3691 if ( rColInfo.bAutoIncrement ) 3692 continue; 3693 3694 if ( rColInfo.bReadOnly ) 3695 continue; 3696 3697 if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() ) 3698 { 3699 continue; 3700 } 3701 3702 // TODO: in case of binary fields, this "getString" below is extremely expensive 3703 if ( !rColInfo.xColumn->wasNull() || !rColInfo.xColumn->getString().isEmpty() ) 3704 continue; 3705 3706 OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) ); 3707 sMessage = sMessage.replaceFirst( "#", rColInfo.sName ); 3708 3709 // the control to focus 3710 Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired ); 3711 if ( !xControl.is() ) 3712 xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY ); 3713 3714 aGuard.clear(); 3715 displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow() ); 3716 return false; 3717 } 3718 } 3719 catch( const Exception& ) 3720 { 3721 DBG_UNHANDLED_EXCEPTION("svx"); 3722 } 3723 3724 return true; 3725 } 3726 3727 3728 sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event) 3729 { 3730 ::osl::MutexGuard aGuard( m_aMutex ); 3731 impl_checkDisposed_throw(); 3732 3733 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners); 3734 if (aIter.hasMoreElements()) 3735 { 3736 EventObject aEvt(event); 3737 aEvt.Source = *this; 3738 return static_cast<XRowSetApproveListener*>(aIter.next())->approveCursorMove(aEvt); 3739 } 3740 3741 return true; 3742 } 3743 3744 3745 sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event) 3746 { 3747 ::osl::MutexGuard aGuard( m_aMutex ); 3748 impl_checkDisposed_throw(); 3749 3750 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners); 3751 if (aIter.hasMoreElements()) 3752 { 3753 EventObject aEvt(event); 3754 aEvt.Source = *this; 3755 return static_cast<XRowSetApproveListener*>(aIter.next())->approveRowSetChange(aEvt); 3756 } 3757 3758 return true; 3759 } 3760 3761 // XRowSetApproveBroadcaster 3762 3763 void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener) 3764 { 3765 ::osl::MutexGuard aGuard( m_aMutex ); 3766 impl_checkDisposed_throw(); 3767 3768 m_aRowSetApproveListeners.addInterface(_rxListener); 3769 } 3770 3771 3772 void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener) 3773 { 3774 ::osl::MutexGuard aGuard( m_aMutex ); 3775 impl_checkDisposed_throw(); 3776 3777 m_aRowSetApproveListeners.removeInterface(_rxListener); 3778 } 3779 3780 // XErrorListener 3781 3782 void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent) 3783 { 3784 ::osl::ClearableMutexGuard aGuard( m_aMutex ); 3785 impl_checkDisposed_throw(); 3786 3787 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aErrorListeners); 3788 if (aIter.hasMoreElements()) 3789 { 3790 SQLErrorEvent aEvt(aEvent); 3791 aEvt.Source = *this; 3792 static_cast<XSQLErrorListener*>(aIter.next())->errorOccured(aEvt); 3793 } 3794 else 3795 { 3796 aGuard.clear(); 3797 displayException(aEvent, getDialogParentWindow()); 3798 } 3799 } 3800 3801 // XErrorBroadcaster 3802 3803 void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener) 3804 { 3805 ::osl::MutexGuard aGuard( m_aMutex ); 3806 impl_checkDisposed_throw(); 3807 3808 m_aErrorListeners.addInterface(aListener); 3809 } 3810 3811 3812 void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener) 3813 { 3814 ::osl::MutexGuard aGuard( m_aMutex ); 3815 impl_checkDisposed_throw(); 3816 3817 m_aErrorListeners.removeInterface(aListener); 3818 } 3819 3820 // XDatabaseParameterBroadcaster2 3821 3822 void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener) 3823 { 3824 ::osl::MutexGuard aGuard( m_aMutex ); 3825 impl_checkDisposed_throw(); 3826 3827 m_aParameterListeners.addInterface(aListener); 3828 } 3829 3830 3831 void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener) 3832 { 3833 ::osl::MutexGuard aGuard( m_aMutex ); 3834 impl_checkDisposed_throw(); 3835 3836 m_aParameterListeners.removeInterface(aListener); 3837 } 3838 3839 // XDatabaseParameterBroadcaster 3840 3841 void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener) 3842 { 3843 FormController::addDatabaseParameterListener( aListener ); 3844 } 3845 3846 3847 void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener) 3848 { 3849 FormController::removeDatabaseParameterListener( aListener ); 3850 } 3851 3852 // XDatabaseParameterListener 3853 3854 sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent) 3855 { 3856 SolarMutexGuard aSolarGuard; 3857 ::osl::MutexGuard aGuard( m_aMutex ); 3858 impl_checkDisposed_throw(); 3859 3860 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aParameterListeners); 3861 if (aIter.hasMoreElements()) 3862 { 3863 DatabaseParameterEvent aEvt(aEvent); 3864 aEvt.Source = *this; 3865 return static_cast<XDatabaseParameterListener*>(aIter.next())->approveParameter(aEvt); 3866 } 3867 else 3868 { 3869 // default handling: instantiate an interaction handler and let it handle the parameter request 3870 try 3871 { 3872 if ( !ensureInteractionHandler() ) 3873 return false; 3874 3875 // two continuations allowed: OK and Cancel 3876 OParameterContinuation* pParamValues = new OParameterContinuation; 3877 OInteractionAbort* pAbort = new OInteractionAbort; 3878 // the request 3879 ParametersRequest aRequest; 3880 aRequest.Parameters = aEvent.Parameters; 3881 aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY)); 3882 OInteractionRequest* pParamRequest = new OInteractionRequest(makeAny(aRequest)); 3883 Reference< XInteractionRequest > xParamRequest(pParamRequest); 3884 // some knittings 3885 pParamRequest->addContinuation(pParamValues); 3886 pParamRequest->addContinuation(pAbort); 3887 3888 // handle the request 3889 m_xInteractionHandler->handle(xParamRequest); 3890 3891 if (!pParamValues->wasSelected()) 3892 // canceled 3893 return false; 3894 3895 // transfer the values into the parameter supplier 3896 Sequence< PropertyValue > aFinalValues = pParamValues->getValues(); 3897 if (aFinalValues.getLength() != aRequest.Parameters->getCount()) 3898 { 3899 OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!"); 3900 return false; 3901 } 3902 const PropertyValue* pFinalValues = aFinalValues.getConstArray(); 3903 for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues) 3904 { 3905 Reference< XPropertySet > xParam( 3906 aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY); 3907 if (xParam.is()) 3908 { 3909 #ifdef DBG_UTIL 3910 OUString sName; 3911 xParam->getPropertyValue(FM_PROP_NAME) >>= sName; 3912 DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!"); 3913 #endif 3914 try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); } 3915 catch(Exception&) 3916 { 3917 OSL_FAIL("FormController::approveParameter: setting one of the properties failed!"); 3918 } 3919 } 3920 } 3921 } 3922 catch(Exception&) 3923 { 3924 DBG_UNHANDLED_EXCEPTION("svx"); 3925 } 3926 } 3927 return true; 3928 } 3929 3930 // XConfirmDeleteBroadcaster 3931 3932 void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener) 3933 { 3934 ::osl::MutexGuard aGuard( m_aMutex ); 3935 impl_checkDisposed_throw(); 3936 3937 m_aDeleteListeners.addInterface(aListener); 3938 } 3939 3940 3941 void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener) 3942 { 3943 ::osl::MutexGuard aGuard( m_aMutex ); 3944 impl_checkDisposed_throw(); 3945 3946 m_aDeleteListeners.removeInterface(aListener); 3947 } 3948 3949 // XConfirmDeleteListener 3950 3951 sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent) 3952 { 3953 ::osl::MutexGuard aGuard( m_aMutex ); 3954 impl_checkDisposed_throw(); 3955 3956 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aDeleteListeners); 3957 if (aIter.hasMoreElements()) 3958 { 3959 RowChangeEvent aEvt(aEvent); 3960 aEvt.Source = *this; 3961 return static_cast<XConfirmDeleteListener*>(aIter.next())->confirmDelete(aEvt); 3962 } 3963 // default handling: instantiate an interaction handler and let it handle the request 3964 3965 OUString sTitle; 3966 sal_Int32 nLength = aEvent.Rows; 3967 if ( nLength > 1 ) 3968 { 3969 sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS ); 3970 sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) ); 3971 } 3972 else 3973 sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD ); 3974 3975 try 3976 { 3977 if ( !ensureInteractionHandler() ) 3978 return false; 3979 3980 // two continuations allowed: Yes and No 3981 OInteractionApprove* pApprove = new OInteractionApprove; 3982 OInteractionDisapprove* pDisapprove = new OInteractionDisapprove; 3983 3984 // the request 3985 SQLWarning aWarning; 3986 aWarning.Message = sTitle; 3987 SQLWarning aDetails; 3988 aDetails.Message = SvxResId(RID_STR_DELETECONFIRM); 3989 aWarning.NextException <<= aDetails; 3990 3991 OInteractionRequest* pRequest = new OInteractionRequest( makeAny( aWarning ) ); 3992 Reference< XInteractionRequest > xRequest( pRequest ); 3993 3994 // some knittings 3995 pRequest->addContinuation( pApprove ); 3996 pRequest->addContinuation( pDisapprove ); 3997 3998 // handle the request 3999 m_xInteractionHandler->handle( xRequest ); 4000 4001 if ( pApprove->wasSelected() ) 4002 return true; 4003 } 4004 catch( const Exception& ) 4005 { 4006 DBG_UNHANDLED_EXCEPTION("svx"); 4007 } 4008 4009 return false; 4010 } 4011 4012 4013 void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features ) 4014 { 4015 ::osl::MutexGuard aGuard( m_aMutex ); 4016 // for now, just copy the ids of the features, because .... 4017 ::std::copy( Features.begin(), Features.end(), 4018 ::std::insert_iterator< ::std::set< sal_Int16 > >( m_aInvalidFeatures, m_aInvalidFeatures.begin() ) 4019 ); 4020 4021 // ... we will do the real invalidation asynchronously 4022 if ( !m_aFeatureInvalidationTimer.IsActive() ) 4023 m_aFeatureInvalidationTimer.Start(); 4024 } 4025 4026 4027 void SAL_CALL FormController::invalidateAllFeatures( ) 4028 { 4029 ::osl::ClearableMutexGuard aGuard( m_aMutex ); 4030 4031 Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) ); 4032 4033 aGuard.clear(); 4034 if ( aInterceptedFeatures.getLength() ) 4035 invalidateFeatures( aInterceptedFeatures ); 4036 } 4037 4038 4039 Reference< XDispatch > 4040 FormController::interceptedQueryDispatch( const URL& aURL, 4041 const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/) 4042 { 4043 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 4044 Reference< XDispatch > xReturn; 4045 // dispatches handled by ourself 4046 if ( ( aURL.Complete == FMURL_CONFIRM_DELETION ) 4047 || ( ( aURL.Complete == "private:/InteractionHandler" ) 4048 && ensureInteractionHandler() 4049 ) 4050 ) 4051 xReturn = static_cast< XDispatch* >( this ); 4052 4053 // dispatches of FormSlot-URLs we have to translate 4054 if ( !xReturn.is() && m_xFormOperations.is() ) 4055 { 4056 // find the slot id which corresponds to the URL 4057 sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main ); 4058 sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1; 4059 if ( nFormFeature > 0 ) 4060 { 4061 // get the dispatcher for this feature, create if necessary 4062 DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature ); 4063 if ( aDispatcherPos == m_aFeatureDispatchers.end() ) 4064 { 4065 aDispatcherPos = m_aFeatureDispatchers.emplace( 4066 nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex ) 4067 ).first; 4068 } 4069 4070 OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" ); 4071 return aDispatcherPos->second; 4072 } 4073 } 4074 4075 // no more to offer 4076 return xReturn; 4077 } 4078 4079 4080 void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs ) 4081 { 4082 if ( _rArgs.getLength() != 1 ) 4083 { 4084 OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" ); 4085 return; 4086 } 4087 4088 if ( _rURL.Complete == "private:/InteractionHandler" ) 4089 { 4090 Reference< XInteractionRequest > xRequest; 4091 OSL_VERIFY( _rArgs[0].Value >>= xRequest ); 4092 if ( xRequest.is() ) 4093 handle( xRequest ); 4094 return; 4095 } 4096 4097 if ( _rURL.Complete == FMURL_CONFIRM_DELETION ) 4098 { 4099 OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" ); 4100 // confirmDelete has a return value - dispatch hasn't 4101 return; 4102 } 4103 4104 OSL_FAIL( "FormController::dispatch: unknown URL!" ); 4105 } 4106 4107 4108 void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL ) 4109 { 4110 if (_rURL.Complete == FMURL_CONFIRM_DELETION) 4111 { 4112 if (_rxListener.is()) 4113 { // send an initial statusChanged event 4114 FeatureStateEvent aEvent; 4115 aEvent.FeatureURL = _rURL; 4116 aEvent.IsEnabled = true; 4117 _rxListener->statusChanged(aEvent); 4118 // and don't add the listener at all (the status will never change) 4119 } 4120 } 4121 else 4122 OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!"); 4123 } 4124 4125 4126 Reference< XInterface > SAL_CALL FormController::getParent() 4127 { 4128 return m_xParent; 4129 } 4130 4131 4132 void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent) 4133 { 4134 m_xParent = Parent; 4135 } 4136 4137 4138 void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL ) 4139 { 4140 OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!"); 4141 // we never really added the listener, so we don't need to remove it 4142 } 4143 4144 4145 Reference< XDispatchProviderInterceptor > FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception) 4146 { 4147 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 4148 #ifdef DBG_UTIL 4149 // check if we already have a interceptor for the given object 4150 for ( const auto & it : m_aControlDispatchInterceptors ) 4151 { 4152 if (it->getIntercepted() == _xInterception) 4153 OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !"); 4154 } 4155 #endif 4156 4157 rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this )); 4158 m_aControlDispatchInterceptors.push_back( pInterceptor ); 4159 4160 return pInterceptor.get(); 4161 } 4162 4163 4164 bool FormController::ensureInteractionHandler() 4165 { 4166 if ( m_xInteractionHandler.is() ) 4167 return true; 4168 if ( m_bAttemptedHandlerCreation ) 4169 return false; 4170 m_bAttemptedHandlerCreation = true; 4171 4172 m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext, 4173 VCLUnoHelper::GetInterface(getDialogParentWindow())); 4174 return m_xInteractionHandler.is(); 4175 } 4176 4177 4178 void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest ) 4179 { 4180 if ( !ensureInteractionHandler() ) 4181 return; 4182 m_xInteractionHandler->handle( _rRequest ); 4183 } 4184 4185 4186 void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception) 4187 { 4188 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); 4189 // search the interceptor responsible for the given object 4190 auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(), 4191 [&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) { 4192 return rpInterceptor->getIntercepted() == _xInterception; 4193 }); 4194 if (aIter != m_aControlDispatchInterceptors.end()) 4195 { 4196 // log off the interception from its interception object 4197 (*aIter)->dispose(); 4198 // remove the interceptor from our array 4199 m_aControlDispatchInterceptors.erase(aIter); 4200 } 4201 } 4202 4203 4204 void FormController::implInvalidateCurrentControlDependentFeatures() 4205 { 4206 Sequence< sal_Int16 > aCurrentControlDependentFeatures(4); 4207 4208 aCurrentControlDependentFeatures[0] = FormFeature::SortAscending; 4209 aCurrentControlDependentFeatures[1] = FormFeature::SortDescending; 4210 aCurrentControlDependentFeatures[2] = FormFeature::AutoFilter; 4211 aCurrentControlDependentFeatures[3] = FormFeature::RefreshCurrentControl; 4212 4213 invalidateFeatures( aCurrentControlDependentFeatures ); 4214 } 4215 4216 4217 void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ ) 4218 { 4219 implInvalidateCurrentControlDependentFeatures(); 4220 } 4221 4222 } 4223 4224 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 4225
