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