1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_forms.hxx" 30 31 #include "binding.hxx" 32 33 #include "model.hxx" 34 #include "unohelper.hxx" 35 #include "NameContainer.hxx" 36 #include "evaluationcontext.hxx" 37 #include "convert.hxx" 38 #include "resourcehelper.hxx" 39 #include "xmlhelper.hxx" 40 #include "xformsevent.hxx" 41 42 #include <rtl/ustrbuf.hxx> 43 #include <osl/diagnose.h> 44 45 #include <tools/diagnose_ex.h> 46 47 #include <algorithm> 48 #include <functional> 49 50 #include <com/sun/star/uno/Any.hxx> 51 #include <com/sun/star/xml/dom/XNodeList.hpp> 52 #include <com/sun/star/xml/dom/XNode.hpp> 53 #include <com/sun/star/xml/dom/XDocument.hpp> 54 #include <com/sun/star/xml/dom/XElement.hpp> 55 #include <com/sun/star/xml/dom/NodeType.hpp> 56 #include <com/sun/star/xml/dom/events/XEventTarget.hpp> 57 #include <com/sun/star/xml/dom/events/XEventListener.hpp> 58 #include <com/sun/star/xml/dom/events/XDocumentEvent.hpp> 59 #include <com/sun/star/lang/XUnoTunnel.hpp> 60 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> 61 #include <com/sun/star/container/XSet.hpp> 62 #include <com/sun/star/container/XNameContainer.hpp> 63 64 #include <comphelper/propertysetinfo.hxx> 65 #include <unotools/textsearch.hxx> 66 #include <cppuhelper/typeprovider.hxx> 67 68 using namespace com::sun::star::xml::xpath; 69 using namespace com::sun::star::xml::dom::events; 70 71 using rtl::OUString; 72 using rtl::OUStringBuffer; 73 using std::vector; 74 using xforms::Binding; 75 using xforms::MIP; 76 using xforms::Model; 77 using xforms::getResource; 78 using xforms::EvaluationContext; 79 using com::sun::star::beans::PropertyVetoException; 80 using com::sun::star::beans::UnknownPropertyException; 81 using com::sun::star::beans::XPropertySet; 82 using com::sun::star::container::XSet; 83 using com::sun::star::container::XNameAccess; 84 using com::sun::star::form::binding::IncompatibleTypesException; 85 using com::sun::star::form::binding::InvalidBindingStateException; 86 using com::sun::star::form::binding::XValueBinding; 87 using com::sun::star::lang::EventObject; 88 using com::sun::star::lang::IllegalArgumentException; 89 using com::sun::star::lang::IndexOutOfBoundsException; 90 using com::sun::star::lang::NoSupportException; 91 using com::sun::star::lang::NullPointerException; 92 using com::sun::star::lang::WrappedTargetException; 93 using com::sun::star::lang::XUnoTunnel; 94 using com::sun::star::uno::Any; 95 using com::sun::star::uno::Reference; 96 using com::sun::star::uno::RuntimeException; 97 using com::sun::star::uno::Sequence; 98 using com::sun::star::uno::UNO_QUERY; 99 using com::sun::star::uno::UNO_QUERY_THROW; 100 using com::sun::star::uno::XInterface; 101 using com::sun::star::uno::Exception; 102 using com::sun::star::uno::makeAny; 103 using com::sun::star::util::XModifyListener; 104 using com::sun::star::xforms::XDataTypeRepository; 105 using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE; 106 using com::sun::star::xml::dom::NodeType_TEXT_NODE; 107 using com::sun::star::xml::dom::XNode; 108 using com::sun::star::xml::dom::XNodeList; 109 using com::sun::star::xml::dom::events::XEventListener; 110 using com::sun::star::xml::dom::events::XEventTarget; 111 using com::sun::star::xsd::XDataType; 112 113 114 115 116 #define EXCEPT(msg) OUSTRING(msg),static_cast<XValueBinding*>(this) 117 118 #define HANDLE_BindingID 0 119 #define HANDLE_BindingExpression 1 120 #define HANDLE_Model 2 121 #define HANDLE_ModelID 3 122 #define HANDLE_BindingNamespaces 4 123 #define HANDLE_ReadonlyExpression 5 124 #define HANDLE_RelevantExpression 6 125 #define HANDLE_RequiredExpression 7 126 #define HANDLE_ConstraintExpression 8 127 #define HANDLE_CalculateExpression 9 128 #define HANDLE_Type 10 129 #define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control 130 #define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control 131 #define HANDLE_ModelNamespaces 13 132 #define HANDLE_ExternalData 14 133 134 135 Binding::Binding() : 136 mxModel(), 137 msBindingID(), 138 maBindingExpression(), 139 maReadonly(), 140 mxNamespaces( new NameContainer<OUString>() ), 141 mbInCalculate( false ), 142 mnDeferModifyNotifications( 0 ), 143 mbValueModified( false ), 144 mbBindingModified( false ) 145 146 { 147 initializePropertySet(); 148 } 149 150 Binding::~Binding() throw() 151 { 152 _setModel(NULL); 153 } 154 155 156 Binding::Model_t Binding::getModel() const 157 { 158 return mxModel; 159 } 160 161 void Binding::_setModel( const Model_t& xModel ) 162 { 163 PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model ); 164 PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID ); 165 166 // prepare binding for removal of old model 167 clear(); // remove all cached data (e.g. XPath evaluation results) 168 XNameContainer_t xNamespaces = getModelNamespaces(); // save namespaces 169 170 mxModel = xModel; 171 172 // set namespaces (and move to model, if appropriate) 173 setBindingNamespaces( xNamespaces ); 174 _checkBindingID(); 175 176 notifyAndCachePropertyValue( HANDLE_ExternalData ); 177 } 178 179 180 OUString Binding::getModelID() const 181 { 182 Model* pModel = getModelImpl(); 183 return ( pModel == NULL ) ? OUString() : pModel->getID(); 184 } 185 186 187 Binding::XNodeList_t Binding::getXNodeList() 188 { 189 // first make sure we are bound 190 if( ! maBindingExpression.hasValue() ) 191 bind( sal_False ); 192 193 return maBindingExpression.getXNodeList(); 194 } 195 196 bool Binding::isSimpleBinding() const 197 { 198 return maBindingExpression.isSimpleExpression() 199 && maReadonly.isSimpleExpression() 200 && maRelevant.isSimpleExpression() 201 && maRequired.isSimpleExpression() 202 && maConstraint.isSimpleExpression() 203 && maCalculate.isSimpleExpression(); 204 } 205 206 bool Binding::isSimpleBindingExpression() const 207 { 208 return maBindingExpression.isSimpleExpression(); 209 } 210 211 void Binding::update() 212 { 213 // clear all expressions (to remove cached node references) 214 maBindingExpression.clear(); 215 maReadonly.clear(); 216 maRelevant.clear(); 217 maRequired.clear(); 218 maConstraint.clear(); 219 maCalculate.clear(); 220 221 // let's just pretend the binding has been modified -> full rebind() 222 bindingModified(); 223 } 224 225 void Binding::deferNotifications( bool bDefer ) 226 { 227 mnDeferModifyNotifications += ( bDefer ? 1 : -1 ); 228 OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" ); 229 230 if( mnDeferModifyNotifications == 0 ) 231 { 232 if( mbBindingModified ) 233 bindingModified(); 234 if( mbValueModified ) 235 valueModified(); 236 } 237 238 OSL_ENSURE( ( mnDeferModifyNotifications > 0 ) 239 || ( ! mbBindingModified && ! mbValueModified ), 240 "deferred modifications not delivered?" ); 241 } 242 243 bool Binding::isValid() 244 { 245 // TODO: determine whether node is suitable, not just whether it exists 246 return maBindingExpression.getNode().is() && 247 isValid_DataType() && 248 maMIP.isConstraint() && 249 ( ! maMIP.isRequired() || 250 ( maBindingExpression.hasValue() && 251 maBindingExpression.getString().getLength() > 0 ) ); 252 } 253 254 bool Binding::isUseful() 255 { 256 // we are useful, if 257 // 0) we don't have a model 258 // (at least, in this case we shouldn't be removed from the model) 259 // 1) we have a proper name 260 // 2) we have some MIPs, 261 // 3) we are bound to some control 262 // (this can be assumed if some listeners are set) 263 bool bUseful = 264 getModelImpl() == NULL 265 // || msBindingID.getLength() > 0 266 || msTypeName.getLength() > 0 267 || ! maReadonly.isEmptyExpression() 268 || ! maRelevant.isEmptyExpression() 269 || ! maRequired.isEmptyExpression() 270 || ! maConstraint.isEmptyExpression() 271 || ! maCalculate.isEmptyExpression() 272 || ! maModifyListeners.empty() 273 || ! maListEntryListeners.empty() 274 || ! maValidityListeners.empty(); 275 276 return bUseful; 277 } 278 279 OUString Binding::explainInvalid() 280 { 281 OUString sReason; 282 if( ! maBindingExpression.getNode().is() ) 283 { 284 sReason = ( maBindingExpression.getExpression().getLength() == 0 ) 285 ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION ) 286 : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION ); 287 } 288 else if( ! isValid_DataType() ) 289 { 290 sReason = explainInvalid_DataType(); 291 if( sReason.getLength() == 0 ) 292 { 293 // no explanation given by data type? Then give generic message 294 sReason = getResource( RID_STR_XFORMS_INVALID_VALUE, 295 maMIP.getTypeName() ); 296 } 297 } 298 else if( ! maMIP.isConstraint() ) 299 { 300 sReason = maMIP.getConstraintExplanation(); 301 } 302 else if( maMIP.isRequired() && maBindingExpression.hasValue() && 303 ( maBindingExpression.getString().getLength() == 0 ) ) 304 { 305 sReason = getResource( RID_STR_XFORMS_REQUIRED ); 306 } 307 // else: no explanation given; should only happen if data is valid 308 309 OSL_ENSURE( ( sReason.getLength() == 0 ) == isValid(), 310 "invalid data should have an explanation!" ); 311 312 return sReason; 313 } 314 315 316 317 EvaluationContext Binding::getEvaluationContext() const 318 { 319 OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); 320 EvaluationContext aContext = getModelImpl()->getEvaluationContext(); 321 aContext.mxNamespaces = getBindingNamespaces(); 322 return aContext; 323 } 324 325 ::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts() 326 { 327 OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); 328 329 // bind (in case we were not bound before) 330 bind( sal_False ); 331 return _getMIPEvaluationContexts(); 332 } 333 334 335 Binding::IntSequence_t Binding::getUnoTunnelID() 336 { 337 static cppu::OImplementationId aImplementationId; 338 return aImplementationId.getImplementationId(); 339 } 340 341 Binding* SAL_CALL Binding::getBinding( const Reference<XPropertySet>& xPropertySet ) 342 { 343 Reference<XUnoTunnel> xTunnel( xPropertySet, UNO_QUERY ); 344 return xTunnel.is() 345 ? reinterpret_cast<Binding*>( xTunnel->getSomething(getUnoTunnelID())) 346 : NULL; 347 } 348 349 350 351 352 OUString Binding::getBindingID() const 353 { 354 return msBindingID; 355 } 356 357 void Binding::setBindingID( const OUString& sBindingID ) 358 { 359 msBindingID = sBindingID; 360 } 361 362 OUString Binding::getBindingExpression() const 363 { 364 return maBindingExpression.getExpression(); 365 } 366 367 void Binding::setBindingExpression( const OUString& sBindingExpression) 368 { 369 maBindingExpression.setExpression( sBindingExpression ); 370 bindingModified(); 371 } 372 373 OUString Binding::getReadonlyExpression() const 374 { 375 return maReadonly.getExpression(); 376 } 377 378 void Binding::setReadonlyExpression( const OUString& sReadonly) 379 { 380 maReadonly.setExpression( sReadonly ); 381 bindingModified(); 382 } 383 384 OUString Binding::getRelevantExpression() const 385 { 386 return maRelevant.getExpression(); 387 } 388 389 void Binding::setRelevantExpression( const OUString& sRelevant ) 390 { 391 maRelevant.setExpression( sRelevant ); 392 bindingModified(); 393 } 394 395 OUString Binding::getRequiredExpression() const 396 { 397 return maRequired.getExpression(); 398 } 399 400 void Binding::setRequiredExpression( const OUString& sRequired ) 401 { 402 maRequired.setExpression( sRequired ); 403 bindingModified(); 404 } 405 406 OUString Binding::getConstraintExpression() const 407 { 408 return maConstraint.getExpression(); 409 } 410 411 void Binding::setConstraintExpression( const OUString& sConstraint ) 412 { 413 maConstraint.setExpression( sConstraint ); 414 msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT, 415 sConstraint ); 416 417 // TODO: This should only re-evaluate the constraint, and notify 418 // the validity constraint listeners; instead we currently pretend 419 // the entire binding was notified, which does a little too much. 420 bindingModified(); 421 } 422 423 OUString Binding::getCalculateExpression() const 424 { 425 return maCalculate.getExpression(); 426 } 427 428 void Binding::setCalculateExpression( const OUString& sCalculate ) 429 { 430 maCalculate.setExpression( sCalculate ); 431 bindingModified(); 432 } 433 434 OUString Binding::getType() const 435 { 436 return msTypeName; 437 } 438 439 void Binding::setType( const OUString& sTypeName ) 440 { 441 msTypeName = sTypeName; 442 bindingModified(); 443 } 444 445 Binding::XNameContainer_t Binding::getBindingNamespaces() const 446 { 447 // return _getNamespaces(); 448 return mxNamespaces; 449 } 450 451 void Binding::setBindingNamespaces( const XNameContainer_t& rNamespaces ) 452 { 453 _setNamespaces( rNamespaces, true ); 454 } 455 456 Binding::XNameContainer_t Binding::getModelNamespaces() const 457 { 458 return _getNamespaces(); 459 } 460 461 void Binding::setModelNamespaces( const XNameContainer_t& rNamespaces ) 462 { 463 _setNamespaces( rNamespaces, false ); 464 } 465 466 bool Binding::getReadOnly() const 467 { 468 return maMIP.isReadonly(); 469 } 470 471 bool Binding::getRelevant() const 472 { 473 return maMIP.isRelevant(); 474 } 475 476 bool Binding::getExternalData() const 477 { 478 bool bExternalData = true; 479 if ( !mxModel.is() ) 480 return bExternalData; 481 482 try 483 { 484 Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW ); 485 OSL_VERIFY( 486 xModelProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExternalData" ) ) ) >>= bExternalData ); 487 } 488 catch( const Exception& ) 489 { 490 DBG_UNHANDLED_EXCEPTION(); 491 } 492 return bExternalData; 493 } 494 495 496 void Binding::checkLive() 497 throw( RuntimeException ) 498 { 499 if( ! isLive() ) 500 throw RuntimeException( EXCEPT("Binding not initialized") ); 501 } 502 503 void Binding::checkModel() 504 throw( RuntimeException ) 505 { 506 if( ! mxModel.is() ) 507 throw RuntimeException( EXCEPT("Binding has no Model") ); 508 } 509 510 bool Binding::isLive() const 511 { 512 const Model* pModel = getModelImpl(); 513 return ( pModel != NULL ) ? pModel->isInitialized() : false; 514 } 515 516 Model* Binding::getModelImpl() const 517 { 518 return getModelImpl( mxModel ); 519 } 520 521 Model* Binding::getModelImpl( const Model_t& xModel ) const 522 { 523 Reference<XUnoTunnel> xTunnel( xModel, UNO_QUERY ); 524 Model* pModel = xTunnel.is() 525 ? reinterpret_cast<Model*>( 526 xTunnel->getSomething( Model::getUnoTunnelID() ) ) 527 : NULL; 528 return pModel; 529 } 530 531 void lcl_addListenerToNode( Reference<XNode> xNode, 532 Reference<XEventListener> xListener ) 533 { 534 Reference<XEventTarget> xTarget( xNode, UNO_QUERY ); 535 if( xTarget.is() ) 536 { 537 xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"), 538 xListener, false ); 539 xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"), 540 xListener, true ); 541 xTarget->addEventListener( OUSTRING("DOMAttrModified"), 542 xListener, false ); 543 xTarget->addEventListener( OUSTRING("DOMAttrModified"), 544 xListener, true ); 545 xTarget->addEventListener( OUSTRING("DOMAttrModified"), 546 xListener, true ); 547 xTarget->addEventListener( OUSTRING("xforms-generic"), 548 xListener, true ); 549 } 550 } 551 552 void lcl_removeListenerFromNode( Reference<XNode> xNode, 553 Reference<XEventListener> xListener ) 554 { 555 Reference<XEventTarget> xTarget( xNode, UNO_QUERY ); 556 if( xTarget.is() ) 557 { 558 xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"), 559 xListener, false ); 560 xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"), 561 xListener, true ); 562 xTarget->removeEventListener( OUSTRING("DOMAttrModified"), 563 xListener, false ); 564 xTarget->removeEventListener( OUSTRING("DOMAttrModified"), 565 xListener, true ); 566 xTarget->removeEventListener( OUSTRING("xforms-generic"), 567 xListener, true ); 568 } 569 } 570 571 ::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const 572 { 573 OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); 574 575 // iterate over nodes of bind expression and create 576 // EvaluationContext for each 577 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); 578 ::std::vector<EvaluationContext> aVector; 579 sal_Int32 nCount = 0; // count nodes for context position 580 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin(); 581 aIter != aNodes.end(); 582 aIter++, nCount++ ) 583 { 584 OSL_ENSURE( aIter->is(), "no node?" ); 585 586 // create proper evaluation context for this MIP 587 aVector.push_back( EvaluationContext( *aIter, getModel(), 588 getBindingNamespaces(), 589 nCount, aNodes.size() ) ); 590 } 591 return aVector; 592 } 593 594 void Binding::bind( bool bForceRebind ) 595 { 596 checkModel(); 597 598 // bind() will evaluate this binding as follows: 599 // 1) evaluate the binding expression 600 // 1b) if necessary, create node according to 'lazy author' rules 601 // 2) register suitable listeners on the instance (and remove old ones) 602 // 3) remove old MIPs defined by this binding 603 // 4) for every node in the binding nodeset do: 604 // 1) create proper evaluation context for this MIP 605 // 2) evaluate calculate expression (and push value into instance) 606 // 3) evaluate remaining MIPs 607 // 4) evaluate the locally defined MIPs, and push them to the model 608 609 610 // 1) evaluate the binding expression 611 EvaluationContext aContext = getEvaluationContext(); 612 maBindingExpression.evaluate( aContext ); 613 if( ! maBindingExpression.getNode().is() ) 614 { 615 // 1b) create node (if valid element name) 616 if( isValidQName( maBindingExpression.getExpression(), 617 aContext.mxNamespaces ) ) 618 { 619 aContext.mxContextNode->appendChild( 620 Reference<XNode>( 621 aContext.mxContextNode->getOwnerDocument()->createElement( 622 maBindingExpression.getExpression() ), 623 UNO_QUERY ) ); 624 maBindingExpression.evaluate( aContext ); 625 OSL_ENSURE( maBindingExpression.getNode().is(), 626 "we should bind to the newly inserted node!" ); 627 } 628 } 629 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); 630 631 // 2) register suitable listeners on the instance (and remove old ones) 632 if( maEventNodes.empty() || bForceRebind ) 633 { 634 for( XNodes_t::iterator aIter = maEventNodes.begin(); 635 aIter != maEventNodes.end(); 636 aIter ++ ) 637 lcl_removeListenerFromNode( *aIter, this ); 638 maEventNodes.clear(); 639 if( isSimpleBinding() ) 640 for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin(); 641 aIter != aNodes.end(); 642 aIter++ ) 643 maEventNodes.push_back( *aIter ); 644 else 645 maEventNodes.push_back( 646 Reference<XNode>( aContext.mxContextNode->getOwnerDocument(), 647 UNO_QUERY_THROW ) ); 648 for( PathExpression::NodeVector_t::iterator aIter2 = maEventNodes.begin(); 649 aIter2 != maEventNodes.end(); 650 aIter2 ++ ) 651 lcl_addListenerToNode( *aIter2, this ); 652 } 653 654 // 3) remove old MIPs defined by this binding 655 Model* pModel = getModelImpl(); 656 OSL_ENSURE( pModel != NULL, "need model" ); 657 pModel->removeMIPs( this ); 658 659 // 4) calculate all MIPs 660 ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts(); 661 for( ::std::vector<EvaluationContext>::iterator aIter = aMIPContexts.begin(); 662 aIter != aMIPContexts.end(); 663 aIter++ ) 664 { 665 EvaluationContext& rContext = *aIter; 666 667 // evaluate calculate expression (and push value into instance) 668 // (prevent recursion using mbInCalculate 669 if( ! maCalculate.isEmptyExpression() ) 670 { 671 if( ! mbInCalculate ) 672 { 673 mbInCalculate = true; 674 maCalculate.evaluate( rContext ); 675 pModel->setSimpleContent( rContext.mxContextNode, 676 maCalculate.getString() ); 677 mbInCalculate = false; 678 } 679 } 680 681 // now evaluate remaining MIPs in the apropriate context 682 maReadonly.evaluate( rContext ); 683 maRelevant.evaluate( rContext ); 684 maRequired.evaluate( rContext ); 685 maConstraint.evaluate( rContext ); 686 // type is static; does not need updating 687 688 // evaluate the locally defined MIPs, and push them to the model 689 pModel->addMIP( this, rContext.mxContextNode, getLocalMIP() ); 690 } 691 } 692 693 694 // helper for Binding::valueModified 695 void lcl_modified( const Binding::XModifyListener_t xListener, 696 const Reference<XInterface> xSource ) 697 { 698 OSL_ENSURE( xListener.is(), "no listener?" ); 699 xListener->modified( EventObject( xSource ) ); 700 } 701 702 // helper for Binding::valueModified 703 void lcl_listentry( const Binding::XListEntryListener_t xListener, 704 const Reference<XInterface> xSource ) 705 { 706 OSL_ENSURE( xListener.is(), "no listener?" ); 707 // TODO: send fine granular events 708 xListener->allEntriesChanged( EventObject( xSource ) ); 709 } 710 711 // helper for Binding::valueModified 712 void lcl_validate( const Binding::XValidityConstraintListener_t xListener, 713 const Reference<XInterface> xSource ) 714 { 715 OSL_ENSURE( xListener.is(), "no listener?" ); 716 xListener->validityConstraintChanged( EventObject( xSource ) ); 717 } 718 719 720 void Binding::valueModified() 721 { 722 // defer notifications, if so desired 723 if( mnDeferModifyNotifications > 0 ) 724 { 725 mbValueModified = true; 726 return; 727 } 728 mbValueModified = false; 729 730 // query MIP used by our first node (also note validity) 731 Reference<XNode> xNode = maBindingExpression.getNode(); 732 maMIP = getModelImpl()->queryMIP( xNode ); 733 734 // distribute MIPs _used_ by this binding 735 if( xNode.is() ) 736 { 737 notifyAndCachePropertyValue( HANDLE_ReadOnly ); 738 notifyAndCachePropertyValue( HANDLE_Relevant ); 739 } 740 741 // iterate over _value_ listeners and send each a modified signal, 742 // using this object as source (will also update validity, because 743 // control will query once the value has changed) 744 Reference<XInterface> xSource = static_cast<XPropertySet*>( this ); 745 ::std::for_each( maModifyListeners.begin(), 746 maModifyListeners.end(), 747 ::std::bind2nd( ::std::ptr_fun( lcl_modified ), xSource ) ); 748 ::std::for_each( maListEntryListeners.begin(), 749 maListEntryListeners.end(), 750 ::std::bind2nd( ::std::ptr_fun( lcl_listentry ), xSource ) ); 751 ::std::for_each( maValidityListeners.begin(), 752 maValidityListeners.end(), 753 ::std::bind2nd( ::std::ptr_fun( lcl_validate ), xSource ) ); 754 755 // now distribute MIPs to childs 756 if( xNode.is() ) 757 distributeMIP( xNode->getFirstChild() ); 758 } 759 760 void Binding::distributeMIP( const XNode_t & rxNode ) { 761 762 typedef com::sun::star::xforms::XFormsEventConcrete XFormsEvent_t; 763 OUString sEventName( RTL_CONSTASCII_USTRINGPARAM("xforms-generic") ); 764 XFormsEvent_t *pEvent = new XFormsEvent_t; 765 pEvent->initXFormsEvent(sEventName, sal_True, sal_False); 766 Reference<XEvent> xEvent(pEvent); 767 768 // naive depth-first traversal 769 XNode_t xNode( rxNode ); 770 while(xNode.is()) { 771 772 // notifications should be triggered at the 773 // leaf nodes first, bubbling upwards the hierarchy. 774 XNode_t child(xNode->getFirstChild()); 775 if(child.is()) 776 distributeMIP(child); 777 778 // we're standing at a particular node somewhere 779 // below the one which changed a property (MIP). 780 // bindings which are listening at this node will receive 781 // a notification message about what exactly happened. 782 Reference< XEventTarget > target(xNode,UNO_QUERY); 783 target->dispatchEvent(xEvent); 784 785 xNode = xNode->getNextSibling(); 786 }; 787 } 788 789 void Binding::bindingModified() 790 { 791 // defer notifications, if so desired 792 if( mnDeferModifyNotifications > 0 ) 793 { 794 mbBindingModified = true; 795 return; 796 } 797 mbBindingModified = false; 798 799 // rebind (if live); then call valueModified 800 // A binding should be inert until its model is fully constructed. 801 if( isLive() ) 802 { 803 bind( true ); 804 valueModified(); 805 } 806 } 807 808 809 MIP Binding::getLocalMIP() const 810 { 811 MIP aMIP; 812 813 if( maReadonly.hasValue() ) 814 aMIP.setReadonly( maReadonly.getBool( false ) ); 815 if( maRelevant.hasValue() ) 816 aMIP.setRelevant( maRelevant.getBool( true ) ); 817 if( maRequired.hasValue() ) 818 aMIP.setRequired( maRequired.getBool( false ) ); 819 if( maConstraint.hasValue() ) 820 { 821 aMIP.setConstraint( maConstraint.getBool( true ) ); 822 if( ! aMIP.isConstraint() ) 823 aMIP.setConstraintExplanation( msExplainConstraint ); 824 } 825 if( msTypeName.getLength() > 0 ) 826 aMIP.setTypeName( msTypeName ); 827 828 // calculate: only handle presence of calculate; value set elsewhere 829 aMIP.setHasCalculate( !maCalculate.isEmptyExpression() ); 830 831 return aMIP; 832 } 833 834 Binding::XDataType_t Binding::getDataType() 835 { 836 OSL_ENSURE( getModel().is(), "need model" ); 837 OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" ); 838 839 Reference<XDataTypeRepository> xRepository( 840 getModel()->getDataTypeRepository(), UNO_QUERY ); 841 OUString sTypeName = maMIP.getTypeName(); 842 843 return ( xRepository.is() && xRepository->hasByName( sTypeName ) ) 844 ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY) 845 : Reference<XDataType>( NULL ); 846 } 847 848 bool Binding::isValid_DataType() 849 { 850 Reference<XDataType> xDataType = getDataType(); 851 return xDataType.is() 852 ? xDataType->validate( maBindingExpression.getString() ) 853 : true; 854 } 855 856 rtl::OUString Binding::explainInvalid_DataType() 857 { 858 Reference<XDataType> xDataType = getDataType(); 859 return xDataType.is() 860 ? xDataType->explainInvalid( maBindingExpression.getString() ) 861 : OUString(); 862 } 863 864 void Binding::clear() 865 { 866 // remove MIPs contributed by this binding 867 Model* pModel = getModelImpl(); 868 if( pModel != NULL ) 869 pModel->removeMIPs( this ); 870 871 // remove all references 872 for( XNodes_t::iterator aIter = maEventNodes.begin(); 873 aIter != maEventNodes.end(); 874 aIter ++ ) 875 lcl_removeListenerFromNode( *aIter, this ); 876 maEventNodes.clear(); 877 878 // clear expressions 879 maBindingExpression.clear(); 880 maReadonly.clear(); 881 maRelevant.clear(); 882 maRequired.clear(); 883 maConstraint.clear(); 884 maCalculate.clear(); 885 886 // TODO: what about our listeners? 887 } 888 889 890 void lcl_removeOtherNamespaces( const Binding::XNameContainer_t& xFrom, 891 Binding::XNameContainer_t& xTo ) 892 { 893 OSL_ENSURE( xFrom.is(), "no source" ); 894 OSL_ENSURE( xTo.is(), "no target" ); 895 896 // iterate over name in source 897 Sequence<OUString> aNames = xTo->getElementNames(); 898 sal_Int32 nNames = aNames.getLength(); 899 const OUString* pNames = aNames.getConstArray(); 900 for( sal_Int32 i = 0; i < nNames; i++ ) 901 { 902 const OUString& rName = pNames[i]; 903 904 if( ! xFrom->hasByName( rName ) ) 905 xTo->removeByName( rName ); 906 } 907 } 908 909 /** copy namespaces from one namespace container into another 910 * @param bOverwrite true: overwrite namespaces in target 911 * false: do not overwrite namespaces in target 912 * @param bMove true: move namespaces (i.e., delete in source) 913 * false: copy namespaces (do not modify source) 914 * @param bFromSource true: use elements from source 915 * false: use only elements from target 916 */ 917 void lcl_copyNamespaces( const Binding::XNameContainer_t& xFrom, 918 Binding::XNameContainer_t& xTo, 919 bool bOverwrite ) 920 { 921 OSL_ENSURE( xFrom.is(), "no source" ); 922 OSL_ENSURE( xTo.is(), "no target" ); 923 924 // iterate over name in source 925 Sequence<OUString> aNames = xFrom->getElementNames(); 926 sal_Int32 nNames = aNames.getLength(); 927 const OUString* pNames = aNames.getConstArray(); 928 for( sal_Int32 i = 0; i < nNames; i++ ) 929 { 930 const OUString& rName = pNames[i]; 931 932 // determine whether to copy the value, and whether to delete 933 // it in the source: 934 935 bool bInTarget = xTo->hasByName( rName ); 936 937 // we copy: if property is in target, and 938 // if bOverwrite is set, or when the namespace prefix is free 939 bool bCopy = bOverwrite || ! bInTarget; 940 941 // and now... ACTION! 942 if( bCopy ) 943 { 944 if( bInTarget ) 945 xTo->replaceByName( rName, xFrom->getByName( rName ) ); 946 else 947 xTo->insertByName( rName, xFrom->getByName( rName ) ); 948 } 949 } 950 } 951 952 // implement get*Namespaces() 953 // (identical for both variants) 954 Binding::XNameContainer_t Binding::_getNamespaces() const 955 { 956 XNameContainer_t xNamespaces = new NameContainer<OUString>(); 957 lcl_copyNamespaces( mxNamespaces, xNamespaces, true ); 958 959 // merge model's with binding's own namespaces 960 Model* pModel = getModelImpl(); 961 if( pModel != NULL ) 962 lcl_copyNamespaces( pModel->getNamespaces(), xNamespaces, false ); 963 964 return xNamespaces; 965 } 966 967 // implement set*Namespaces() 968 // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces 969 void Binding::_setNamespaces( const XNameContainer_t& rNamespaces, 970 bool bBinding ) 971 { 972 Model* pModel = getModelImpl(); 973 XNameContainer_t xModelNamespaces = ( pModel != NULL ) 974 ? pModel->getNamespaces() 975 : NULL; 976 OSL_ENSURE( ( pModel != NULL ) == xModelNamespaces.is(), "no model nmsp?"); 977 978 // remove deleted namespaces 979 lcl_removeOtherNamespaces( rNamespaces, mxNamespaces ); 980 if( !bBinding && xModelNamespaces.is() ) 981 lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces ); 982 983 // copy namespaces as appropriate 984 Sequence<OUString> aNames = rNamespaces->getElementNames(); 985 sal_Int32 nNames = aNames.getLength(); 986 const OUString* pNames = aNames.getConstArray(); 987 for( sal_Int32 i = 0; i < nNames; i++ ) 988 { 989 const OUString& rName = pNames[i]; 990 Any aValue = rNamespaces->getByName( rName ); 991 992 // determine whether the namespace should go into model's or 993 // into binding's namespaces 994 bool bLocal = 995 ! xModelNamespaces.is() 996 || mxNamespaces->hasByName( rName ) 997 || ( bBinding 998 && xModelNamespaces.is() 999 && xModelNamespaces->hasByName( rName ) ); 1000 1001 // write namespace into the appropriate namespace container 1002 XNameContainer_t& rWhich = bLocal ? mxNamespaces : xModelNamespaces; 1003 OSL_ENSURE( rWhich.is(), "whoops" ); 1004 if( rWhich->hasByName( rName ) ) 1005 rWhich->replaceByName( rName, aValue ); 1006 else 1007 rWhich->insertByName( rName, aValue ); 1008 1009 // always 'promote' namespaces from binding to model, if equal 1010 if( xModelNamespaces.is() 1011 && xModelNamespaces->hasByName( rName ) 1012 && mxNamespaces->hasByName( rName ) 1013 && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) ) 1014 { 1015 mxNamespaces->removeByName( rName ); 1016 } 1017 } 1018 1019 // ... done. But we modified the binding! 1020 bindingModified(); 1021 } 1022 1023 void Binding::_checkBindingID() 1024 { 1025 if( getModel().is() ) 1026 { 1027 Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW ); 1028 if( msBindingID.getLength() == 0 ) 1029 { 1030 // no binding ID? then make one up! 1031 OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ); 1032 sIDPrefix += String::CreateFromAscii( " " ); 1033 sal_Int32 nNumber = 0; 1034 OUString sName; 1035 do 1036 { 1037 nNumber++; 1038 sName = sIDPrefix + OUString::valueOf( nNumber ); 1039 } 1040 while( xBindings->hasByName( sName ) ); 1041 setBindingID( sName ); 1042 } 1043 } 1044 } 1045 1046 1047 1048 1049 // 1050 // XValueBinding 1051 // 1052 1053 Binding::Sequence_Type_t Binding::getSupportedValueTypes() 1054 throw( RuntimeException ) 1055 { 1056 return Convert::get().getTypes(); 1057 } 1058 1059 sal_Bool Binding::supportsType( const Type_t& rType ) 1060 throw( RuntimeException ) 1061 { 1062 return Convert::get().hasType( rType ); 1063 } 1064 1065 Binding::Any_t Binding::getValue( const Type_t& rType ) 1066 throw( IncompatibleTypesException, 1067 RuntimeException ) 1068 { 1069 // first, check for model 1070 checkLive(); 1071 1072 // second, check for type 1073 if( ! supportsType( rType ) ) 1074 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) ); 1075 1076 // return string value (if present; else return empty Any) 1077 Binding::Any_t result = Any(); 1078 if(maBindingExpression.hasValue()) { 1079 rtl::OUString pathExpr(maBindingExpression.getString()); 1080 Convert &rConvert = Convert::get(); 1081 result = rConvert.toAny(pathExpr,rType); 1082 } 1083 1084 // return maBindingExpression.hasValue() 1085 // ? Convert::get().toAny( maBindingExpression.getString(), rType ) 1086 // : Any(); 1087 1088 return result; 1089 } 1090 1091 void Binding::setValue( const Any_t& aValue ) 1092 throw( IncompatibleTypesException, 1093 InvalidBindingStateException, 1094 NoSupportException, 1095 RuntimeException ) 1096 { 1097 // first, check for model 1098 checkLive(); 1099 1100 // check for supported type 1101 if( ! supportsType( aValue.getValueType() ) ) 1102 throw IncompatibleTypesException( EXCEPT( "type unsupported" ) ); 1103 1104 if( maBindingExpression.hasValue() ) 1105 { 1106 Binding::XNode_t xNode = maBindingExpression.getNode(); 1107 if( xNode.is() ) 1108 { 1109 OUString sValue = Convert::get().toXSD( aValue ); 1110 bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue ); 1111 if( ! bSuccess ) 1112 throw InvalidBindingStateException( EXCEPT( "can't set value" ) ); 1113 } 1114 else 1115 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) ); 1116 } 1117 else 1118 throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) ); 1119 } 1120 1121 1122 // 1123 // XListEntry Source 1124 // 1125 1126 sal_Int32 Binding::getListEntryCount() 1127 throw( RuntimeException ) 1128 { 1129 // first, check for model 1130 checkLive(); 1131 1132 // return size of node list 1133 return maBindingExpression.getNodeList().size(); 1134 } 1135 1136 void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer ) 1137 { 1138 if( xNode->getNodeType() == NodeType_TEXT_NODE 1139 || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE ) 1140 { 1141 rBuffer.append( xNode->getNodeValue() ); 1142 } 1143 else 1144 { 1145 for( Reference<XNode> xChild = xNode->getFirstChild(); 1146 xChild.is(); 1147 xChild = xChild->getNextSibling() ) 1148 { 1149 lcl_getString( xChild, rBuffer ); 1150 } 1151 } 1152 } 1153 1154 OUString lcl_getString( const Reference<XNode>& xNode ) 1155 { 1156 OUStringBuffer aBuffer; 1157 lcl_getString( xNode, aBuffer ); 1158 return aBuffer.makeStringAndClear(); 1159 } 1160 1161 OUString Binding::getListEntry( sal_Int32 nPosition ) 1162 throw( IndexOutOfBoundsException, 1163 RuntimeException ) 1164 { 1165 // first, check for model 1166 checkLive(); 1167 1168 // check bounds and return proper item 1169 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); 1170 if( nPosition < 0 || nPosition >= static_cast<sal_Int32>( aNodes.size() ) ) 1171 throw IndexOutOfBoundsException( EXCEPT("") ); 1172 return lcl_getString( aNodes[ nPosition ] ); 1173 } 1174 1175 Sequence<OUString> Binding::getAllListEntries() 1176 throw( RuntimeException ) 1177 { 1178 // first, check for model 1179 checkLive(); 1180 1181 // create sequence of string values 1182 PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); 1183 Sequence<OUString> aSequence( aNodes.size() ); 1184 OUString* pSequence = aSequence.getArray(); 1185 for( sal_Int32 n = 0; n < aSequence.getLength(); n++ ) 1186 { 1187 pSequence[n] = lcl_getString( aNodes[n] ); 1188 } 1189 1190 return aSequence; 1191 } 1192 1193 void Binding::addListEntryListener( const XListEntryListener_t& xListener ) 1194 throw( NullPointerException, 1195 RuntimeException ) 1196 { 1197 OSL_ENSURE( xListener.is(), "need listener!" ); 1198 if( ::std::find( maListEntryListeners.begin(), 1199 maListEntryListeners.end(), 1200 xListener) 1201 == maListEntryListeners.end() ) 1202 maListEntryListeners.push_back( xListener ); 1203 } 1204 1205 void Binding::removeListEntryListener( const XListEntryListener_t& xListener ) 1206 throw( NullPointerException, 1207 RuntimeException ) 1208 { 1209 XListEntryListeners_t::iterator aIter = 1210 ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(), 1211 xListener ); 1212 if( aIter != maListEntryListeners.end() ) 1213 maListEntryListeners.erase( aIter ); 1214 } 1215 1216 1217 // 1218 // XValidator 1219 // 1220 1221 sal_Bool Binding::isValid( const Any_t& ) 1222 throw( RuntimeException ) 1223 { 1224 // first, check for model 1225 checkLive(); 1226 1227 // ignore value; determine validate only on current data 1228 return isValid(); 1229 } 1230 1231 rtl::OUString Binding::explainInvalid( 1232 const Any_t& /*Value*/ ) 1233 throw( RuntimeException ) 1234 { 1235 // first, check for model 1236 checkLive(); 1237 1238 // ignore value; determine explanation only on current data 1239 return explainInvalid(); 1240 } 1241 1242 void Binding::addValidityConstraintListener( 1243 const XValidityConstraintListener_t& xListener ) 1244 throw( NullPointerException, 1245 RuntimeException ) 1246 { 1247 OSL_ENSURE( xListener.is(), "need listener!" ); 1248 if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener) 1249 == maValidityListeners.end() ) 1250 maValidityListeners.push_back( xListener ); 1251 } 1252 1253 void Binding::removeValidityConstraintListener( 1254 const XValidityConstraintListener_t& xListener ) 1255 throw( NullPointerException, 1256 RuntimeException ) 1257 { 1258 XValidityConstraintListeners_t::iterator aIter = 1259 ::std::find( maValidityListeners.begin(), maValidityListeners.end(), 1260 xListener ); 1261 if( aIter != maValidityListeners.end() ) 1262 maValidityListeners.erase( aIter ); 1263 } 1264 1265 1266 1267 // 1268 // xml::dom::event::XEventListener 1269 // 1270 1271 void Binding::handleEvent( const XEvent_t& xEvent ) 1272 throw( RuntimeException ) 1273 { 1274 OUString sType(xEvent->getType()); 1275 //OUString sEventMIPChanged(RTL_CONSTASCII_USTRINGPARAM("xforms-generic")); 1276 //if(sType.equals(sEventMIPChanged)) { 1277 if(!sType.compareToAscii("xforms-generic")) { 1278 1279 // the modification of the 'mnDeferModifyNotifications'-member 1280 // is necessary to prevent infinite notication looping. 1281 // This can happend in case the binding which caused 1282 // the notification chain is listening to those events 1283 // as well... 1284 bool bPreserveValueModified = mbValueModified; 1285 mnDeferModifyNotifications++; 1286 valueModified(); 1287 --mnDeferModifyNotifications; 1288 mbValueModified = bPreserveValueModified; 1289 return; 1290 } 1291 1292 // if we're a dynamic binding, we better re-bind, too! 1293 bind( false ); 1294 1295 // our value was maybe modified 1296 valueModified(); 1297 } 1298 1299 1300 // 1301 // lang::XUnoTunnel 1302 // 1303 1304 sal_Int64 Binding::getSomething( const IntSequence_t& xId ) 1305 throw( RuntimeException ) 1306 { 1307 return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL ); 1308 } 1309 1310 // 1311 // XCloneable 1312 // 1313 1314 Binding::XCloneable_t SAL_CALL Binding::createClone() 1315 throw( RuntimeException ) 1316 { 1317 Reference< XPropertySet > xClone; 1318 1319 Model* pModel = getModelImpl(); 1320 if ( pModel ) 1321 xClone = pModel->cloneBinding( this ); 1322 else 1323 { 1324 xClone = new Binding; 1325 copy( this, xClone ); 1326 } 1327 return XCloneable_t( xClone, UNO_QUERY ); 1328 } 1329 1330 // 1331 // property set implementations 1332 // 1333 1334 #define REGISTER_PROPERTY( property, type ) \ 1335 registerProperty( PROPERTY( property, type ), \ 1336 new DirectPropertyAccessor< Binding, type >( this, &Binding::set##property, &Binding::get##property ) ); 1337 1338 #define REGISTER_PROPERTY_RO( property, type ) \ 1339 registerProperty( PROPERTY_RO( property, type ), \ 1340 new DirectPropertyAccessor< Binding, type >( this, NULL, &Binding::get##property ) ); 1341 1342 #define REGISTER_BOOL_PROPERTY_RO( property ) \ 1343 registerProperty( PROPERTY_RO( property, sal_Bool ), \ 1344 new BooleanPropertyAccessor< Binding, bool >( this, NULL, &Binding::get##property ) ); 1345 1346 void Binding::initializePropertySet() 1347 { 1348 REGISTER_PROPERTY ( BindingID, OUString ); 1349 REGISTER_PROPERTY ( BindingExpression, OUString ); 1350 REGISTER_PROPERTY_RO ( Model, Model_t ); 1351 REGISTER_PROPERTY ( BindingNamespaces, XNameContainer_t ); 1352 REGISTER_PROPERTY ( ModelNamespaces, XNameContainer_t ); 1353 REGISTER_PROPERTY_RO ( ModelID, OUString ); 1354 REGISTER_PROPERTY ( ReadonlyExpression, OUString ); 1355 REGISTER_PROPERTY ( RelevantExpression, OUString ); 1356 REGISTER_PROPERTY ( RequiredExpression, OUString ); 1357 REGISTER_PROPERTY ( ConstraintExpression, OUString ); 1358 REGISTER_PROPERTY ( CalculateExpression, OUString ); 1359 REGISTER_PROPERTY ( Type, OUString ); 1360 REGISTER_PROPERTY_RO ( ReadOnly, bool ); 1361 REGISTER_PROPERTY_RO ( Relevant, bool ); 1362 REGISTER_BOOL_PROPERTY_RO( ExternalData ); 1363 1364 initializePropertyValueCache( HANDLE_ReadOnly ); 1365 initializePropertyValueCache( HANDLE_Relevant ); 1366 initializePropertyValueCache( HANDLE_ExternalData ); 1367 } 1368 1369 void Binding::addModifyListener( 1370 const XModifyListener_t& xListener ) 1371 throw( RuntimeException ) 1372 { 1373 OSL_ENSURE( xListener.is(), "need listener!" ); 1374 if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener ) 1375 == maModifyListeners.end() ) 1376 maModifyListeners.push_back( xListener ); 1377 1378 // HACK: currently, we have to 'push' some MIPs to the control 1379 // (read-only, relevant, etc.) To enable this, we need to update 1380 // the control at least once when it registers here. 1381 valueModified(); 1382 } 1383 1384 void Binding::removeModifyListener( 1385 const XModifyListener_t& xListener ) 1386 throw( RuntimeException ) 1387 { 1388 ModifyListeners_t::iterator aIter = 1389 ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener ); 1390 if( aIter != maModifyListeners.end() ) 1391 maModifyListeners.erase( aIter ); 1392 } 1393 1394 1395 1396 1397 rtl::OUString Binding::getName() 1398 throw( RuntimeException ) 1399 { 1400 return getBindingID(); 1401 } 1402 1403 void SAL_CALL Binding::setName( const rtl::OUString& rName ) 1404 throw( RuntimeException ) 1405 { 1406 // use the XPropertySet methods, so the change in the name is notified to the 1407 // property listeners 1408 setFastPropertyValue( HANDLE_BindingID, makeAny( rName ) ); 1409 } 1410