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