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