1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: fmcontrolbordermanager.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #ifndef SVX_SOURCE_FORM_FMCONTROLBORDERMANAGER_HXX
35 #include "fmcontrolbordermanager.hxx"
38 #ifndef _SVX_FMPROP_HRC
42 /** === begin UNO includes === **/
43 #include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
44 #include <com/sun/star/awt/XTextComponent.hpp>
45 #include <com/sun/star/awt/XListBox.hpp>
46 /** === end UNO includes === **/
47 #include <tools/debug.hxx>
49 //........................................................................
52 //........................................................................
54 using namespace ::com::sun::star::uno
;
55 using namespace ::com::sun::star::awt
;
56 using namespace ::com::sun::star::form::validation
;
58 //====================================================================
60 //====================================================================
61 //--------------------------------------------------------------------
62 static void setUnderline( const Reference
< XVclWindowPeer
>& _rxPeer
, const UnderlineDescriptor
& _rUnderline
)
64 OSL_ENSURE( _rxPeer
.is(), "setUnderline: invalid peer!" );
66 // the underline type is an aspect of the font
68 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_FONT
) >>= aFont
);
69 aFont
.Underline
= _rUnderline
.nUnderlineType
;
70 _rxPeer
->setProperty( FM_PROP_FONT
, makeAny( aFont
) );
71 // the underline color is a separate property
72 _rxPeer
->setProperty( FM_PROP_TEXTLINECOLOR
, makeAny( _rUnderline
.nUnderlineColor
) );
75 //--------------------------------------------------------------------
76 static void getUnderline( const Reference
< XVclWindowPeer
>& _rxPeer
, UnderlineDescriptor
& _rUnderline
)
78 OSL_ENSURE( _rxPeer
.is(), "getUnderline: invalid peer!" );
81 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_FONT
) >>= aFont
);
82 _rUnderline
.nUnderlineType
= aFont
.Underline
;
84 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_TEXTLINECOLOR
) >>= _rUnderline
.nUnderlineColor
);
87 //--------------------------------------------------------------------
88 static void getBorder( const Reference
< XVclWindowPeer
>& _rxPeer
, BorderDescriptor
& _rBoder
)
90 OSL_ENSURE( _rxPeer
.is(), "getBorder: invalid peer!" );
92 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDER
) >>= _rBoder
.nBorderType
);
93 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDERCOLOR
) >>= _rBoder
.nBorderColor
);
96 //--------------------------------------------------------------------
97 static void setBorder( const Reference
< XVclWindowPeer
>& _rxPeer
, const BorderDescriptor
& _rBoder
)
99 OSL_ENSURE( _rxPeer
.is(), "setBorder: invalid peer!" );
101 _rxPeer
->setProperty( FM_PROP_BORDER
, makeAny( _rBoder
.nBorderType
) );
102 _rxPeer
->setProperty( FM_PROP_BORDERCOLOR
, makeAny( _rBoder
.nBorderColor
) );
105 //====================================================================
106 //= ControlBorderManager
107 //====================================================================
108 //--------------------------------------------------------------------
109 ControlBorderManager::ControlBorderManager()
110 :m_nFocusColor ( 0x000000FF )
111 ,m_nMouseHoveColor( 0x007098BE )
112 ,m_nInvalidColor ( 0x00FF0000 )
113 ,m_bDynamicBorderColors( false )
117 //--------------------------------------------------------------------
118 ControlBorderManager::~ControlBorderManager()
122 //--------------------------------------------------------------------
123 bool ControlBorderManager::canColorBorder( const Reference
< XVclWindowPeer
>& _rxPeer
)
125 OSL_PRECOND( _rxPeer
.is(), "ControlBorderManager::canColorBorder: invalid peer!" );
127 PeerBag::const_iterator aPos
= m_aColorableControls
.find( _rxPeer
);
128 if ( aPos
!= m_aColorableControls
.end() )
131 aPos
= m_aNonColorableControls
.find( _rxPeer
);
132 if ( aPos
!= m_aNonColorableControls
.end() )
135 // this peer is not yet known
137 // no border coloring for controls which are not for text input
138 // #i37434# / 2004-11-19 / frank.schoenheit@sun.com
139 Reference
< XTextComponent
> xText( _rxPeer
, UNO_QUERY
);
140 Reference
< XListBox
> xListBox( _rxPeer
, UNO_QUERY
);
141 if ( xText
.is() || xListBox
.is() )
143 sal_Int16 nBorderStyle
= VisualEffect::NONE
;
144 OSL_VERIFY( _rxPeer
->getProperty( FM_PROP_BORDER
) >>= nBorderStyle
);
145 if ( nBorderStyle
== VisualEffect::FLAT
)
146 // if you change this to also accept LOOK3D, then this would also work, but look ugly
148 m_aColorableControls
.insert( _rxPeer
);
153 m_aNonColorableControls
.insert( _rxPeer
);
157 //--------------------------------------------------------------------
158 ControlStatus
ControlBorderManager::getControlStatus( const Reference
< XControl
>& _rxControl
) SAL_THROW(())
160 ControlStatus nStatus
= CONTROL_STATUS_NONE
;
162 if ( _rxControl
.get() == m_aFocusControl
.xControl
.get() )
163 nStatus
|= CONTROL_STATUS_FOCUSED
;
165 if ( _rxControl
.get() == m_aMouseHoverControl
.xControl
.get() )
166 nStatus
|= CONTROL_STATUS_MOUSE_HOVER
;
168 if ( m_aInvalidControls
.find( ControlData( _rxControl
) ) != m_aInvalidControls
.end() )
169 nStatus
|= CONTROL_STATUS_INVALID
;
174 //--------------------------------------------------------------------
175 sal_Int32
ControlBorderManager::getControlColorByStatus( ControlStatus _nStatus
)
177 // "invalid" is ranked highest
178 if ( _nStatus
& CONTROL_STATUS_INVALID
)
179 return m_nInvalidColor
;
181 // then, "focused" is more important than ...
182 if ( _nStatus
& CONTROL_STATUS_FOCUSED
)
183 return m_nFocusColor
;
186 if ( _nStatus
& CONTROL_STATUS_MOUSE_HOVER
)
187 return m_nMouseHoveColor
;
189 OSL_ENSURE( sal_False
, "ControlBorderManager::getControlColorByStatus: invalid status!" );
193 //--------------------------------------------------------------------
194 void ControlBorderManager::updateBorderStyle( const Reference
< XControl
>& _rxControl
, const Reference
< XVclWindowPeer
>& _rxPeer
, const BorderDescriptor
& _rFallback
) SAL_THROW(())
196 OSL_PRECOND( _rxControl
.is() && _rxPeer
.is(), "ControlBorderManager::updateBorderStyle: invalid parameters!" );
198 ControlStatus nStatus
= getControlStatus( _rxControl
);
199 BorderDescriptor aBorder
;
200 aBorder
.nBorderType
= ( nStatus
== CONTROL_STATUS_NONE
)
201 ? _rFallback
.nBorderType
202 : VisualEffect::FLAT
;
203 aBorder
.nBorderColor
= ( nStatus
== CONTROL_STATUS_NONE
)
204 ? _rFallback
.nBorderColor
205 : getControlColorByStatus( nStatus
);
206 setBorder( _rxPeer
, aBorder
);
209 //--------------------------------------------------------------------
210 void ControlBorderManager::determineOriginalBorderStyle( const Reference
< XControl
>& _rxControl
, BorderDescriptor
& _rData
) const
212 _rData
= ControlData();
213 if ( m_aFocusControl
.xControl
.get() == _rxControl
.get() )
215 _rData
= m_aFocusControl
;
217 else if ( m_aMouseHoverControl
.xControl
.get() == _rxControl
.get() )
219 _rData
= m_aMouseHoverControl
;
223 ControlBag::const_iterator aPos
= m_aInvalidControls
.find( _rxControl
);
224 if ( aPos
!= m_aInvalidControls
.end() )
230 Reference
< XVclWindowPeer
> xPeer( _rxControl
->getPeer(), UNO_QUERY
);
231 getBorder( xPeer
, _rData
);
236 //--------------------------------------------------------------------
237 void ControlBorderManager::controlStatusGained( const Reference
< XInterface
>& _rxControl
, ControlData
& _rControlData
) SAL_THROW(())
239 if ( _rxControl
== _rControlData
.xControl
)
240 // nothing to do - though suspicious
243 Reference
< XControl
> xAsControl( _rxControl
, UNO_QUERY
);
244 DBG_ASSERT( xAsControl
.is(), "ControlBorderManager::controlStatusGained: invalid control!" );
245 if ( !xAsControl
.is() )
250 Reference
< XVclWindowPeer
> xPeer( xAsControl
->getPeer(), UNO_QUERY
);
251 if ( xPeer
.is() && canColorBorder( xPeer
) )
253 // remember the control and it's current border color
254 _rControlData
.xControl
.clear(); // so determineOriginalBorderStyle doesn't get confused
256 determineOriginalBorderStyle( xAsControl
, _rControlData
);
258 _rControlData
.xControl
= xAsControl
;
260 updateBorderStyle( xAsControl
, xPeer
, _rControlData
);
263 catch( const Exception
& )
265 OSL_ENSURE( sal_False
, "ControlBorderManager::controlStatusGained: caught an exception!" );
269 //--------------------------------------------------------------------
270 void ControlBorderManager::controlStatusLost( const Reference
< XInterface
>& _rxControl
, ControlData
& _rControlData
) SAL_THROW(())
272 if ( _rxControl
!= _rControlData
.xControl
)
276 OSL_PRECOND( _rControlData
.xControl
.is(), "ControlBorderManager::controlStatusLost: invalid control data - this will crash!" );
279 Reference
< XVclWindowPeer
> xPeer( _rControlData
.xControl
->getPeer(), UNO_QUERY
);
280 if ( xPeer
.is() && canColorBorder( xPeer
) )
282 ControlData
aPreviousStatus( _rControlData
);
283 _rControlData
= ControlData();
284 updateBorderStyle( aPreviousStatus
.xControl
, xPeer
, aPreviousStatus
);
287 catch( const Exception
& )
289 OSL_ENSURE( sal_False
, "ControlBorderManager::controlStatusLost: caught an exception!" );
293 //--------------------------------------------------------------------
294 void ControlBorderManager::enableDynamicBorderColor( )
296 m_bDynamicBorderColors
= true;
299 //--------------------------------------------------------------------
300 void ControlBorderManager::disableDynamicBorderColor( )
302 m_bDynamicBorderColors
= false;
306 //--------------------------------------------------------------------
307 void ControlBorderManager::setStatusColor( ControlStatus _nStatus
, sal_Int32 _nColor
)
311 case CONTROL_STATUS_FOCUSED
:
312 m_nFocusColor
= _nColor
;
314 case CONTROL_STATUS_MOUSE_HOVER
:
315 m_nMouseHoveColor
= _nColor
;
317 case CONTROL_STATUS_INVALID
:
318 m_nInvalidColor
= _nColor
;
321 OSL_ENSURE( sal_False
, "ControlBorderManager::setStatusColor: invalid status!" );
325 //--------------------------------------------------------------------
326 void ControlBorderManager::restoreAll()
328 if ( m_aFocusControl
.xControl
.is() )
329 controlStatusLost( m_aFocusControl
.xControl
, m_aFocusControl
);
330 if ( m_aMouseHoverControl
.xControl
.is() )
331 controlStatusLost( m_aMouseHoverControl
.xControl
, m_aMouseHoverControl
);
333 ControlBag aInvalidControls
;
334 m_aInvalidControls
.swap( aInvalidControls
);
336 for ( ControlBag::const_iterator loop
= aInvalidControls
.begin();
337 loop
!= aInvalidControls
.end();
341 Reference
< XVclWindowPeer
> xPeer( loop
->xControl
->getPeer(), UNO_QUERY
);
344 updateBorderStyle( loop
->xControl
, xPeer
, *loop
);
345 xPeer
->setProperty( FM_PROP_HELPTEXT
, makeAny( loop
->sOriginalHelpText
) );
346 setUnderline( xPeer
, *loop
);
351 //--------------------------------------------------------------------
352 void ControlBorderManager::focusGained( const Reference
< XInterface
>& _rxControl
) SAL_THROW(())
354 if ( m_bDynamicBorderColors
)
355 controlStatusGained( _rxControl
, m_aFocusControl
);
358 //--------------------------------------------------------------------
359 void ControlBorderManager::focusLost( const Reference
< XInterface
>& _rxControl
) SAL_THROW(())
361 if ( m_bDynamicBorderColors
)
362 controlStatusLost( _rxControl
, m_aFocusControl
);
365 //--------------------------------------------------------------------
366 void ControlBorderManager::mouseEntered( const Reference
< XInterface
>& _rxControl
) SAL_THROW(())
368 if ( m_bDynamicBorderColors
)
369 controlStatusGained( _rxControl
, m_aMouseHoverControl
);
372 //--------------------------------------------------------------------
373 void ControlBorderManager::mouseExited( const Reference
< XInterface
>& _rxControl
) SAL_THROW(())
375 if ( m_bDynamicBorderColors
)
376 controlStatusLost( _rxControl
, m_aMouseHoverControl
);
379 //--------------------------------------------------------------------
380 void ControlBorderManager::validityChanged( const Reference
< XControl
>& _rxControl
, const Reference
< XValidatableFormComponent
>& _rxValidatable
) SAL_THROW(())
384 OSL_ENSURE( _rxControl
.is(), "ControlBorderManager::validityChanged: invalid control!" );
385 OSL_ENSURE( _rxValidatable
.is(), "ControlBorderManager::validityChanged: invalid validatable!" );
387 Reference
< XVclWindowPeer
> xPeer( _rxControl
.is() ? _rxControl
->getPeer() : Reference
< XWindowPeer
>(), UNO_QUERY
);
388 if ( !xPeer
.is() || !_rxValidatable
.is() )
391 ControlData
aData( _rxControl
);
393 if ( _rxValidatable
->isValid() )
395 ControlBag::iterator aPos
= m_aInvalidControls
.find( aData
);
396 if ( aPos
!= m_aInvalidControls
.end() )
397 { // invalid before, valid now
398 ControlData
aOriginalLayout( *aPos
);
399 m_aInvalidControls
.erase( aPos
);
401 // restore all the things we used to indicate invalidity
402 if ( m_bDynamicBorderColors
)
403 updateBorderStyle( _rxControl
, xPeer
, aOriginalLayout
);
404 xPeer
->setProperty( FM_PROP_HELPTEXT
, makeAny( aOriginalLayout
.sOriginalHelpText
) );
405 setUnderline( xPeer
, aOriginalLayout
);
410 // we're here in the INVALID case
411 if ( m_aInvalidControls
.find( _rxControl
) == m_aInvalidControls
.end() )
412 { // valid before, invalid now
414 // remember the current border
415 determineOriginalBorderStyle( _rxControl
, aData
);
417 xPeer
->getProperty( FM_PROP_HELPTEXT
) >>= aData
.sOriginalHelpText
;
419 getUnderline( xPeer
, aData
);
421 m_aInvalidControls
.insert( aData
);
423 // update the border to the new invalidity
424 if ( m_bDynamicBorderColors
&& canColorBorder( xPeer
) )
425 updateBorderStyle( _rxControl
, xPeer
, aData
);
428 // and also the new font
429 setUnderline( xPeer
, UnderlineDescriptor( com::sun::star::awt::FontUnderline::WAVE
, m_nInvalidColor
) );
433 // update the explanation for invalidity (this is always done, even if the validity did not change)
434 Reference
< XValidator
> xValidator
= _rxValidatable
->getValidator();
435 OSL_ENSURE( xValidator
.is(), "ControlBorderManager::validityChanged: invalid, but no validator?" );
436 ::rtl::OUString sExplainInvalidity
= xValidator
.is() ? xValidator
->explainInvalid( _rxValidatable
->getCurrentValue() ) : ::rtl::OUString();
437 xPeer
->setProperty( FM_PROP_HELPTEXT
, makeAny( sExplainInvalidity
) );
439 catch( const Exception
& )
441 OSL_ENSURE( sal_False
, "ControlBorderManager::validityChanged: caught an exception!" );
445 //........................................................................
446 } // namespace svxform
447 //........................................................................