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_svx.hxx" 26 27 #ifndef SVX_SOURCE_FORM_FMCONTROLBORDERMANAGER_HXX 28 #include "fmcontrolbordermanager.hxx" 29 #endif 30 31 #ifndef _SVX_FMPROP_HRC 32 #include "fmprop.hrc" 33 #endif 34 35 /** === begin UNO includes === **/ 36 #include <com/sun/star/form/validation/XValidatableFormComponent.hpp> 37 #include <com/sun/star/awt/XTextComponent.hpp> 38 #include <com/sun/star/awt/XListBox.hpp> 39 /** === end UNO includes === **/ 40 #include <tools/debug.hxx> 41 42 //........................................................................ 43 namespace svxform 44 { 45 //........................................................................ 46 47 using namespace ::com::sun::star::uno; 48 using namespace ::com::sun::star::awt; 49 using namespace ::com::sun::star::form::validation; 50 51 //==================================================================== 52 //= helper 53 //==================================================================== 54 //-------------------------------------------------------------------- setUnderline(const Reference<XVclWindowPeer> & _rxPeer,const UnderlineDescriptor & _rUnderline)55 static void setUnderline( const Reference< XVclWindowPeer >& _rxPeer, const UnderlineDescriptor& _rUnderline ) 56 { 57 OSL_ENSURE( _rxPeer.is(), "setUnderline: invalid peer!" ); 58 59 // the underline type is an aspect of the font 60 FontDescriptor aFont; 61 OSL_VERIFY( _rxPeer->getProperty( FM_PROP_FONT ) >>= aFont ); 62 aFont.Underline = _rUnderline.nUnderlineType; 63 _rxPeer->setProperty( FM_PROP_FONT, makeAny( aFont ) ); 64 // the underline color is a separate property 65 _rxPeer->setProperty( FM_PROP_TEXTLINECOLOR, makeAny( _rUnderline.nUnderlineColor ) ); 66 } 67 68 //-------------------------------------------------------------------- getUnderline(const Reference<XVclWindowPeer> & _rxPeer,UnderlineDescriptor & _rUnderline)69 static void getUnderline( const Reference< XVclWindowPeer >& _rxPeer, UnderlineDescriptor& _rUnderline ) 70 { 71 OSL_ENSURE( _rxPeer.is(), "getUnderline: invalid peer!" ); 72 73 FontDescriptor aFont; 74 OSL_VERIFY( _rxPeer->getProperty( FM_PROP_FONT ) >>= aFont ); 75 _rUnderline.nUnderlineType = aFont.Underline; 76 77 OSL_VERIFY( _rxPeer->getProperty( FM_PROP_TEXTLINECOLOR ) >>= _rUnderline.nUnderlineColor ); 78 } 79 80 //-------------------------------------------------------------------- getBorder(const Reference<XVclWindowPeer> & _rxPeer,BorderDescriptor & _rBoder)81 static void getBorder( const Reference< XVclWindowPeer >& _rxPeer, BorderDescriptor& _rBoder ) 82 { 83 OSL_ENSURE( _rxPeer.is(), "getBorder: invalid peer!" ); 84 85 OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDER ) >>= _rBoder.nBorderType ); 86 OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDERCOLOR ) >>= _rBoder.nBorderColor ); 87 } 88 89 //-------------------------------------------------------------------- setBorder(const Reference<XVclWindowPeer> & _rxPeer,const BorderDescriptor & _rBoder)90 static void setBorder( const Reference< XVclWindowPeer >& _rxPeer, const BorderDescriptor& _rBoder ) 91 { 92 OSL_ENSURE( _rxPeer.is(), "setBorder: invalid peer!" ); 93 94 _rxPeer->setProperty( FM_PROP_BORDER, makeAny( _rBoder.nBorderType ) ); 95 _rxPeer->setProperty( FM_PROP_BORDERCOLOR, makeAny( _rBoder.nBorderColor ) ); 96 } 97 98 //==================================================================== 99 //= ControlBorderManager 100 //==================================================================== 101 //-------------------------------------------------------------------- ControlBorderManager()102 ControlBorderManager::ControlBorderManager() 103 :m_nFocusColor ( 0x000000FF ) 104 ,m_nMouseHoveColor( 0x007098BE ) 105 ,m_nInvalidColor ( 0x00FF0000 ) 106 ,m_bDynamicBorderColors( false ) 107 { 108 } 109 110 //-------------------------------------------------------------------- ~ControlBorderManager()111 ControlBorderManager::~ControlBorderManager() 112 { 113 } 114 115 //-------------------------------------------------------------------- canColorBorder(const Reference<XVclWindowPeer> & _rxPeer)116 bool ControlBorderManager::canColorBorder( const Reference< XVclWindowPeer >& _rxPeer ) 117 { 118 OSL_PRECOND( _rxPeer.is(), "ControlBorderManager::canColorBorder: invalid peer!" ); 119 120 PeerBag::const_iterator aPos = m_aColorableControls.find( _rxPeer ); 121 if ( aPos != m_aColorableControls.end() ) 122 return true; 123 124 aPos = m_aNonColorableControls.find( _rxPeer ); 125 if ( aPos != m_aNonColorableControls.end() ) 126 return false; 127 128 // this peer is not yet known 129 130 // no border coloring for controls which are not for text input 131 // #i37434# / 2004-11-19 / frank.schoenheit@sun.com 132 Reference< XTextComponent > xText( _rxPeer, UNO_QUERY ); 133 Reference< XListBox > xListBox( _rxPeer, UNO_QUERY ); 134 if ( xText.is() || xListBox.is() ) 135 { 136 sal_Int16 nBorderStyle = VisualEffect::NONE; 137 OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDER ) >>= nBorderStyle ); 138 if ( nBorderStyle == VisualEffect::FLAT ) 139 // if you change this to also accept LOOK3D, then this would also work, but look ugly 140 { 141 m_aColorableControls.insert( _rxPeer ); 142 return true; 143 } 144 } 145 146 m_aNonColorableControls.insert( _rxPeer ); 147 return false; 148 } 149 150 //-------------------------------------------------------------------- getControlStatus(const Reference<XControl> & _rxControl)151 ControlStatus ControlBorderManager::getControlStatus( const Reference< XControl >& _rxControl ) SAL_THROW(()) 152 { 153 ControlStatus nStatus = CONTROL_STATUS_NONE; 154 155 if ( _rxControl.get() == m_aFocusControl.xControl.get() ) 156 nStatus |= CONTROL_STATUS_FOCUSED; 157 158 if ( _rxControl.get() == m_aMouseHoverControl.xControl.get() ) 159 nStatus |= CONTROL_STATUS_MOUSE_HOVER; 160 161 if ( m_aInvalidControls.find( ControlData( _rxControl ) ) != m_aInvalidControls.end() ) 162 nStatus |= CONTROL_STATUS_INVALID; 163 164 return nStatus; 165 } 166 167 //-------------------------------------------------------------------- getControlColorByStatus(ControlStatus _nStatus)168 sal_Int32 ControlBorderManager::getControlColorByStatus( ControlStatus _nStatus ) 169 { 170 // "invalid" is ranked highest 171 if ( _nStatus & CONTROL_STATUS_INVALID ) 172 return m_nInvalidColor; 173 174 // then, "focused" is more important than ... 175 if ( _nStatus & CONTROL_STATUS_FOCUSED ) 176 return m_nFocusColor; 177 178 // ... "mouse over" 179 if ( _nStatus & CONTROL_STATUS_MOUSE_HOVER ) 180 return m_nMouseHoveColor; 181 182 OSL_ENSURE( sal_False, "ControlBorderManager::getControlColorByStatus: invalid status!" ); 183 return 0x00000000; 184 } 185 186 //-------------------------------------------------------------------- updateBorderStyle(const Reference<XControl> & _rxControl,const Reference<XVclWindowPeer> & _rxPeer,const BorderDescriptor & _rFallback)187 void ControlBorderManager::updateBorderStyle( const Reference< XControl >& _rxControl, const Reference< XVclWindowPeer >& _rxPeer, const BorderDescriptor& _rFallback ) SAL_THROW(()) 188 { 189 OSL_PRECOND( _rxControl.is() && _rxPeer.is(), "ControlBorderManager::updateBorderStyle: invalid parameters!" ); 190 191 ControlStatus nStatus = getControlStatus( _rxControl ); 192 BorderDescriptor aBorder; 193 aBorder.nBorderType = ( nStatus == CONTROL_STATUS_NONE ) 194 ? _rFallback.nBorderType 195 : VisualEffect::FLAT; 196 aBorder.nBorderColor = ( nStatus == CONTROL_STATUS_NONE ) 197 ? _rFallback.nBorderColor 198 : getControlColorByStatus( nStatus ); 199 setBorder( _rxPeer, aBorder ); 200 } 201 202 //-------------------------------------------------------------------- determineOriginalBorderStyle(const Reference<XControl> & _rxControl,BorderDescriptor & _rData) const203 void ControlBorderManager::determineOriginalBorderStyle( const Reference< XControl >& _rxControl, BorderDescriptor& _rData ) const 204 { 205 _rData = ControlData(); 206 if ( m_aFocusControl.xControl.get() == _rxControl.get() ) 207 { 208 _rData = m_aFocusControl; 209 } 210 else if ( m_aMouseHoverControl.xControl.get() == _rxControl.get() ) 211 { 212 _rData = m_aMouseHoverControl; 213 } 214 else 215 { 216 ControlBag::const_iterator aPos = m_aInvalidControls.find( _rxControl ); 217 if ( aPos != m_aInvalidControls.end() ) 218 { 219 _rData = *aPos; 220 } 221 else 222 { 223 Reference< XVclWindowPeer > xPeer( _rxControl->getPeer(), UNO_QUERY ); 224 getBorder( xPeer, _rData ); 225 } 226 } 227 } 228 229 //-------------------------------------------------------------------- controlStatusGained(const Reference<XInterface> & _rxControl,ControlData & _rControlData)230 void ControlBorderManager::controlStatusGained( const Reference< XInterface >& _rxControl, ControlData& _rControlData ) SAL_THROW(()) 231 { 232 if ( _rxControl == _rControlData.xControl ) 233 // nothing to do - though suspicious 234 return; 235 236 Reference< XControl > xAsControl( _rxControl, UNO_QUERY ); 237 DBG_ASSERT( xAsControl.is(), "ControlBorderManager::controlStatusGained: invalid control!" ); 238 if ( !xAsControl.is() ) 239 return; 240 241 try 242 { 243 Reference< XVclWindowPeer > xPeer( xAsControl->getPeer(), UNO_QUERY ); 244 if ( xPeer.is() && canColorBorder( xPeer ) ) 245 { 246 // remember the control and it's current border color 247 _rControlData.xControl.clear(); // so determineOriginalBorderStyle doesn't get confused 248 249 determineOriginalBorderStyle( xAsControl, _rControlData ); 250 251 _rControlData.xControl = xAsControl; 252 253 updateBorderStyle( xAsControl, xPeer, _rControlData ); 254 } 255 } 256 catch( const Exception& ) 257 { 258 OSL_ENSURE( sal_False, "ControlBorderManager::controlStatusGained: caught an exception!" ); 259 } 260 } 261 262 //-------------------------------------------------------------------- controlStatusLost(const Reference<XInterface> & _rxControl,ControlData & _rControlData)263 void ControlBorderManager::controlStatusLost( const Reference< XInterface >& _rxControl, ControlData& _rControlData ) SAL_THROW(()) 264 { 265 if ( _rxControl != _rControlData.xControl ) 266 // nothing to do 267 return; 268 269 OSL_PRECOND( _rControlData.xControl.is(), "ControlBorderManager::controlStatusLost: invalid control data - this will crash!" ); 270 try 271 { 272 Reference< XVclWindowPeer > xPeer( _rControlData.xControl->getPeer(), UNO_QUERY ); 273 if ( xPeer.is() && canColorBorder( xPeer ) ) 274 { 275 ControlData aPreviousStatus( _rControlData ); 276 _rControlData = ControlData(); 277 updateBorderStyle( aPreviousStatus.xControl, xPeer, aPreviousStatus ); 278 } 279 } 280 catch( const Exception& ) 281 { 282 OSL_ENSURE( sal_False, "ControlBorderManager::controlStatusLost: caught an exception!" ); 283 } 284 } 285 286 //-------------------------------------------------------------------- enableDynamicBorderColor()287 void ControlBorderManager::enableDynamicBorderColor( ) 288 { 289 m_bDynamicBorderColors = true; 290 } 291 292 //-------------------------------------------------------------------- disableDynamicBorderColor()293 void ControlBorderManager::disableDynamicBorderColor( ) 294 { 295 m_bDynamicBorderColors = false; 296 restoreAll(); 297 } 298 299 //-------------------------------------------------------------------- setStatusColor(ControlStatus _nStatus,sal_Int32 _nColor)300 void ControlBorderManager::setStatusColor( ControlStatus _nStatus, sal_Int32 _nColor ) 301 { 302 switch ( _nStatus ) 303 { 304 case CONTROL_STATUS_FOCUSED: 305 m_nFocusColor = _nColor; 306 break; 307 case CONTROL_STATUS_MOUSE_HOVER: 308 m_nMouseHoveColor = _nColor; 309 break; 310 case CONTROL_STATUS_INVALID: 311 m_nInvalidColor = _nColor; 312 break; 313 default: 314 OSL_ENSURE( sal_False, "ControlBorderManager::setStatusColor: invalid status!" ); 315 } 316 } 317 318 //-------------------------------------------------------------------- restoreAll()319 void ControlBorderManager::restoreAll() 320 { 321 if ( m_aFocusControl.xControl.is() ) 322 controlStatusLost( m_aFocusControl.xControl, m_aFocusControl ); 323 if ( m_aMouseHoverControl.xControl.is() ) 324 controlStatusLost( m_aMouseHoverControl.xControl, m_aMouseHoverControl ); 325 326 ControlBag aInvalidControls; 327 m_aInvalidControls.swap( aInvalidControls ); 328 329 for ( ControlBag::const_iterator loop = aInvalidControls.begin(); 330 loop != aInvalidControls.end(); 331 ++loop 332 ) 333 { 334 Reference< XVclWindowPeer > xPeer( loop->xControl->getPeer(), UNO_QUERY ); 335 if ( xPeer.is() ) 336 { 337 updateBorderStyle( loop->xControl, xPeer, *loop ); 338 xPeer->setProperty( FM_PROP_HELPTEXT, makeAny( loop->sOriginalHelpText ) ); 339 setUnderline( xPeer, *loop ); 340 } 341 } 342 } 343 344 //-------------------------------------------------------------------- focusGained(const Reference<XInterface> & _rxControl)345 void ControlBorderManager::focusGained( const Reference< XInterface >& _rxControl ) SAL_THROW(()) 346 { 347 if ( m_bDynamicBorderColors ) 348 controlStatusGained( _rxControl, m_aFocusControl ); 349 } 350 351 //-------------------------------------------------------------------- focusLost(const Reference<XInterface> & _rxControl)352 void ControlBorderManager::focusLost( const Reference< XInterface >& _rxControl ) SAL_THROW(()) 353 { 354 if ( m_bDynamicBorderColors ) 355 controlStatusLost( _rxControl, m_aFocusControl ); 356 } 357 358 //-------------------------------------------------------------------- mouseEntered(const Reference<XInterface> & _rxControl)359 void ControlBorderManager::mouseEntered( const Reference< XInterface >& _rxControl ) SAL_THROW(()) 360 { 361 if ( m_bDynamicBorderColors ) 362 controlStatusGained( _rxControl, m_aMouseHoverControl ); 363 } 364 365 //-------------------------------------------------------------------- mouseExited(const Reference<XInterface> & _rxControl)366 void ControlBorderManager::mouseExited( const Reference< XInterface >& _rxControl ) SAL_THROW(()) 367 { 368 if ( m_bDynamicBorderColors ) 369 controlStatusLost( _rxControl, m_aMouseHoverControl ); 370 } 371 372 //-------------------------------------------------------------------- validityChanged(const Reference<XControl> & _rxControl,const Reference<XValidatableFormComponent> & _rxValidatable)373 void ControlBorderManager::validityChanged( const Reference< XControl >& _rxControl, const Reference< XValidatableFormComponent >& _rxValidatable ) SAL_THROW(()) 374 { 375 try 376 { 377 OSL_ENSURE( _rxControl.is(), "ControlBorderManager::validityChanged: invalid control!" ); 378 OSL_ENSURE( _rxValidatable.is(), "ControlBorderManager::validityChanged: invalid validatable!" ); 379 380 Reference< XVclWindowPeer > xPeer( _rxControl.is() ? _rxControl->getPeer() : Reference< XWindowPeer >(), UNO_QUERY ); 381 if ( !xPeer.is() || !_rxValidatable.is() ) 382 return; 383 384 ControlData aData( _rxControl ); 385 386 if ( _rxValidatable->isValid() ) 387 { 388 ControlBag::iterator aPos = m_aInvalidControls.find( aData ); 389 if ( aPos != m_aInvalidControls.end() ) 390 { // invalid before, valid now 391 ControlData aOriginalLayout( *aPos ); 392 m_aInvalidControls.erase( aPos ); 393 394 // restore all the things we used to indicate invalidity 395 if ( m_bDynamicBorderColors ) 396 updateBorderStyle( _rxControl, xPeer, aOriginalLayout ); 397 xPeer->setProperty( FM_PROP_HELPTEXT, makeAny( aOriginalLayout.sOriginalHelpText ) ); 398 setUnderline( xPeer, aOriginalLayout ); 399 } 400 return; 401 } 402 403 // we're here in the INVALID case 404 if ( m_aInvalidControls.find( _rxControl ) == m_aInvalidControls.end() ) 405 { // valid before, invalid now 406 407 // remember the current border 408 determineOriginalBorderStyle( _rxControl, aData ); 409 // and tool tip 410 xPeer->getProperty( FM_PROP_HELPTEXT ) >>= aData.sOriginalHelpText; 411 // and font 412 getUnderline( xPeer, aData ); 413 414 m_aInvalidControls.insert( aData ); 415 416 // update the border to the new invalidity 417 if ( m_bDynamicBorderColors && canColorBorder( xPeer ) ) 418 updateBorderStyle( _rxControl, xPeer, aData ); 419 else 420 { 421 // and also the new font 422 setUnderline( xPeer, UnderlineDescriptor( com::sun::star::awt::FontUnderline::WAVE, m_nInvalidColor ) ); 423 } 424 } 425 426 // update the explanation for invalidity (this is always done, even if the validity did not change) 427 Reference< XValidator > xValidator = _rxValidatable->getValidator(); 428 OSL_ENSURE( xValidator.is(), "ControlBorderManager::validityChanged: invalid, but no validator?" ); 429 ::rtl::OUString sExplainInvalidity = xValidator.is() ? xValidator->explainInvalid( _rxValidatable->getCurrentValue() ) : ::rtl::OUString(); 430 xPeer->setProperty( FM_PROP_HELPTEXT, makeAny( sExplainInvalidity ) ); 431 } 432 catch( const Exception& ) 433 { 434 OSL_ENSURE( sal_False, "ControlBorderManager::validityChanged: caught an exception!" ); 435 } 436 } 437 438 //........................................................................ 439 } // namespace svxform 440 //........................................................................ 441 442