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 {
118  int temp = (int) GetValue();
119 
121 
122  SetValue( temp );
123 
124  aEvent.Skip();
125 }
126 
127 
128 void UNIT_BINDER::onSetFocus( wxFocusEvent& aEvent )
129 {
130  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
131 
132  if( m_allowEval && textEntry )
133  {
134  wxString oldStr = m_eval.OriginalText();
135 
136  if( oldStr.length() && oldStr != textEntry->GetValue() )
137  {
138  textEntry->SetValue( oldStr );
139  textEntry->SetSelection( m_selStart, m_selEnd );
140  }
141 
142  m_needsEval = true;
143  }
144 
145  aEvent.Skip();
146 }
147 
148 
149 void UNIT_BINDER::onKillFocus( wxFocusEvent& aEvent )
150 {
151  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
152 
153  if( m_allowEval && textEntry )
154  {
155  if( m_eval.Process( textEntry->GetValue() ) )
156  {
157  textEntry->GetSelection( &m_selStart, &m_selEnd );
158  wxString sel = textEntry->GetStringSelection();
159 
160  textEntry->ChangeValue( m_eval.Result() );
161 
162 #ifdef __WXGTK__
163  // Manually copy the selected text to the primary selection clipboard
164  if( wxTheClipboard->Open() )
165  {
166  bool clipTarget = wxTheClipboard->IsUsingPrimarySelection();
167  wxTheClipboard->UsePrimarySelection( true );
168  wxTheClipboard->SetData( new wxTextDataObject( sel ) );
169  wxTheClipboard->UsePrimarySelection( clipTarget );
170  wxTheClipboard->Close();
171  }
172 #endif
173  }
174 
175  m_needsEval = false;
176  }
177 
178  aEvent.Skip();
179 }
180 
181 
182 wxString valueDescriptionFromLabel( wxStaticText* aLabel )
183 {
184  wxString desc = aLabel->GetLabel();
185 
186  desc.EndsWith( wxT( ":" ), &desc );
187  return desc;
188 }
189 
190 
191 void UNIT_BINDER::delayedFocusHandler( wxCommandEvent& )
192 {
193  if( !m_errorMessage.IsEmpty() )
194  DisplayError( m_valueCtrl->GetParent(), m_errorMessage );
195 
196  m_errorMessage = wxEmptyString;
197  m_valueCtrl->SetFocus();
198 }
199 
200 
201 bool UNIT_BINDER::Validate( double aMin, double aMax, EDA_UNITS aUnits )
202 {
203  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
204 
205  if( !textEntry
206  || textEntry->GetValue() == INDETERMINATE_ACTION
207  || textEntry->GetValue() == INDETERMINATE_STATE )
208  {
209  return true;
210  }
211 
212  // TODO: Validate() does not currently support m_dataType being anything other than DISTANCE
213  // Note: aMin and aMax are not always given in internal units
214  if( GetValue() < From_User_Unit( aUnits, aMin ) )
215  {
216  double val_min_iu = From_User_Unit( aUnits, aMin );
217  m_errorMessage = wxString::Format( _( "%s must be at least %s." ),
219  StringFromValue( m_units, val_min_iu, true ) );
220 
221  textEntry->SelectAll();
222 
223  // Don't focus directly; we might be inside a KillFocus event handler
224  wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
225 
226  return false;
227  }
228 
229  if( GetValue() > From_User_Unit( aUnits, aMax ) )
230  {
231  double val_max_iu = From_User_Unit( aUnits, aMax );
232  m_errorMessage = wxString::Format( _( "%s must be less than %s." ),
234  StringFromValue( m_units, val_max_iu, true ) );
235 
236  textEntry->SelectAll();
237 
238  // Don't focus directly; we might be inside a KillFocus event handler
239  wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
240 
241  return false;
242  }
243 
244  return true;
245 }
246 
247 
248 void UNIT_BINDER::SetValue( int aValue )
249 {
250  double value = aValue;
251  double displayValue = m_originTransforms.ToDisplay( value, m_coordType );
252 
253  if( displayValue == 0 && m_negativeZero )
254  SetValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false, m_dataType ) );
255  else
256  SetValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
257 }
258 
259 
260 void UNIT_BINDER::SetDoubleValue( double aValue )
261 {
262  double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
263  displayValue = setPrecision( displayValue, false );
264 
265  if( displayValue == 0 && m_negativeZero )
266  SetValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false, m_dataType ) );
267  else
268  SetValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
269 }
270 
271 
272 void UNIT_BINDER::SetValue( const wxString& aValue )
273 {
274  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
275  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
276 
277  if( textEntry )
278  textEntry->SetValue( aValue );
279  else if( staticText )
280  staticText->SetLabel( aValue );
281 
282  if( m_allowEval )
283  m_eval.Clear();
284 
285  if( m_unitLabel )
287 }
288 
289 
290 void UNIT_BINDER::ChangeValue( int aValue )
291 {
292  double value = aValue;
293  double displayValue = m_originTransforms.ToDisplay( value, m_coordType );
294 
295  if( displayValue == 0 && m_negativeZero )
296  ChangeValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false ) );
297  else
298  ChangeValue( StringFromValue( m_units, displayValue, false ) );
299 }
300 
301 
302 void UNIT_BINDER::ChangeDoubleValue( double aValue )
303 {
304  double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
305  displayValue = setPrecision( displayValue, false );
306 
307  if( displayValue == 0 && m_negativeZero )
308  ChangeValue( wxT( "-" ) + StringFromValue( m_units, displayValue, false, m_dataType ) );
309  else
310  ChangeValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
311 }
312 
313 
314 void UNIT_BINDER::ChangeValue( const wxString& aValue )
315 {
316  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
317  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
318 
319  if( textEntry )
320  textEntry->ChangeValue( aValue );
321  else if( staticText )
322  staticText->SetLabel( aValue );
323 
324  if( m_allowEval )
325  m_eval.Clear();
326 
327  if( m_unitLabel )
329 }
330 
331 
332 long long int UNIT_BINDER::GetValue()
333 {
334  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
335  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
336  wxString value;
337 
338  if( textEntry )
339  {
340  if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
341  value = m_eval.Result();
342  else
343  value = textEntry->GetValue();
344  }
345  else if( staticText )
346  {
347  value = staticText->GetLabel();
348  }
349  else
350  {
351  return 0;
352  }
353 
354  long long int displayValue = ValueFromString( m_units, value, m_dataType );
355  return m_originTransforms.FromDisplay( displayValue, m_coordType );
356 }
357 
358 
359 double UNIT_BINDER::setPrecision( double aValue, bool aValueUsesUserUnits )
360 {
361  if( m_precision > 1 )
362  {
363  int scale = pow( 10, m_precision );
364  long long tmp = aValueUsesUserUnits ? aValue : To_User_Unit( m_units, aValue ) * scale;
365  aValue = static_cast<double>( tmp ) / scale;
366 
367  if( !aValueUsesUserUnits )
368  aValue = From_User_Unit( m_units, aValue );
369  }
370 
371  return aValue;
372 }
373 
374 
376 {
377  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
378  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
379  wxString value;
380 
381  if( textEntry )
382  {
383  if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
384  value = m_eval.Result();
385  else
386  value = textEntry->GetValue();
387  }
388  else if( staticText )
389  {
390  value = staticText->GetLabel();
391  }
392  else
393  {
394  return 0.0;
395  }
396 
397  double displayValue = DoubleValueFromString( m_units, value, m_dataType );
398  displayValue = setPrecision( displayValue, false );
399 
400  return m_originTransforms.FromDisplay( displayValue, m_coordType );
401 }
402 
403 
405 {
406  wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl );
407 
408  if( te )
409  return te->GetValue() == INDETERMINATE_STATE || te->GetValue() == INDETERMINATE_ACTION;
410 
411  return false;
412 }
413 
414 
416 {
417  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
418  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
419 
420  if( m_allowEval )
421  return m_eval.OriginalText();
422  else if( textEntry )
423  return textEntry->GetValue();
424  else if( staticText )
425  return staticText->GetLabel();
426  else
427  return wxEmptyString;
428 }
429 
430 
431 void UNIT_BINDER::SetLabel( const wxString& aLabel )
432 {
433  m_label->SetLabel( aLabel );
434 }
435 
436 
437 void UNIT_BINDER::Enable( bool aEnable )
438 {
439  m_label->Enable( aEnable );
440  m_valueCtrl->Enable( aEnable );
441 
442  if( m_unitLabel )
443  m_unitLabel->Enable( aEnable );
444 }
445 
446 
447 void UNIT_BINDER::Show( bool aShow, bool aResize )
448 {
449  m_label->Show( aShow );
450  m_valueCtrl->Show( aShow );
451 
452  if( m_unitLabel )
453  m_unitLabel->Show( aShow );
454 
455  if( aResize )
456  {
457  if( aShow )
458  {
459  m_label->SetSize( -1, -1 );
460  m_valueCtrl->SetSize( -1, -1 );
461 
462  if( m_unitLabel )
463  m_unitLabel->SetSize( -1, -1 );
464  }
465  else
466  {
467  m_label->SetSize( 0, 0 );
468  m_valueCtrl->SetSize( 0, 0 );
469 
470  if( m_unitLabel )
471  m_unitLabel->SetSize( 0, 0 );
472  }
473  }
474 }
475 
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:216
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:229
EDA_DRAW_FRAME * m_frame
The bound widgets.
Definition: unit_binder.h:203
~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:226
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:220
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:211
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:206
EDA_DATA_TYPE m_dataType
Definition: unit_binder.h:213
wxStaticText * m_unitLabel
Can be nullptr.
Definition: unit_binder.h:208
NUMERIC_EVALUATOR m_eval
Definition: unit_binder.h:218
#define _(s)
wxWindow * m_valueCtrl
Definition: unit_binder.h:207
bool m_negativeZero
Indicates "-0" should be displayed for 0.
Definition: unit_binder.h:212
virtual int ToDisplay(int aValue, COORD_TYPES_T aCoordType) const
int m_precision
0 to 6
Definition: unit_binder.h:214
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:219
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:222
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...