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