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 <osl/diagnose.h>
36 using namespace ::com::sun::star::uno
;
37 using namespace ::com::sun::star::awt
;
38 using namespace ::com::sun::star::form::validation
;
44 static void setUnderline( const Reference
< XVclWindowPeer
>& _rxPeer
, const UnderlineDescriptor
& _rUnderline
)
46 OSL_ENSURE( _rxPeer
.is(), "setUnderline: invalid peer!" );
48 // the underline type is an aspect of the font
50 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_FONT
) >>= aFont
);
51 aFont
.Underline
= _rUnderline
.nUnderlineType
;
52 _rxPeer
->setProperty( FM_PROP_FONT
, makeAny( aFont
) );
53 // the underline color is a separate property
54 _rxPeer
->setProperty( FM_PROP_TEXTLINECOLOR
, makeAny( _rUnderline
.nUnderlineColor
) );
58 static void getUnderline( const Reference
< XVclWindowPeer
>& _rxPeer
, UnderlineDescriptor
& _rUnderline
)
60 OSL_ENSURE( _rxPeer
.is(), "getUnderline: invalid peer!" );
63 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_FONT
) >>= aFont
);
64 _rUnderline
.nUnderlineType
= aFont
.Underline
;
66 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_TEXTLINECOLOR
) >>= _rUnderline
.nUnderlineColor
);
70 static void getBorder( const Reference
< XVclWindowPeer
>& _rxPeer
, BorderDescriptor
& _rBoder
)
72 OSL_ENSURE( _rxPeer
.is(), "getBorder: invalid peer!" );
74 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDER
) >>= _rBoder
.nBorderType
);
75 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDERCOLOR
) >>= _rBoder
.nBorderColor
);
79 static void setBorder( const Reference
< XVclWindowPeer
>& _rxPeer
, const BorderDescriptor
& _rBoder
)
81 OSL_ENSURE( _rxPeer
.is(), "setBorder: invalid peer!" );
83 _rxPeer
->setProperty( FM_PROP_BORDER
, makeAny( _rBoder
.nBorderType
) );
84 _rxPeer
->setProperty( FM_PROP_BORDERCOLOR
, makeAny( _rBoder
.nBorderColor
) );
87 ControlBorderManager::ControlBorderManager()
88 :m_nFocusColor ( 0x000000FF )
89 ,m_nMouseHoveColor( 0x007098BE )
90 ,m_nInvalidColor ( 0x00FF0000 )
91 ,m_bDynamicBorderColors( false )
96 ControlBorderManager::~ControlBorderManager()
101 bool ControlBorderManager::canColorBorder( const Reference
< XVclWindowPeer
>& _rxPeer
)
103 OSL_PRECOND( _rxPeer
.is(), "ControlBorderManager::canColorBorder: invalid peer!" );
105 PeerBag::const_iterator aPos
= m_aColorableControls
.find( _rxPeer
);
106 if ( aPos
!= m_aColorableControls
.end() )
109 aPos
= m_aNonColorableControls
.find( _rxPeer
);
110 if ( aPos
!= m_aNonColorableControls
.end() )
113 // this peer is not yet known
115 // no border coloring for controls which are not for text input
116 // #i37434# / 2004-11-19 / frank.schoenheit@sun.com
117 Reference
< XTextComponent
> xText( _rxPeer
, UNO_QUERY
);
118 Reference
< XListBox
> xListBox( _rxPeer
, UNO_QUERY
);
119 if ( xText
.is() || xListBox
.is() )
121 sal_Int16 nBorderStyle
= VisualEffect::NONE
;
122 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDER
) >>= nBorderStyle
);
123 if ( nBorderStyle
== VisualEffect::FLAT
)
124 // if you change this to also accept LOOK3D, then this would also work, but look ugly
126 m_aColorableControls
.insert( _rxPeer
);
131 m_aNonColorableControls
.insert( _rxPeer
);
136 ControlStatus
ControlBorderManager::getControlStatus( const Reference
< XControl
>& _rxControl
)
138 ControlStatus nStatus
= CONTROL_STATUS_NONE
;
140 if ( _rxControl
.get() == m_aFocusControl
.xControl
.get() )
141 nStatus
|= CONTROL_STATUS_FOCUSED
;
143 if ( _rxControl
.get() == m_aMouseHoverControl
.xControl
.get() )
144 nStatus
|= CONTROL_STATUS_MOUSE_HOVER
;
146 if ( m_aInvalidControls
.find( ControlData( _rxControl
) ) != m_aInvalidControls
.end() )
147 nStatus
|= CONTROL_STATUS_INVALID
;
153 sal_Int32
ControlBorderManager::getControlColorByStatus( ControlStatus _nStatus
)
155 // "invalid" is ranked highest
156 if ( _nStatus
& CONTROL_STATUS_INVALID
)
157 return m_nInvalidColor
;
159 // then, "focused" is more important than ...
160 if ( _nStatus
& CONTROL_STATUS_FOCUSED
)
161 return m_nFocusColor
;
164 if ( _nStatus
& CONTROL_STATUS_MOUSE_HOVER
)
165 return m_nMouseHoveColor
;
167 OSL_FAIL( "ControlBorderManager::getControlColorByStatus: invalid status!" );
172 void ControlBorderManager::updateBorderStyle( const Reference
< XControl
>& _rxControl
, const Reference
< XVclWindowPeer
>& _rxPeer
, const BorderDescriptor
& _rFallback
)
174 OSL_PRECOND( _rxControl
.is() && _rxPeer
.is(), "ControlBorderManager::updateBorderStyle: invalid parameters!" );
176 ControlStatus nStatus
= getControlStatus( _rxControl
);
177 BorderDescriptor aBorder
;
178 aBorder
.nBorderType
= ( nStatus
== CONTROL_STATUS_NONE
)
179 ? _rFallback
.nBorderType
180 : VisualEffect::FLAT
;
181 aBorder
.nBorderColor
= ( nStatus
== CONTROL_STATUS_NONE
)
182 ? _rFallback
.nBorderColor
183 : getControlColorByStatus( nStatus
);
184 setBorder( _rxPeer
, aBorder
);
188 void ControlBorderManager::determineOriginalBorderStyle( const Reference
< XControl
>& _rxControl
, BorderDescriptor
& _rData
) const
190 _rData
= ControlData();
191 if ( m_aFocusControl
.xControl
.get() == _rxControl
.get() )
193 _rData
= m_aFocusControl
;
195 else if ( m_aMouseHoverControl
.xControl
.get() == _rxControl
.get() )
197 _rData
= m_aMouseHoverControl
;
201 ControlBag::const_iterator aPos
= m_aInvalidControls
.find( _rxControl
);
202 if ( aPos
!= m_aInvalidControls
.end() )
208 Reference
< XVclWindowPeer
> xPeer( _rxControl
->getPeer(), UNO_QUERY
);
209 getBorder( xPeer
, _rData
);
215 void ControlBorderManager::controlStatusGained( const Reference
< XInterface
>& _rxControl
, ControlData
& _rControlData
)
217 if ( _rxControl
== _rControlData
.xControl
)
218 // nothing to do - though suspicious
221 Reference
< XControl
> xAsControl( _rxControl
, UNO_QUERY
);
222 DBG_ASSERT( xAsControl
.is(), "ControlBorderManager::controlStatusGained: invalid control!" );
223 if ( !xAsControl
.is() )
228 Reference
< XVclWindowPeer
> xPeer( xAsControl
->getPeer(), UNO_QUERY
);
229 if ( xPeer
.is() && canColorBorder( xPeer
) )
231 // remember the control and its current border color
232 _rControlData
.xControl
.clear(); // so determineOriginalBorderStyle doesn't get confused
234 determineOriginalBorderStyle( xAsControl
, _rControlData
);
236 _rControlData
.xControl
= xAsControl
;
238 updateBorderStyle( xAsControl
, xPeer
, _rControlData
);
241 catch( const Exception
& )
243 OSL_FAIL( "ControlBorderManager::controlStatusGained: caught an exception!" );
248 void ControlBorderManager::controlStatusLost( const Reference
< XInterface
>& _rxControl
, ControlData
& _rControlData
)
250 if ( _rxControl
!= _rControlData
.xControl
)
254 OSL_PRECOND( _rControlData
.xControl
.is(), "ControlBorderManager::controlStatusLost: invalid control data - this will crash!" );
257 Reference
< XVclWindowPeer
> xPeer( _rControlData
.xControl
->getPeer(), UNO_QUERY
);
258 if ( xPeer
.is() && canColorBorder( xPeer
) )
260 ControlData
aPreviousStatus( _rControlData
);
261 _rControlData
= ControlData();
262 updateBorderStyle( aPreviousStatus
.xControl
, xPeer
, aPreviousStatus
);
265 catch( const Exception
& )
267 OSL_FAIL( "ControlBorderManager::controlStatusLost: caught an exception!" );
272 void ControlBorderManager::enableDynamicBorderColor( )
274 m_bDynamicBorderColors
= true;
278 void ControlBorderManager::disableDynamicBorderColor( )
280 m_bDynamicBorderColors
= false;
285 void ControlBorderManager::setStatusColor( ControlStatus _nStatus
, sal_Int32 _nColor
)
289 case CONTROL_STATUS_FOCUSED
:
290 m_nFocusColor
= _nColor
;
292 case CONTROL_STATUS_MOUSE_HOVER
:
293 m_nMouseHoveColor
= _nColor
;
295 case CONTROL_STATUS_INVALID
:
296 m_nInvalidColor
= _nColor
;
299 OSL_FAIL( "ControlBorderManager::setStatusColor: invalid status!" );
304 void ControlBorderManager::restoreAll()
306 if ( m_aFocusControl
.xControl
.is() )
307 controlStatusLost( m_aFocusControl
.xControl
, m_aFocusControl
);
308 if ( m_aMouseHoverControl
.xControl
.is() )
309 controlStatusLost( m_aMouseHoverControl
.xControl
, m_aMouseHoverControl
);
311 ControlBag aInvalidControls
;
312 m_aInvalidControls
.swap( aInvalidControls
);
314 for ( ControlBag::const_iterator loop
= aInvalidControls
.begin();
315 loop
!= aInvalidControls
.end();
319 Reference
< XVclWindowPeer
> xPeer( loop
->xControl
->getPeer(), UNO_QUERY
);
322 updateBorderStyle( loop
->xControl
, xPeer
, *loop
);
323 xPeer
->setProperty( FM_PROP_HELPTEXT
, makeAny( loop
->sOriginalHelpText
) );
324 setUnderline( xPeer
, *loop
);
330 void ControlBorderManager::focusGained( const Reference
< XInterface
>& _rxControl
)
332 if ( m_bDynamicBorderColors
)
333 controlStatusGained( _rxControl
, m_aFocusControl
);
337 void ControlBorderManager::focusLost( const Reference
< XInterface
>& _rxControl
)
339 if ( m_bDynamicBorderColors
)
340 controlStatusLost( _rxControl
, m_aFocusControl
);
344 void ControlBorderManager::mouseEntered( const Reference
< XInterface
>& _rxControl
)
346 if ( m_bDynamicBorderColors
)
347 controlStatusGained( _rxControl
, m_aMouseHoverControl
);
351 void ControlBorderManager::mouseExited( const Reference
< XInterface
>& _rxControl
)
353 if ( m_bDynamicBorderColors
)
354 controlStatusLost( _rxControl
, m_aMouseHoverControl
);
358 void ControlBorderManager::validityChanged( const Reference
< XControl
>& _rxControl
, const Reference
< XValidatableFormComponent
>& _rxValidatable
)
362 OSL_ENSURE( _rxControl
.is(), "ControlBorderManager::validityChanged: invalid control!" );
363 OSL_ENSURE( _rxValidatable
.is(), "ControlBorderManager::validityChanged: invalid validatable!" );
365 Reference
< XVclWindowPeer
> xPeer( _rxControl
.is() ? _rxControl
->getPeer() : Reference
< XWindowPeer
>(), UNO_QUERY
);
366 if ( !xPeer
.is() || !_rxValidatable
.is() )
369 ControlData
aData( _rxControl
);
371 if ( _rxValidatable
->isValid() )
373 ControlBag::iterator aPos
= m_aInvalidControls
.find( aData
);
374 if ( aPos
!= m_aInvalidControls
.end() )
375 { // invalid before, valid now
376 ControlData
aOriginalLayout( *aPos
);
377 m_aInvalidControls
.erase( aPos
);
379 // restore all the things we used to indicate invalidity
380 if ( m_bDynamicBorderColors
)
381 updateBorderStyle( _rxControl
, xPeer
, aOriginalLayout
);
382 xPeer
->setProperty( FM_PROP_HELPTEXT
, makeAny( aOriginalLayout
.sOriginalHelpText
) );
383 setUnderline( xPeer
, aOriginalLayout
);
388 // we're here in the INVALID case
389 if ( m_aInvalidControls
.find( _rxControl
) == m_aInvalidControls
.end() )
390 { // valid before, invalid now
392 // remember the current border
393 determineOriginalBorderStyle( _rxControl
, aData
);
395 xPeer
->getProperty( FM_PROP_HELPTEXT
) >>= aData
.sOriginalHelpText
;
397 getUnderline( xPeer
, aData
);
399 m_aInvalidControls
.insert( aData
);
401 // update the border to the new invalidity
402 if ( m_bDynamicBorderColors
&& canColorBorder( xPeer
) )
403 updateBorderStyle( _rxControl
, xPeer
, aData
);
406 // and also the new font
407 setUnderline( xPeer
, UnderlineDescriptor( com::sun::star::awt::FontUnderline::WAVE
, m_nInvalidColor
) );
411 // update the explanation for invalidity (this is always done, even if the validity did not change)
412 Reference
< XValidator
> xValidator
= _rxValidatable
->getValidator();
413 OSL_ENSURE( xValidator
.is(), "ControlBorderManager::validityChanged: invalid, but no validator?" );
414 OUString sExplainInvalidity
= xValidator
.is() ? xValidator
->explainInvalid( _rxValidatable
->getCurrentValue() ) : OUString();
415 xPeer
->setProperty( FM_PROP_HELPTEXT
, makeAny( sExplainInvalidity
) );
417 catch( const Exception
& )
419 OSL_FAIL( "ControlBorderManager::validityChanged: caught an exception!" );
427 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */