1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <fmcontrolbordermanager.hxx>
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 <comphelper/diagnose_ex.hxx>
30 #include <osl/diagnose.h>
37 using namespace ::com::sun::star::uno
;
38 using namespace ::com::sun::star::awt
;
39 using namespace ::com::sun::star::form::validation
;
45 static void setUnderline( const Reference
< XVclWindowPeer
>& _rxPeer
, const UnderlineDescriptor
& _rUnderline
)
47 OSL_ENSURE( _rxPeer
.is(), "setUnderline: invalid peer!" );
49 // the underline type is an aspect of the font
51 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_FONT
) >>= aFont
);
52 aFont
.Underline
= _rUnderline
.nUnderlineType
;
53 _rxPeer
->setProperty( FM_PROP_FONT
, Any( aFont
) );
54 // the underline color is a separate property
55 _rxPeer
->setProperty( FM_PROP_TEXTLINECOLOR
, Any( _rUnderline
.nUnderlineColor
) );
59 static void getUnderline( const Reference
< XVclWindowPeer
>& _rxPeer
, UnderlineDescriptor
& _rUnderline
)
61 OSL_ENSURE( _rxPeer
.is(), "getUnderline: invalid peer!" );
64 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_FONT
) >>= aFont
);
65 _rUnderline
.nUnderlineType
= aFont
.Underline
;
67 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_TEXTLINECOLOR
) >>= _rUnderline
.nUnderlineColor
);
71 static void getBorder( const Reference
< XVclWindowPeer
>& _rxPeer
, BorderDescriptor
& _rBorder
)
73 OSL_ENSURE( _rxPeer
.is(), "getBorder: invalid peer!" );
75 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDER
) >>= _rBorder
.nBorderType
);
76 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDERCOLOR
) >>= _rBorder
.nBorderColor
);
80 static void setBorder( const Reference
< XVclWindowPeer
>& _rxPeer
, const BorderDescriptor
& _rBorder
)
82 OSL_ENSURE( _rxPeer
.is(), "setBorder: invalid peer!" );
84 _rxPeer
->setProperty( FM_PROP_BORDER
, Any( _rBorder
.nBorderType
) );
85 _rxPeer
->setProperty( FM_PROP_BORDERCOLOR
, Any( _rBorder
.nBorderColor
) );
88 ControlBorderManager::ControlBorderManager()
89 :m_nFocusColor ( 0x000000FF )
90 ,m_nMouseHoveColor( 0x007098BE )
91 ,m_nInvalidColor ( 0x00FF0000 )
92 ,m_bDynamicBorderColors( false )
97 ControlBorderManager::~ControlBorderManager()
102 bool ControlBorderManager::canColorBorder( const Reference
< XVclWindowPeer
>& _rxPeer
)
104 OSL_PRECOND( _rxPeer
.is(), "ControlBorderManager::canColorBorder: invalid peer!" );
106 PeerBag::const_iterator aPos
= m_aColorableControls
.find( _rxPeer
);
107 if ( aPos
!= m_aColorableControls
.end() )
110 aPos
= m_aNonColorableControls
.find( _rxPeer
);
111 if ( aPos
!= m_aNonColorableControls
.end() )
114 // this peer is not yet known
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() )
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
127 m_aColorableControls
.insert( _rxPeer
);
132 m_aNonColorableControls
.insert( _rxPeer
);
137 ControlStatus
ControlBorderManager::getControlStatus( const Reference
< XControl
>& _rxControl
)
139 ControlStatus nStatus
= ControlStatus::NONE
;
141 if ( _rxControl
.get() == m_aFocusControl
.xControl
.get() )
142 nStatus
|= ControlStatus::Focused
;
144 if ( _rxControl
.get() == m_aMouseHoverControl
.xControl
.get() )
145 nStatus
|= ControlStatus::MouseHover
;
147 if ( m_aInvalidControls
.find( ControlData( _rxControl
) ) != m_aInvalidControls
.end() )
148 nStatus
|= ControlStatus::Invalid
;
154 Color
ControlBorderManager::getControlColorByStatus( ControlStatus _nStatus
) const
156 // "invalid" is ranked highest
157 if ( _nStatus
& ControlStatus::Invalid
)
158 return m_nInvalidColor
;
160 // then, "focused" is more important than ...
161 if ( _nStatus
& ControlStatus::Focused
)
162 return m_nFocusColor
;
165 if ( _nStatus
& ControlStatus::MouseHover
)
166 return m_nMouseHoveColor
;
168 OSL_FAIL( "ControlBorderManager::getControlColorByStatus: invalid status!" );
173 void ControlBorderManager::updateBorderStyle( const Reference
< XControl
>& _rxControl
, const Reference
< XVclWindowPeer
>& _rxPeer
, const BorderDescriptor
& _rFallback
)
175 OSL_PRECOND( _rxControl
.is() && _rxPeer
.is(), "ControlBorderManager::updateBorderStyle: invalid parameters!" );
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
);
189 void ControlBorderManager::determineOriginalBorderStyle( const Reference
< XControl
>& _rxControl
, BorderDescriptor
& _rData
) const
191 _rData
= ControlData();
192 if ( m_aFocusControl
.xControl
.get() == _rxControl
.get() )
194 _rData
= m_aFocusControl
;
196 else if ( m_aMouseHoverControl
.xControl
.get() == _rxControl
.get() )
198 _rData
= m_aMouseHoverControl
;
202 ControlBag::const_iterator aPos
= m_aInvalidControls
.find( _rxControl
);
203 if ( aPos
!= m_aInvalidControls
.end() )
209 Reference
< XVclWindowPeer
> xPeer( _rxControl
->getPeer(), UNO_QUERY
);
210 getBorder( xPeer
, _rData
);
216 void ControlBorderManager::controlStatusGained( const Reference
< XInterface
>& _rxControl
, ControlData
& _rControlData
)
218 if ( _rxControl
== _rControlData
.xControl
)
219 // nothing to do - though suspicious
222 Reference
< XControl
> xAsControl( _rxControl
, UNO_QUERY
);
223 DBG_ASSERT( xAsControl
.is(), "ControlBorderManager::controlStatusGained: invalid control!" );
224 if ( !xAsControl
.is() )
229 Reference
< XVclWindowPeer
> xPeer( xAsControl
->getPeer(), UNO_QUERY
);
230 if ( xPeer
.is() && canColorBorder( xPeer
) )
232 // remember the control and its current border color
233 _rControlData
.xControl
.clear(); // so determineOriginalBorderStyle doesn't get confused
235 determineOriginalBorderStyle( xAsControl
, _rControlData
);
237 _rControlData
.xControl
= xAsControl
;
239 updateBorderStyle( xAsControl
, xPeer
, _rControlData
);
242 catch( const Exception
& )
244 TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::controlStatusGained" );
249 void ControlBorderManager::controlStatusLost( const Reference
< XInterface
>& _rxControl
, ControlData
& _rControlData
)
251 if ( _rxControl
!= _rControlData
.xControl
)
255 OSL_PRECOND( _rControlData
.xControl
.is(), "ControlBorderManager::controlStatusLost: invalid control data - this will crash!" );
258 Reference
< XVclWindowPeer
> xPeer( _rControlData
.xControl
->getPeer(), UNO_QUERY
);
259 if ( xPeer
.is() && canColorBorder( xPeer
) )
261 ControlData
aPreviousStatus( _rControlData
);
262 _rControlData
= ControlData();
263 updateBorderStyle( aPreviousStatus
.xControl
, xPeer
, aPreviousStatus
);
266 catch( const Exception
& )
268 TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::controlStatusLost" );
273 void ControlBorderManager::enableDynamicBorderColor( )
275 m_bDynamicBorderColors
= true;
279 void ControlBorderManager::disableDynamicBorderColor( )
281 m_bDynamicBorderColors
= false;
286 void ControlBorderManager::setStatusColor( ControlStatus _nStatus
, Color _nColor
)
290 case ControlStatus::Focused
:
291 m_nFocusColor
= _nColor
;
293 case ControlStatus::MouseHover
:
294 m_nMouseHoveColor
= _nColor
;
296 case ControlStatus::Invalid
:
297 m_nInvalidColor
= _nColor
;
300 OSL_FAIL( "ControlBorderManager::setStatusColor: invalid status!" );
305 void ControlBorderManager::restoreAll()
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
);
312 ControlBag aInvalidControls
;
313 m_aInvalidControls
.swap( aInvalidControls
);
315 for (const auto& rControl
: aInvalidControls
)
317 Reference
< XVclWindowPeer
> xPeer( rControl
.xControl
->getPeer(), UNO_QUERY
);
320 updateBorderStyle( rControl
.xControl
, xPeer
, rControl
);
321 xPeer
->setProperty( FM_PROP_HELPTEXT
, Any( rControl
.sOriginalHelpText
) );
322 setUnderline( xPeer
, rControl
);
328 void ControlBorderManager::focusGained( const Reference
< XInterface
>& _rxControl
)
330 if ( m_bDynamicBorderColors
)
331 controlStatusGained( _rxControl
, m_aFocusControl
);
335 void ControlBorderManager::focusLost( const Reference
< XInterface
>& _rxControl
)
337 if ( m_bDynamicBorderColors
)
338 controlStatusLost( _rxControl
, m_aFocusControl
);
342 void ControlBorderManager::mouseEntered( const Reference
< XInterface
>& _rxControl
)
344 if ( m_bDynamicBorderColors
)
345 controlStatusGained( _rxControl
, m_aMouseHoverControl
);
349 void ControlBorderManager::mouseExited( const Reference
< XInterface
>& _rxControl
)
351 if ( m_bDynamicBorderColors
)
352 controlStatusLost( _rxControl
, m_aMouseHoverControl
);
356 void ControlBorderManager::validityChanged( const Reference
< XControl
>& _rxControl
, const Reference
< XValidatableFormComponent
>& _rxValidatable
)
360 OSL_ENSURE( _rxControl
.is(), "ControlBorderManager::validityChanged: invalid control!" );
361 OSL_ENSURE( _rxValidatable
.is(), "ControlBorderManager::validityChanged: invalid validatable!" );
363 Reference
< XVclWindowPeer
> xPeer( _rxControl
.is() ? _rxControl
->getPeer() : Reference
< XWindowPeer
>(), UNO_QUERY
);
364 if ( !xPeer
.is() || !_rxValidatable
.is() )
367 ControlData
aData( _rxControl
);
369 if ( _rxValidatable
->isValid() )
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
);
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
, Any( aOriginalLayout
.sOriginalHelpText
) );
381 setUnderline( xPeer
, aOriginalLayout
);
386 // we're here in the INVALID case
387 if ( m_aInvalidControls
.find( _rxControl
) == m_aInvalidControls
.end() )
388 { // valid before, invalid now
390 // remember the current border
391 determineOriginalBorderStyle( _rxControl
, aData
);
393 xPeer
->getProperty( FM_PROP_HELPTEXT
) >>= aData
.sOriginalHelpText
;
395 getUnderline( xPeer
, aData
);
397 m_aInvalidControls
.insert( aData
);
399 // update the border to the new invalidity
400 if ( m_bDynamicBorderColors
&& canColorBorder( xPeer
) )
401 updateBorderStyle( _rxControl
, xPeer
, aData
);
404 // and also the new font
405 setUnderline( xPeer
, UnderlineDescriptor( css::awt::FontUnderline::WAVE
, m_nInvalidColor
) );
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
, Any( sExplainInvalidity
) );
415 catch( const Exception
& )
417 TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::validityChanged" );
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */