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 <maciej.suminski@cern.ch>
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_eval( aParent->GetUserUnits() ),
47  m_originTransforms( aParent->GetOriginTransforms() ),
48  m_coordType( ORIGIN_TRANSFORMS::NOT_A_COORD )
49 {
50  m_units = aParent->GetUserUnits();
52  m_precision = 0;
53  m_allowEval = allowEval && dynamic_cast<wxTextEntry*>( m_valueCtrl );
54  m_needsEval = false;
55  m_selStart = 0;
56  m_selEnd = 0;
57 
58  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
59 
60  if( textEntry )
61  {
62  // Use ChangeValue() instead of SetValue() so we don't generate events.
63  textEntry->ChangeValue( wxT( "0" ) );
64  }
65 
66  if( m_unitLabel )
68 
69  m_valueCtrl->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ),
70  nullptr, this );
71  m_valueCtrl->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ),
72  nullptr, this );
73  Connect( DELAY_FOCUS, wxCommandEventHandler( UNIT_BINDER::delayedFocusHandler ), nullptr,
74  this );
75 
76  m_frame->Connect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
77  nullptr, this );
78 }
79 
80 
82 {
83  m_frame->Disconnect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
84  nullptr, this );
85 }
86 
87 
89 {
90  m_units = aUnits;
91 
92  if( m_unitLabel )
94 }
95 
96 
97 void UNIT_BINDER::SetPrecision( int aLength )
98 {
99  m_precision = std::min( aLength, 6 );
100 }
101 
102 
104 {
105  m_dataType = aDataType;
106 
107  if( m_unitLabel )
109 }
110 
111 
112 void UNIT_BINDER::onUnitsChanged( wxCommandEvent& aEvent )
113 {
114  int temp = (int) GetValue();
115 
117 
118  SetValue( temp );
119 
120  aEvent.Skip();
121 }
122 
123 
124 void UNIT_BINDER::onSetFocus( wxFocusEvent& aEvent )
125 {
126  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
127 
128  if( m_allowEval && textEntry )
129  {
130  wxString oldStr = m_eval.OriginalText();
131 
132  if( oldStr.length() && oldStr != textEntry->GetValue() )
133  {
134  textEntry->SetValue( oldStr );
135  textEntry->SetSelection( m_selStart, m_selEnd );
136  }
137 
138  m_needsEval = true;
139  }
140 
141  aEvent.Skip();
142 }
143 
144 
145 void UNIT_BINDER::onKillFocus( wxFocusEvent& aEvent )
146 {
147  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
148 
149  if( m_allowEval && textEntry )
150  {
151  if( m_eval.Process( textEntry->GetValue() ) )
152  {
153  textEntry->GetSelection( &m_selStart, &m_selEnd );
154  wxString sel = textEntry->GetStringSelection();
155 
156  textEntry->ChangeValue( m_eval.Result() );
157 
158 #ifdef __WXGTK__
159  // Manually copy the selected text to the primary selection clipboard
160  if( wxTheClipboard->Open() )
161  {
162  bool clipTarget = wxTheClipboard->IsUsingPrimarySelection();
163  wxTheClipboard->UsePrimarySelection( true );
164  wxTheClipboard->SetData( new wxTextDataObject( sel ) );
165  wxTheClipboard->UsePrimarySelection( clipTarget );
166  wxTheClipboard->Close();
167  }
168 #endif
169  }
170 
171  m_needsEval = false;
172  }
173 
174  aEvent.Skip();
175 }
176 
177 
178 wxString valueDescriptionFromLabel( wxStaticText* aLabel )
179 {
180  wxString desc = aLabel->GetLabel();
181 
182  desc.EndsWith( wxT( ":" ), &desc );
183  return desc;
184 }
185 
186 
187 void UNIT_BINDER::delayedFocusHandler( wxCommandEvent& )
188 {
189  if( !m_errorMessage.IsEmpty() )
190  DisplayError( m_valueCtrl->GetParent(), m_errorMessage );
191 
192  m_errorMessage = wxEmptyString;
193  m_valueCtrl->SetFocus();
194 }
195 
196 
197 bool UNIT_BINDER::Validate( double aMin, double aMax, EDA_UNITS aUnits )
198 {
199  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
200 
201  if( !textEntry
202  || textEntry->GetValue() == INDETERMINATE_ACTION
203  || textEntry->GetValue() == INDETERMINATE_STATE )
204  {
205  return true;
206  }
207 
208  // TODO: Validate() does not currently support m_dataType being anything other than DISTANCE
209  // Note: aMin and aMax are not always given in internal units
210  if( GetValue() < From_User_Unit( aUnits, aMin ) )
211  {
212  double val_min_iu = From_User_Unit( aUnits, aMin );
213  m_errorMessage = wxString::Format( _( "%s must be at least %s." ),
215  StringFromValue( m_units, val_min_iu, true ) );
216 
217  textEntry->SelectAll();
218 
219  // Don't focus directly; we might be inside a KillFocus event handler
220  wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
221 
222  return false;
223  }
224 
225  if( GetValue() > From_User_Unit( aUnits, aMax ) )
226  {
227  double val_max_iu = From_User_Unit( aUnits, aMax );
228  m_errorMessage = wxString::Format( _( "%s must be less than %s." ),
230  StringFromValue( m_units, val_max_iu, true ) );
231 
232  textEntry->SelectAll();
233 
234  // Don't focus directly; we might be inside a KillFocus event handler
235  wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
236 
237  return false;
238  }
239 
240  return true;
241 }
242 
243 
244 void UNIT_BINDER::SetValue( int aValue )
245 {
246  double value = aValue;
247  double displayValue = m_originTransforms.ToDisplay( value, m_coordType );
248  SetValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
249 }
250 
251 
252 void UNIT_BINDER::SetDoubleValue( double aValue )
253 {
254  double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
255  displayValue = setPrecision( displayValue, false );
256  SetValue( StringFromValue( m_units, displayValue, false, m_dataType ) );
257 }
258 
259 
260 void UNIT_BINDER::SetValue( wxString aValue )
261 {
262  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
263  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
264 
265  if( textEntry )
266  textEntry->SetValue( aValue );
267  else if( staticText )
268  staticText->SetLabel( aValue );
269 
270  if( m_allowEval )
271  m_eval.Clear();
272 
273  if( m_unitLabel )
275 }
276 
277 
278 void UNIT_BINDER::ChangeValue( int aValue )
279 {
280  double value = aValue;
281  double displayValue = m_originTransforms.ToDisplay( value, m_coordType );
282  ChangeValue( StringFromValue( m_units, displayValue, false ) );
283 }
284 
285 
286 void UNIT_BINDER::ChangeValue( const wxString& aValue )
287 {
288  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
289  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
290 
291  if( textEntry )
292  textEntry->ChangeValue( aValue );
293  else if( staticText )
294  staticText->SetLabel( aValue );
295 
296  if( m_allowEval )
297  m_eval.Clear();
298 
299  if( m_unitLabel )
301 }
302 
303 
304 long long int UNIT_BINDER::GetValue()
305 {
306  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
307  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
308  wxString value;
309 
310  if( textEntry )
311  {
312  if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
313  value = m_eval.Result();
314  else
315  value = textEntry->GetValue();
316  }
317  else if( staticText )
318  {
319  value = staticText->GetLabel();
320  }
321  else
322  {
323  return 0;
324  }
325 
326  long long int displayValue = ValueFromString( m_units, value, m_dataType );
327  return m_originTransforms.FromDisplay( displayValue, m_coordType );
328 }
329 
330 
331 double UNIT_BINDER::setPrecision( double aValue, bool aValueUsesUserUnits )
332 {
333  if( m_precision > 1 )
334  {
335  int scale = pow( 10, m_precision );
336  long long tmp = aValueUsesUserUnits ? aValue : To_User_Unit( m_units, aValue ) * scale;
337  aValue = static_cast<double>( tmp ) / scale;
338 
339  if( !aValueUsesUserUnits )
340  aValue = From_User_Unit( m_units, aValue );
341  }
342 
343  return aValue;
344 }
345 
346 
348 {
349  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
350  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
351  wxString value;
352 
353  if( textEntry )
354  {
355  if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
356  value = m_eval.Result();
357  else
358  value = textEntry->GetValue();
359  }
360  else if( staticText )
361  {
362  value = staticText->GetLabel();
363  }
364  else
365  {
366  return 0.0;
367  }
368 
369  double displayValue = DoubleValueFromString( m_units, value, m_dataType );
370  displayValue = setPrecision( displayValue, false );
371 
372  return m_originTransforms.FromDisplay( displayValue, m_coordType );
373 }
374 
375 
377 {
378  wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl );
379 
380  if( te )
381  return te->GetValue() == INDETERMINATE_STATE || te->GetValue() == INDETERMINATE_ACTION;
382 
383  return false;
384 }
385 
386 
388 {
389  wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
390  wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
391 
392  if( m_allowEval )
393  return m_eval.OriginalText();
394  else if( textEntry )
395  return textEntry->GetValue();
396  else if( staticText )
397  return staticText->GetLabel();
398  else
399  return wxEmptyString;
400 }
401 
402 
403 void UNIT_BINDER::SetLabel( const wxString& aLabel )
404 {
405  m_label->SetLabel( aLabel );
406 }
407 
408 
409 void UNIT_BINDER::Enable( bool aEnable )
410 {
411  m_label->Enable( aEnable );
412  m_valueCtrl->Enable( aEnable );
413 
414  if( m_unitLabel )
415  m_unitLabel->Enable( aEnable );
416 }
417 
418 
419 void UNIT_BINDER::Show( bool aShow, bool aResize )
420 {
421  m_label->Show( aShow );
422  m_valueCtrl->Show( aShow );
423 
424  if( m_unitLabel )
425  m_unitLabel->Show( aShow );
426 
427  if( aResize )
428  {
429  if( aShow )
430  {
431  m_label->SetSize( -1, -1 );
432  m_valueCtrl->SetSize( -1, -1 );
433 
434  if( m_unitLabel )
435  m_unitLabel->SetSize( -1, -1 );
436  }
437  else
438  {
439  m_label->SetSize( 0, 0 );
440  m_valueCtrl->SetSize( 0, 0 );
441 
442  if( m_unitLabel )
443  m_unitLabel->SetSize( 0, 0 );
444  }
445  }
446 }
447 
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:252
wxString m_errorMessage
< Validation support.
Definition: unit_binder.h:212
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:227
EDA_DRAW_FRAME * m_frame
The bound widgets.
Definition: unit_binder.h:199
~UNIT_BINDER() override
Definition: unit_binder.cpp:81
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:224
void SetDataType(EDA_DATA_TYPE aDataType)
Used to override the datatype of the displayed property (default is DISTANCE)
bool m_needsEval
Selection start and end of the original text.
Definition: unit_binder.h:217
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 (...
Definition: unit_binder.cpp:97
EDA_UNITS m_units
< Currently used units.
Definition: unit_binder.h:207
void Show(bool aShow, bool aResize=false)
Show/hide the label, widget and units label.
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:202
EDA_DATA_TYPE m_dataType
Definition: unit_binder.h:208
wxStaticText * m_unitLabel
Definition: unit_binder.h:204
NUMERIC_EVALUATOR m_eval
Definition: unit_binder.h:215
#define _(s)
wxWindow * m_valueCtrl
Definition: unit_binder.h:203
virtual int ToDisplay(int aValue, COORD_TYPES_T aCoordType) const
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:216
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:88
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
Definition: unit_binder.h:220
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)
Change the value (in Internal Units) for the text field, taking care of units conversion but does not...