1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_forms.hxx" 26 #include "RadioButton.hxx" 27 #include "property.hxx" 28 #ifndef _FRM_PROPERTY_HRC_ 29 #include "property.hrc" 30 #endif 31 #include "services.hxx" 32 #include <tools/debug.hxx> 33 #include <comphelper/extract.hxx> 34 #include <comphelper/basicio.hxx> 35 #include <com/sun/star/container/XIndexAccess.hpp> 36 #include <com/sun/star/awt/XVclWindowPeer.hpp> 37 38 //......................................................................... 39 namespace frm 40 { 41 using namespace ::com::sun::star::uno; 42 using namespace ::com::sun::star::sdb; 43 using namespace ::com::sun::star::sdbc; 44 using namespace ::com::sun::star::sdbcx; 45 using namespace ::com::sun::star::beans; 46 using namespace ::com::sun::star::container; 47 using namespace ::com::sun::star::form; 48 using namespace ::com::sun::star::awt; 49 using namespace ::com::sun::star::io; 50 using namespace ::com::sun::star::lang; 51 using namespace ::com::sun::star::util; 52 using namespace ::com::sun::star::form::binding; 53 54 //================================================================== 55 //------------------------------------------------------------------------------ 56 InterfaceRef SAL_CALL ORadioButtonControl_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory) throw (RuntimeException) 57 { 58 return *(new ORadioButtonControl(_rxFactory)); 59 } 60 61 //------------------------------------------------------------------------------ 62 StringSequence SAL_CALL ORadioButtonControl::getSupportedServiceNames() throw(RuntimeException) 63 { 64 StringSequence aSupported = OBoundControl::getSupportedServiceNames(); 65 aSupported.realloc(aSupported.getLength() + 1); 66 67 ::rtl::OUString* pArray = aSupported.getArray(); 68 pArray[aSupported.getLength()-1] = FRM_SUN_CONTROL_RADIOBUTTON; 69 return aSupported; 70 } 71 72 73 //------------------------------------------------------------------ 74 ORadioButtonControl::ORadioButtonControl(const Reference<XMultiServiceFactory>& _rxFactory) 75 :OBoundControl(_rxFactory, VCL_CONTROL_RADIOBUTTON) 76 { 77 } 78 79 //------------------------------------------------------------------ 80 void SAL_CALL ORadioButtonControl::createPeer(const Reference<starawt::XToolkit>& _rxToolkit, const Reference<starawt::XWindowPeer>& _rxParent) throw (RuntimeException) 81 { 82 OBoundControl::createPeer(_rxToolkit, _rxParent); 83 84 // switch off the auto-toggle, we do this ourself .... 85 // (formerly this switch-off was done in the toolkit - but the correct place is here ...) 86 // Reference< XVclWindowPeer > xVclWindowPeer( getPeer(), UNO_QUERY ); 87 // if (xVclWindowPeer.is()) 88 // xVclWindowPeer->setProperty(::rtl::OUString::createFromAscii("AutoToggle"), ::cppu::bool2any(sal_False)); 89 // new order: do _not_ switch off the auto toggle because: 90 // * today, it is not necessary anymore to handle the toggling ourself (everything works fine without it) 91 // * without auto toggle, the AccessibleEvents as fired by the radio buttons are 92 // a. newly checked button: "unchecked"->"checked" 93 // b. previously checked button: "checked"->"unchecked" 94 // This is deadly for AT-tools, which then get the "unchecked" event _immediately_ after the "checked" event, 95 // and only read the latter. This makes radio buttons pretty unusable in form documents. 96 // So we switched AutoToggle _on_, again, because then VCL can handle the notifications, and will send 97 // them in the proper order. 98 } 99 100 //================================================================== 101 InterfaceRef SAL_CALL ORadioButtonModel_CreateInstance(const Reference<XMultiServiceFactory>& _rxFactory) throw (RuntimeException) 102 { 103 return *(new ORadioButtonModel(_rxFactory)); 104 } 105 106 //------------------------------------------------------------------ 107 DBG_NAME( ORadioButtonModel ) 108 //------------------------------------------------------------------ 109 ORadioButtonModel::ORadioButtonModel(const Reference<XMultiServiceFactory>& _rxFactory) 110 :OReferenceValueComponent( _rxFactory, VCL_CONTROLMODEL_RADIOBUTTON, FRM_SUN_CONTROL_RADIOBUTTON,sal_True ) 111 // use the old control name for compytibility reasons 112 { 113 DBG_CTOR( ORadioButtonModel, NULL ); 114 115 m_nClassId = FormComponentType::RADIOBUTTON; 116 m_aLabelServiceName = FRM_SUN_COMPONENT_GROUPBOX; 117 initValueProperty( PROPERTY_STATE, PROPERTY_ID_STATE ); 118 } 119 120 //------------------------------------------------------------------ 121 ORadioButtonModel::ORadioButtonModel( const ORadioButtonModel* _pOriginal, const Reference<XMultiServiceFactory>& _rxFactory ) 122 :OReferenceValueComponent( _pOriginal, _rxFactory ) 123 { 124 DBG_CTOR( ORadioButtonModel, NULL ); 125 } 126 127 //------------------------------------------------------------------------------ 128 ORadioButtonModel::~ORadioButtonModel() 129 { 130 DBG_DTOR( ORadioButtonModel, NULL ); 131 } 132 133 // XCloneable 134 //------------------------------------------------------------------------------ 135 IMPLEMENT_DEFAULT_CLONING( ORadioButtonModel ) 136 137 // XServiceInfo 138 //------------------------------------------------------------------------------ 139 StringSequence SAL_CALL ORadioButtonModel::getSupportedServiceNames() throw(RuntimeException) 140 { 141 StringSequence aSupported = OReferenceValueComponent::getSupportedServiceNames(); 142 143 sal_Int32 nOldLen = aSupported.getLength(); 144 aSupported.realloc( nOldLen + 8 ); 145 ::rtl::OUString* pStoreTo = aSupported.getArray() + nOldLen; 146 147 *pStoreTo++ = BINDABLE_CONTROL_MODEL; 148 *pStoreTo++ = DATA_AWARE_CONTROL_MODEL; 149 *pStoreTo++ = VALIDATABLE_CONTROL_MODEL; 150 151 *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL; 152 *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL; 153 154 *pStoreTo++ = FRM_SUN_COMPONENT_RADIOBUTTON; 155 *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_RADIOBUTTON; 156 *pStoreTo++ = BINDABLE_DATABASE_RADIO_BUTTON; 157 158 return aSupported; 159 } 160 161 //------------------------------------------------------------------------------ 162 void ORadioButtonModel::SetSiblingPropsTo(const ::rtl::OUString& rPropName, const Any& rValue) 163 { 164 // mein Name 165 ::rtl::OUString sMyName(m_aName); 166 167 // meine Siblings durchiterieren 168 Reference<XIndexAccess> xIndexAccess(getParent(), UNO_QUERY); 169 if (xIndexAccess.is()) 170 { 171 Reference<XPropertySet> xMyProps; 172 query_interface(static_cast<XWeak*>(this), xMyProps); 173 ::rtl::OUString sCurrentName; 174 for (sal_Int32 i=0; i<xIndexAccess->getCount(); ++i) 175 { 176 Reference<XPropertySet> xSiblingProperties(*(InterfaceRef*)xIndexAccess->getByIndex(i).getValue(), UNO_QUERY); 177 if (!xSiblingProperties.is()) 178 continue; 179 if (xMyProps == xSiblingProperties) 180 continue; // mich selber nicht umsetzen 181 182 // nur wenn es ein Radio-Button ist 183 if (!hasProperty(PROPERTY_CLASSID, xSiblingProperties)) 184 continue; 185 sal_Int16 nType = 0; 186 xSiblingProperties->getPropertyValue(PROPERTY_CLASSID) >>= nType; 187 if (nType != FormComponentType::RADIOBUTTON) 188 continue; 189 190 // das 'zur selben Gruppe gehoeren' wird am Namen festgemacht 191 xSiblingProperties->getPropertyValue(PROPERTY_NAME) >>= sCurrentName; 192 if (sCurrentName == sMyName) 193 xSiblingProperties->setPropertyValue(rPropName, rValue); 194 } 195 } 196 } 197 198 //------------------------------------------------------------------------------ 199 void ORadioButtonModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) throw (Exception) 200 { 201 OReferenceValueComponent::setFastPropertyValue_NoBroadcast( nHandle, rValue ); 202 203 // if the label control changed ... 204 if (nHandle == PROPERTY_ID_CONTROLLABEL) 205 { // ... forward this to our siblings 206 SetSiblingPropsTo(PROPERTY_CONTROLLABEL, rValue); 207 } 208 209 // wenn sich die ControlSource-Eigenschaft geaendert hat ... 210 if (nHandle == PROPERTY_ID_CONTROLSOURCE) 211 { // ... muss ich allen meinen Siblings, die in der selben RadioButton-Gruppe sind wie ich, auch die 212 // neue ControlSource mitgeben 213 SetSiblingPropsTo(PROPERTY_CONTROLSOURCE, rValue); 214 } 215 216 // die andere Richtung : wenn sich mein Name aendert ... 217 if (nHandle == PROPERTY_ID_NAME) 218 { 219 // ... muss ich testen, ob ich Siblings mit dem selben Namen habe, damit ich deren ControlSource uebernehmen kann 220 Reference<XIndexAccess> xIndexAccess(getParent(), UNO_QUERY); 221 if (xIndexAccess.is()) 222 { 223 ::rtl::OUString sName; 224 ::rtl::OUString sControlSource; 225 226 Reference<XPropertySet> xMyProps; 227 query_interface(static_cast<XWeak*>(this), xMyProps); 228 for (sal_Int32 i=0; i<xIndexAccess->getCount(); ++i) 229 { 230 Reference<XPropertySet> xSiblingProperties(*(InterfaceRef*)xIndexAccess->getByIndex(i).getValue(), UNO_QUERY); 231 if (!xSiblingProperties.is()) 232 continue; 233 234 if (xMyProps == xSiblingProperties) 235 // nur wenn ich nicht mich selber gefunden habe 236 continue; 237 238 sal_Int16 nType = 0; 239 xSiblingProperties->getPropertyValue(PROPERTY_CLASSID) >>= nType; 240 if (nType != FormComponentType::RADIOBUTTON) 241 // nur Radio-Buttons 242 continue; 243 244 xSiblingProperties->getPropertyValue(PROPERTY_NAME) >>= sName; 245 // Control, das zur gleichen Gruppe gehoert ? 246 if (rValue == sName) 247 { 248 setPropertyValue(PROPERTY_CONTROLSOURCE, xSiblingProperties->getPropertyValue(PROPERTY_CONTROLSOURCE)); 249 break; 250 } 251 } 252 } 253 } 254 255 if (nHandle == PROPERTY_ID_DEFAULT_STATE) 256 { 257 sal_Int16 nValue; 258 rValue >>= nValue; 259 if (1 == nValue) 260 { // bei allen Radios der selben Gruppe das 'default checked' ruecksetzen, denn wie schon der highlander wusste : 261 // es kann nur einen geben. 262 Any aZero; 263 nValue = 0; 264 aZero <<= nValue; 265 SetSiblingPropsTo(PROPERTY_DEFAULT_STATE, aZero); 266 } 267 } 268 } 269 270 //------------------------------------------------------------------------------ 271 void ORadioButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const 272 { 273 BEGIN_DESCRIBE_PROPERTIES( 1, OReferenceValueComponent ) 274 DECL_PROP1(TABINDEX, sal_Int16, BOUND); 275 END_DESCRIBE_PROPERTIES(); 276 } 277 278 //------------------------------------------------------------------------------ 279 ::rtl::OUString SAL_CALL ORadioButtonModel::getServiceName() throw(RuntimeException) 280 { 281 return FRM_COMPONENT_RADIOBUTTON; // old (non-sun) name for compatibility ! 282 } 283 284 //------------------------------------------------------------------------------ 285 void SAL_CALL ORadioButtonModel::write(const Reference<XObjectOutputStream>& _rxOutStream) 286 throw(IOException, RuntimeException) 287 { 288 OReferenceValueComponent::write(_rxOutStream); 289 290 // Version 291 _rxOutStream->writeShort(0x0003); 292 293 // Properties 294 _rxOutStream << getReferenceValue(); 295 _rxOutStream << (sal_Int16)getDefaultChecked(); 296 writeHelpTextCompatibly(_rxOutStream); 297 298 // from version 0x0003 : common properties 299 writeCommonProperties(_rxOutStream); 300 } 301 302 //------------------------------------------------------------------------------ 303 void SAL_CALL ORadioButtonModel::read(const Reference<XObjectInputStream>& _rxInStream) throw(IOException, RuntimeException) 304 { 305 OReferenceValueComponent::read(_rxInStream); 306 ::osl::MutexGuard aGuard(m_aMutex); 307 308 // Version 309 sal_uInt16 nVersion = _rxInStream->readShort(); 310 311 ::rtl::OUString sReferenceValue; 312 sal_Int16 nDefaultChecked( 0 ); 313 switch (nVersion) 314 { 315 case 0x0001 : 316 _rxInStream >> sReferenceValue; 317 _rxInStream >> nDefaultChecked; 318 break; 319 case 0x0002 : 320 _rxInStream >> sReferenceValue; 321 _rxInStream >> nDefaultChecked; 322 readHelpTextCompatibly(_rxInStream); 323 break; 324 case 0x0003 : 325 _rxInStream >> sReferenceValue; 326 _rxInStream >> nDefaultChecked; 327 readHelpTextCompatibly(_rxInStream); 328 readCommonProperties(_rxInStream); 329 break; 330 default : 331 DBG_ERROR("ORadioButtonModel::read : unknown version !"); 332 defaultCommonProperties(); 333 break; 334 } 335 336 setReferenceValue( sReferenceValue ); 337 setDefaultChecked( (ToggleState)nDefaultChecked ); 338 339 // Nach dem Lesen die Defaultwerte anzeigen 340 if ( getControlSource().getLength() ) 341 // (not if we don't have a control source - the "State" property acts like it is persistent, then 342 resetNoBroadcast(); 343 } 344 345 //------------------------------------------------------------------------------ 346 void ORadioButtonModel::_propertyChanged(const PropertyChangeEvent& _rEvent) throw(RuntimeException) 347 { 348 if ( _rEvent.PropertyName.equals( PROPERTY_STATE ) ) 349 { 350 if ( _rEvent.NewValue == (sal_Int16)1 ) 351 { 352 // wenn sich mein Status auf 'checked' geaendert hat, muss ich alle meine Siblings, die in der selben Gruppe 353 // sind wie ich, entsprechend zuruecksetzen 354 Any aZero; 355 aZero <<= (sal_Int16)0; 356 SetSiblingPropsTo( PROPERTY_STATE, aZero ); 357 } 358 } 359 360 OReferenceValueComponent::_propertyChanged( _rEvent ); 361 } 362 363 //------------------------------------------------------------------------------ 364 Any ORadioButtonModel::translateDbColumnToControlValue() 365 { 366 return makeAny( (sal_Int16) 367 ( ( m_xColumn->getString() == getReferenceValue() ) ? STATE_CHECK : STATE_NOCHECK ) 368 ); 369 } 370 371 //------------------------------------------------------------------------------ 372 Any ORadioButtonModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const 373 { 374 Any aControlValue = OReferenceValueComponent::translateExternalValueToControlValue( _rExternalValue ); 375 sal_Int16 nState = STATE_NOCHECK; 376 if ( ( aControlValue >>= nState ) && ( nState == STATE_DONTKNOW ) ) 377 // radio buttons do not have the DONTKNOW state 378 aControlValue <<= (sal_Int16)STATE_NOCHECK; 379 return aControlValue; 380 } 381 382 //----------------------------------------------------------------------------- 383 sal_Bool ORadioButtonModel::commitControlValueToDbColumn( bool /*_bPostReset*/ ) 384 { 385 Reference< XPropertySet > xField( getField() ); 386 OSL_PRECOND( xField.is(), "ORadioButtonModel::commitControlValueToDbColumn: not bound!" ); 387 if ( xField.is() ) 388 { 389 try 390 { 391 sal_Int16 nValue = 0; 392 m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) >>= nValue; 393 if ( nValue == 1 ) 394 xField->setPropertyValue( PROPERTY_VALUE, makeAny( getReferenceValue() ) ); 395 } 396 catch(Exception&) 397 { 398 DBG_ERROR("ORadioButtonModel::commitControlValueToDbColumn: could not commit !"); 399 } 400 } 401 return sal_True; 402 } 403 404 //......................................................................... 405 } 406 //......................................................................... 407 408