KiCad PCB EDA Suite
unit_binder.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014-2015 CERN
5  * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * Author: Maciej Suminski <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <wx/clipbrd.h>
27 #include <wx/stattext.h>
28 #include <wx/textentry.h>
29 #include <limits>
30 #include <base_units.h>
31 #include <eda_draw_frame.h>
32 #include <confirm.h>
33 
34 #include "widgets/unit_binder.h"
35 
36 
37 wxDEFINE_EVENT( DELAY_FOCUS, wxCommandEvent );
38 
39 
40 UNIT_BINDER::UNIT_BINDER( EDA_DRAW_FRAME* aParent, wxStaticText* aLabel, wxWindow* aValueCtrl,
41  wxStaticText* aUnitLabel, bool allowEval ) :
42  m_frame( aParent ),
43  m_label( aLabel ),
44  m_valueCtrl( aValueCtrl ),
45  m_unitLabel( aUnitLabel ),
46  m_negativeZero( false ),
47  m_dataType( EDA_DATA_TYPE::DISTANCE ),
48  m_precision( 0 ),
49  m_eval( aParent->GetUserUnits() ),
50  m_originTransforms( aParent->GetOriginTransforms() ),
51  m_coordType( ORIGIN_TRANSFORMS::NOT_A_COORD )
52 {
53  m_units = aParent->GetUserUnits();
54  m_allowEval = allowEval && dynamic_cast<wxTextEntry*>( m_valueCtrl );
55  m_needsEval = false;
56  m_selStart = 0;
57  m_selEnd = 0;
58 
59  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
60 
61  if( textEntry )
62  {
63  // Use ChangeValue() instead of SetValue() so we don't generate events.
64  if( m_negativeZero )
65  textEntry->ChangeValue( wxT( "-0" ) );
66  else
67  textEntry->ChangeValue( wxT( "0" ) );
68  }
69 
70  if( m_unitLabel )
72 
73  m_valueCtrl->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ),
74  nullptr, this );
75  m_valueCtrl->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ),
76  nullptr, this );
77  Connect( DELAY_FOCUS, wxCommandEventHandler( UNIT_BINDER::delayedFocusHandler ), nullptr,
78  this );
79 
80  m_frame->Connect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
81  nullptr, this );
82 }
83 
84 
86 {
87  m_frame->Disconnect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
88  nullptr, this );
89 }
90 
91 
93 {
94  m_units = aUnits;
95 
96  if( m_unitLabel )
98 }
99 
100 
101 void UNIT_BINDER::SetPrecision( int aLength )
102 {
103  m_precision = std::min( aLength, 6 );
104 }
105 
106 
108 {
109  m_dataType = aDataType;
110 
111  if( m_unitLabel )
113 }
114 
115 
116 void UNIT_BINDER::onUnitsChanged( wxCommandEvent& aEvent )
117 {
121  {
122  int temp = (int) GetValue();
123 
125 
126  SetValue( temp );
127  }
128 
129  aEvent.Skip();
130 }
131 
132 
133 void UNIT_BINDER::onSetFocus( wxFocusEvent& aEvent )
134 {
135  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
136 
137  if( m_allowEval && textEntry )
138  {
139  wxString oldStr = m_eval.OriginalText();
140 
141  if( oldStr.length() && oldStr != textEntry->GetValue() )
142  {
143  textEntry->SetValue( oldStr );
144  textEntry->SetSelection( m_selStart, m_selEnd );
145  }
146 
147  m_needsEval = true;
148  }
149 
150  aEvent.Skip();
151 }
152 
153 
154 void UNIT_BINDER::onKillFocus( wxFocusEvent& aEvent )
155 {
156  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
157 
158  if( m_allowEval && textEntry )
159  {
160  if( m_eval.Process( textEntry->GetValue() ) )
161  {
162  textEntry->GetSelection( &m_selStart, &m_selEnd );
163  wxString sel = textEntry->GetStringSelection();
164 
165  textEntry->ChangeValue( m_eval.Result() );
166 
167 #ifdef __WXGTK__
168  // Manually copy the selected text to the primary selection clipboard
169  if( wxTheClipboard->Open() )
170  {
171  bool clipTarget = wxTheClipboard->IsUsingPrimarySelection();
172  wxTheClipboard->UsePrimarySelection( true );
173  wxTheClipboard->SetData( new wxTextDataObject( sel ) );
174  wxTheClipboard->UsePrimarySelection( clipTarget );
175  wxTheClipboard->Close();
176  }
177 #endif
178  }
179 
180  m_needsEval = false;
181  }
182 
183  aEvent.Skip();
184 }
185 
186 
187 wxString valueDescriptionFromLabel( wxStaticText* aLabel )
188 {
189  wxString desc = aLabel->GetLabel();
190 
191  desc.EndsWith( wxT( ":" ), &desc );
192  return desc;
193 }
194 
195 
196 void UNIT_BINDER::delayedFocusHandler( wxCommandEvent& )
197 {
198  if( !m_errorMessage.IsEmpty() )
199  DisplayError( m_valueCtrl->GetParent(), m_errorMessage );
200 
201  m_errorMessage = wxEmptyString;
202  m_valueCtrl->SetFocus();
203 }
204 
205 
206 bool UNIT_BINDER::Validate( double aMin, double aMax, EDA_UNITS aUnits )
207 {
208  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
209 
210  if( !textEntry
211  || textEntry->GetValue() == INDETERMINATE_ACTION
212  || textEntry->GetValue() == INDETERMINATE_STATE )
213  {
214  return true;
215  }
216 
217  // TODO: Validate() does not currently support m_dataType being anything other than DISTANCE
218  // Note: aMin and aMax are not always given in internal units
219  if( GetValue() < From_User_Unit( aUnits, aMin ) )
220  {
221  double val_min_iu = From_User_Unit( aUnits, aMin );
222  m_errorMessage = wxString::Format( _( "%s must be at least %s." ),
224  StringFromValue( m_units, val_min_iu, true ) );
225 
226  textEntry->SelectAll();
227 
228  // Don't focus directly; we might be inside a KillFocus event handler
229  wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
230 
231  return false;
232  }
233 
234  if( GetValue() > From_User_Unit( aUnits, aMax ) )
235  {
236  double val_max_iu = From_User_Unit( aUnits, aMax );
237  m_errorMessage = wxString::Format( _( "%s must be less than %s." ),
239  StringFromValue( m_units, val_max_iu, true ) );
240 
241  textEntry->SelectAll();
242 
243  // Don't focus directly; we might be inside a KillFocus event handler
244  wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
245 
246  return false;
247  }
248 
249  return true;
250 }
251 
252 
253 void UNIT_BINDER::SetValue( int aValue )
254 {
255  double value = aValue;
256  double displayValue = m_originTransforms.ToDisplay( value, m_coordType );
257 
258  if( displayValue == 0 && m_negativeZero )
259  SetValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false, m_dataType ) );
260  else
261  SetValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
262 }
263 
264 
265 void UNIT_BINDER::SetDoubleValue( double aValue )
266 {
267  double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
268  displayValue = setPrecision( displayValue, false );
269 
270  if( displayValue == 0 && m_negativeZero )
271  SetValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false, m_dataType ) );
272  else
273  SetValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
274 }
275 
276 
277 void UNIT_BINDER::SetValue( const wxString& aValue )
278 {
279  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
280  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
281 
282  if( textEntry )
283  textEntry->SetValue( aValue );
284  else if( staticText )
285  staticText->SetLabel( aValue );
286 
287  if( m_allowEval )
288  m_eval.Clear();
289 
290  if( m_unitLabel )
292 }
293 
294 
295 void UNIT_BINDER::ChangeValue( int aValue )
296 {
297  double value = aValue;
298  double displayValue = m_originTransforms.ToDisplay( value, m_coordType );
299 
300  if( displayValue == 0 && m_negativeZero )
301  ChangeValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false ) );
302  else
303  ChangeValue( StringFromValue( m_units, displayValue, false ) );
304 }
305 
306 
307 void UNIT_BINDER::ChangeDoubleValue( double aValue )
308 {
309  double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
310  displayValue = setPrecision( displayValue, false );
311 
312  if( displayValue == 0 && m_negativeZero )
313  ChangeValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false, m_dataType ) );
314  else
315  ChangeValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
316 }
317 
318 
319 void UNIT_BINDER::ChangeValue( const wxString& aValue )
320 {
321  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
322  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
323 
324  if( textEntry )
325  textEntry->ChangeValue( aValue );
326  else if( staticText )
327  staticText->SetLabel( aValue );
328 
329  if( m_allowEval )
330  m_eval.Clear();
331 
332  if( m_unitLabel )
334 }
335 
336 
337 long long int UNIT_BINDER::GetValue()
338 {
339  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
340  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
341  wxString value;
342 
343  if( textEntry )
344  {
345  if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
346  value = m_eval.Result();
347  else
348  value = textEntry->GetValue();
349  }
350  else if( staticText )
351  {
352  value = staticText->GetLabel();
353  }
354  else
355  {
356  return 0;
357  }
358 
359  long long int displayValue = ValueFromString( m_units, value, m_dataType );
360  return m_originTransforms.FromDisplay( displayValue, m_coordType );
361 }
362 
363 
364 double UNIT_BINDER::setPrecision( double aValue, bool aValueUsesUserUnits )
365 {
366  if( m_precision > 1 )
367  {
368  int scale = pow( 10, m_precision );
369  long long tmp = aValueUsesUserUnits ? aValue : To_User_Unit( m_units, aValue ) * scale;
370  aValue = static_cast<double>( tmp ) / scale;
371 
372  if( !aValueUsesUserUnits )
373  aValue = From_User_Unit( m_units, aValue );
374  }
375 
376  return aValue;
377 }
378 
379 
381 {
382  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
383  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
384  wxString value;
385 
386  if( textEntry )
387  {
388  if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
389  value = m_eval.Result();
390  else
391  value = textEntry->GetValue();
392  }
393  else if( staticText )
394  {
395  value = staticText->GetLabel();
396  }
397  else
398  {
399  return 0.0;
400  }
401 
402  double displayValue = DoubleValueFromString( m_units, value, m_dataType );
403  displayValue = setPrecision( displayValue, false );
404 
405  return m_originTransforms.FromDisplay( displayValue, m_coordType );
406 }
407 
408 
410 {
411  wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl );
412 
413  if( te )
414  return te->GetValue() == INDETERMINATE_STATE || te->GetValue() == INDETERMINATE_ACTION;
415 
416  return false;
417 }
418 
419 
421 {
422  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
423  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
424 
425  if( m_allowEval )
426  return m_eval.OriginalText();
427  else if( textEntry )
428  return textEntry->GetValue();
429  else if( staticText )
430  return staticText->GetLabel();
431  else
432  return wxEmptyString;
433 }
434 
435 
436 void UNIT_BINDER::SetLabel( const wxString& aLabel )
437 {
438  m_label->SetLabel( aLabel );
439 }
440 
441 
442 void UNIT_BINDER::Enable( bool aEnable )
443 {
444  m_label->Enable( aEnable );
445  m_valueCtrl->Enable( aEnable );
446 
447  if( m_unitLabel )
448  m_unitLabel->Enable( aEnable );
449 }
450 
451 
452 void UNIT_BINDER::Show( bool aShow, bool aResize )
453 {
454  m_label->Show( aShow );
455  m_valueCtrl->Show( aShow );
456 
457  if( m_unitLabel )
458  m_unitLabel->Show( aShow );
459 
460  if( aResize )
461  {
462  if( aShow )
463  {
464  m_label->SetSize( -1, -1 );
465  m_valueCtrl->SetSize( -1, -1 );
466 
467  if( m_unitLabel )
468  m_unitLabel->SetSize( -1, -1 );
469  }
470  else
471  {
472  m_label->SetSize( 0, 0 );
473  m_valueCtrl->SetSize( 0, 0 );
474 
475  if( m_unitLabel )
476  m_unitLabel->SetSize( 0, 0 );
477  }
478  }
479 }
480 
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:279
wxString m_errorMessage
Definition: unit_binder.h:224
wxString GetOriginalText() const
Return the pre-evaluated text (or the current text if evaluation is not supported).
ORIGIN_TRANSFORMS::COORD_TYPES_T m_coordType
Type of coordinate for display origin transforms.
Definition: unit_binder.h:237
EDA_DRAW_FRAME * m_frame
The bound widgets.
Definition: unit_binder.h:211
~UNIT_BINDER() override
Definition: unit_binder.cpp:85
UNIT_BINDER(EDA_DRAW_FRAME *aParent, wxStaticText *aLabel, wxWindow *aValueCtrl, wxStaticText *aUnitLabel, bool aAllowEval=true)
Definition: unit_binder.cpp:40
void delayedFocusHandler(wxCommandEvent &aEvent)
int GetUserUnits()
Return the currently selected user unit value for the interface.
Implementation of conversion functions that require both schematic and board internal units.
This file is part of the common library.
wxString OriginalText() const
EDA_DATA_TYPE
The type of unit.
Definition: eda_units.h:31
ORIGIN_TRANSFORMS & m_originTransforms
A reference to an ORIGIN_TRANSFORMS object.
Definition: unit_binder.h:234
virtual void ChangeDoubleValue(double aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion WITHOUT trigger...
void SetDataType(EDA_DATA_TYPE aDataType)
Used to override the datatype of the displayed property (default is DISTANCE)
bool m_needsEval
Definition: unit_binder.h:228
wxString GetAbbreviatedUnitsLabel(EDA_UNITS aUnit, EDA_DATA_TYPE aType)
Get the units string for a given units type.
Definition: base_units.cpp:424
bool IsIndeterminate() const
Return true if the control holds the indeterminate value (for instance, if it represents a multiple s...
wxDEFINE_EVENT(DELAY_FOCUS, wxCommandEvent)
The base class for create windows for drawing purpose.
wxString valueDescriptionFromLabel(wxStaticText *aLabel)
virtual void SetPrecision(int aLength)
Normally not needed, but can be used to set the precision when using internal units that are floats (...
EDA_UNITS m_units
Definition: unit_binder.h:219
void Show(bool aShow, bool aResize=false)
Show/hide the label, widget and units label.
Display value expressed in distance units (mm/inch)
Definition: property.h:53
bool Process(const wxString &aString)
long long int ValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: base_units.cpp:416
wxStaticText * m_label
Definition: unit_binder.h:214
EDA_DATA_TYPE m_dataType
Definition: unit_binder.h:221
wxStaticText * m_unitLabel
Can be nullptr.
Definition: unit_binder.h:216
NUMERIC_EVALUATOR m_eval
Definition: unit_binder.h:226
#define _(s)
wxWindow * m_valueCtrl
Definition: unit_binder.h:215
bool m_negativeZero
Indicates "-0" should be displayed for 0.
Definition: unit_binder.h:220
virtual int ToDisplay(int aValue, COORD_TYPES_T aCoordType) const
int m_precision
0 to 6
Definition: unit_binder.h:222
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
bool m_allowEval
Definition: unit_binder.h:227
EDA_UNITS
Definition: eda_units.h:38
virtual void SetUnits(EDA_UNITS aUnits)
Normally not needed (as the UNIT_BINDER inherits from the parent frame), but can be used to set to DE...
Definition: unit_binder.cpp:92
void onKillFocus(wxFocusEvent &aEvent)
wxString Result() const
double To_User_Unit(EDA_UNITS aUnit, double aValue)
Function To_User_Unit convert aValue in internal units to the appropriate user units defined by aUnit...
Definition: base_units.cpp:68
const int scale
virtual bool Validate(double aMin, double aMax, EDA_UNITS aUnits=EDA_UNITS::UNSCALED)
Validate the control against the given range, informing the user of any errors found.
void onSetFocus(wxFocusEvent &aEvent)
void SetLabel(const wxString &aLabel)
virtual int FromDisplay(int aValue, COORD_TYPES_T aCoordType) const
A class to perform either relative or absolute display origin transforms for a single axis of a point...
void onUnitsChanged(wxCommandEvent &aEvent)
virtual void SetValue(int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
virtual long long int GetValue()
Return the current value in Internal Units.
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:204
long m_selStart
Selection start and end of the original text.
Definition: unit_binder.h:230
virtual void SetDoubleValue(double aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
virtual double GetDoubleValue()
Return the current value in Internal Units.
double setPrecision(double aValue, bool aValueUsesUserUnits)
When m_precision > 0 truncate the value aValue to show only m_precision digits in mantissa.
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:307
#define INDETERMINATE_ACTION
Definition: base_units.h:48
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition: base_units.h:47
double From_User_Unit(EDA_UNITS aUnits, double aValue)
Return in internal units the value "val" given in a real unit such as "in", "mm" or "deg".
Definition: base_units.cpp:282
void Enable(bool aEnable)
Enable/disable the label, widget and units label.
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
virtual void ChangeValue(int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion WITHOUT trigger...