LibreOffice Module svx (master)  1
fmcontrolbordermanager.cxx
Go to the documentation of this file.
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 
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 
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 
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 
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 
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 
89  :m_nFocusColor ( 0x000000FF )
90  ,m_nMouseHoveColor( 0x007098BE )
91  ,m_nInvalidColor ( 0x00FF0000 )
92  ,m_bDynamicBorderColors( false )
93  {
94  }
95 
96 
98  {
99  }
100 
101 
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 
137  ControlStatus ControlBorderManager::getControlStatus( const Reference< XControl >& _rxControl )
138  {
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 
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 
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 
189  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 
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 
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 
274  {
275  m_bDynamicBorderColors = true;
276  }
277 
278 
280  {
281  m_bDynamicBorderColors = false;
282  restoreAll();
283  }
284 
285 
287  {
288  switch ( _nStatus )
289  {
291  m_nFocusColor = _nColor;
292  break;
294  m_nMouseHoveColor = _nColor;
295  break;
297  m_nInvalidColor = _nColor;
298  break;
299  default:
300  OSL_FAIL( "ControlBorderManager::setStatusColor: invalid status!" );
301  }
302  }
303 
304 
306  {
307  if ( m_aFocusControl.xControl.is() )
309  if ( m_aMouseHoverControl.xControl.is() )
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 
329  {
331  controlStatusGained( _rxControl, m_aFocusControl );
332  }
333 
334 
336  {
338  controlStatusLost( _rxControl, m_aFocusControl );
339  }
340 
341 
343  {
346  }
347 
348 
350  {
353  }
354 
355 
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
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: */
const char aData[]
void mouseExited(const css::uno::Reference< css::uno::XInterface > &_rxControl)
static void setUnderline(const Reference< XVclWindowPeer > &_rxPeer, const UnderlineDescriptor &_rUnderline)
#define FM_PROP_BORDER
Definition: fmprop.hxx:95
void focusGained(const css::uno::Reference< css::uno::XInterface > &_rxControl)
ControlStatus getControlStatus(const css::uno::Reference< css::awt::XControl > &_rxControl)
determines the status of the given control
bool canColorBorder(const css::uno::Reference< css::awt::XVclWindowPeer > &_rxPeer)
determines whether the border of a given peer can be colored
void updateBorderStyle(const css::uno::Reference< css::awt::XControl > &_rxControl, const css::uno::Reference< css::awt::XVclWindowPeer > &_rxPeer, const BorderDescriptor &_rFallback)
sets the border color for a given control, depending on its status
void determineOriginalBorderStyle(const css::uno::Reference< css::awt::XControl > &_rxControl, BorderDescriptor &_rData) const
determines the to-be-remembered original border color and type for a control
void disableDynamicBorderColor()
disables dynamic border color for the controls
void restoreAll()
restores all colors of all controls where we possibly changed them
static void getBorder(const Reference< XVclWindowPeer > &_rxPeer, BorderDescriptor &_rBorder)
::std::set< ControlData, ControlDataCompare > ControlBag
static void setBorder(const Reference< XVclWindowPeer > &_rxPeer, const BorderDescriptor &_rBorder)
#define TOOLS_WARN_EXCEPTION(area, stream)
void controlStatusGained(const css::uno::Reference< css::uno::XInterface > &_rxControl, ControlData &_rControlData)
called when a control got one of the two possible statuses (focused, and hovered with the mouse) ...
#define DBG_ASSERT(sCon, aError)
#define FM_PROP_FONT
Definition: fmprop.hxx:92
void enableDynamicBorderColor()
enables dynamic border color for the controls
void mouseEntered(const css::uno::Reference< css::uno::XInterface > &_rxControl)
void validityChanged(const css::uno::Reference< css::awt::XControl > &_rxControl, const css::uno::Reference< css::form::validation::XValidatableFormComponent > &_rxValidatable)
#define FM_PROP_BORDERCOLOR
Definition: fmprop.hxx:137
#define FM_PROP_TEXTLINECOLOR
Definition: fmprop.hxx:131
static void getUnderline(const Reference< XVclWindowPeer > &_rxPeer, UnderlineDescriptor &_rUnderline)
#define FM_PROP_HELPTEXT
Definition: fmprop.hxx:98
void controlStatusLost(const css::uno::Reference< css::uno::XInterface > &_rxControl, ControlData &_rControlData)
called when a control lost one of the two possible statuses (focused, and hovered with the mouse) ...
void setStatusColor(ControlStatus _nStatus, Color _nColor)
sets a color to be used for a given status
class FmSearchEngine - Impl class for FmSearchDialog
void focusLost(const css::uno::Reference< css::uno::XInterface > &_rxControl)
Color getControlColorByStatus(ControlStatus _eStatus)
retrieves the color associated with a given ControlStatus
css::uno::Reference< css::awt::XControl > xControl
css::uno::Any SAL_CALL makeAny(const SharedUNOComponent< INTERFACE, COMPONENT > &value)