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_units.h>
32#include <eda_draw_frame.h>
33#include <confirm.h>
34
35#include "widgets/unit_binder.h"
36
37
38wxDEFINE_EVENT( DELAY_FOCUS, wxCommandEvent );
39
40
41UNIT_BINDER::UNIT_BINDER( EDA_DRAW_FRAME* aParent, wxStaticText* aLabel, wxWindow* aValueCtrl,
42 wxStaticText* aUnitLabel, bool allowEval ) :
43 UNIT_BINDER( aParent, aParent->GetIuScale(), aLabel, aValueCtrl, aUnitLabel, allowEval )
44{
45}
46
48 wxStaticText* aLabel, wxWindow* aValueCtrl,
49 wxStaticText* aUnitLabel, bool allowEval ) :
50 m_frame( aParent ),
51 m_label( aLabel ),
52 m_valueCtrl( aValueCtrl ),
53 m_unitLabel( aUnitLabel ),
54 m_iuScale( aIUScale ),
55 m_negativeZero( false ),
56 m_dataType( EDA_DATA_TYPE::DISTANCE ),
57 m_precision( 0 ),
58 m_eval( aParent->GetUserUnits() ),
59 m_originTransforms( aParent->GetOriginTransforms() ),
60 m_coordType( ORIGIN_TRANSFORMS::NOT_A_COORD )
61{
62 m_units = aParent->GetUserUnits();
63 m_allowEval = allowEval && dynamic_cast<wxTextEntry*>( m_valueCtrl );
64 m_needsEval = false;
65 m_selStart = 0;
66 m_selEnd = 0;
67
68 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
69
70 if( textEntry )
71 {
72 // Use ChangeValue() instead of SetValue() so we don't generate events.
73 if( m_negativeZero )
74 textEntry->ChangeValue( wxT( "-0" ) );
75 else
76 textEntry->ChangeValue( wxT( "0" ) );
77 }
78
79 if( m_unitLabel )
81
82 m_valueCtrl->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ),
83 nullptr, this );
84 m_valueCtrl->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ),
85 nullptr, this );
86 m_valueCtrl->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( UNIT_BINDER::onClick ), nullptr,
87 this );
88 Connect( DELAY_FOCUS, wxCommandEventHandler( UNIT_BINDER::delayedFocusHandler ), nullptr,
89 this );
90
91 m_frame->Connect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
92 nullptr, this );
93}
94
95
97{
98 m_frame->Disconnect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
99 nullptr, this );
100}
101
102
104{
105 m_units = aUnits;
106
107 if( m_unitLabel )
109}
110
111
112void UNIT_BINDER::SetPrecision( int aLength )
113{
114 m_precision = std::min( aLength, 6 );
115}
116
117
119{
120 m_dataType = aDataType;
121
122 if( m_unitLabel )
124}
125
126
127void UNIT_BINDER::onUnitsChanged( wxCommandEvent& aEvent )
128{
132 {
133 int temp = (int) GetValue();
134
136
137 SetValue( temp );
138 }
139
140 aEvent.Skip();
141}
142
143
144void UNIT_BINDER::onClick( wxMouseEvent& aEvent )
145{
146 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
147
148 if( textEntry && ( textEntry->GetValue() == INDETERMINATE_ACTION
149 || textEntry->GetValue() == INDETERMINATE_STATE ) )
150 {
151 // These are tokens, not strings, so do a select all
152 textEntry->SelectAll();
153 }
154
155 // Needed at least on Windows to avoid hanging
156 aEvent.Skip();
157}
158
159
160void UNIT_BINDER::onSetFocus( wxFocusEvent& aEvent )
161{
162 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
163
164 if( textEntry )
165 {
166 if( m_allowEval )
167 {
168 wxString oldStr = m_eval.OriginalText();
169
170 if( oldStr.length() && oldStr != textEntry->GetValue() )
171 {
172 textEntry->SetValue( oldStr );
173 textEntry->SetSelection( m_selStart, m_selEnd );
174 }
175
176 m_needsEval = true;
177 }
178
179 if( textEntry->GetValue() == INDETERMINATE_ACTION
180 || textEntry->GetValue() == INDETERMINATE_STATE )
181 {
182 // These are tokens, not strings, so do a select all
183 textEntry->SelectAll();
184 }
185 }
186
187 aEvent.Skip();
188}
189
190
191void UNIT_BINDER::onKillFocus( wxFocusEvent& aEvent )
192{
193 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
194
195 if( m_allowEval && textEntry )
196 {
197 if( m_eval.Process( textEntry->GetValue() ) )
198 {
199 textEntry->GetSelection( &m_selStart, &m_selEnd );
200 textEntry->ChangeValue( m_eval.Result() );
201
202#ifdef __WXGTK__
203 // Manually copy the selected text to the primary selection clipboard
204 if( wxTheClipboard->Open() )
205 {
206 wxString sel = textEntry->GetStringSelection();
207 bool clipTarget = wxTheClipboard->IsUsingPrimarySelection();
208 wxTheClipboard->UsePrimarySelection( true );
209 wxTheClipboard->SetData( new wxTextDataObject( sel ) );
210 wxTheClipboard->UsePrimarySelection( clipTarget );
211 wxTheClipboard->Close();
212 }
213#endif
214 }
215
216 m_needsEval = false;
217 }
218
219 aEvent.Skip();
220}
221
222
223wxString valueDescriptionFromLabel( wxStaticText* aLabel )
224{
225 wxString desc = aLabel->GetLabel();
226
227 desc.EndsWith( wxT( ":" ), &desc );
228 return desc;
229}
230
231
232void UNIT_BINDER::delayedFocusHandler( wxCommandEvent& )
233{
234 if( !m_errorMessage.IsEmpty() )
235 DisplayError( m_valueCtrl->GetParent(), m_errorMessage );
236
237 m_errorMessage = wxEmptyString;
238 m_valueCtrl->SetFocus();
239}
240
241
242bool UNIT_BINDER::Validate( double aMin, double aMax, EDA_UNITS aUnits )
243{
244 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
245
246 if( !textEntry
247 || textEntry->GetValue() == INDETERMINATE_ACTION
248 || textEntry->GetValue() == INDETERMINATE_STATE )
249 {
250 return true;
251 }
252
253 // TODO: Validate() does not currently support m_dataType being anything other than DISTANCE
254 // Note: aMin and aMax are not always given in internal units
255 if( GetValue() < EDA_UNIT_UTILS::UI::FromUserUnit( m_iuScale, aUnits, aMin ) )
256 {
257 double val_min_iu = EDA_UNIT_UTILS::UI::FromUserUnit( m_iuScale, aUnits, aMin );
258 m_errorMessage = wxString::Format( _( "%s must be at least %s." ),
261 val_min_iu, true ) );
262
263 textEntry->SelectAll();
264
265 // Don't focus directly; we might be inside a KillFocus event handler
266 wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
267
268 return false;
269 }
270
271 if( GetValue() > EDA_UNIT_UTILS::UI::FromUserUnit( m_iuScale, aUnits, aMax ) )
272 {
273 double val_max_iu = EDA_UNIT_UTILS::UI::FromUserUnit( m_iuScale, aUnits, aMax );
274 m_errorMessage = wxString::Format( _( "%s must be less than %s." ),
277 val_max_iu, true ) );
278
279 textEntry->SelectAll();
280
281 // Don't focus directly; we might be inside a KillFocus event handler
282 wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
283
284 return false;
285 }
286
287 return true;
288}
289
290
291void UNIT_BINDER::SetValue( long long int aValue )
292{
293 double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
294 wxString textValue = EDA_UNIT_UTILS::UI::StringFromValue( m_iuScale, m_units, displayValue,
295 false, m_dataType );
296
297 if( displayValue == 0 && m_negativeZero )
298 SetValue( wxT( "-" ) + textValue );
299 else
300 SetValue( textValue );
301}
302
303
304void UNIT_BINDER::SetDoubleValue( double aValue )
305{
306 double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
308 setPrecision( displayValue, false ),
309 false, m_dataType );
310
311 if( displayValue == 0 && m_negativeZero )
312 SetValue( wxT( "-" ) + textValue );
313 else
314 SetValue( textValue );
315}
316
317
319{
320 SetDoubleValue( aValue.AsDegrees() );
321}
322
323
324void UNIT_BINDER::SetValue( const wxString& aValue )
325{
326 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
327 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
328
329 if( textEntry )
330 textEntry->SetValue( aValue );
331 else if( staticText )
332 staticText->SetLabel( aValue );
333
334 if( m_allowEval )
335 m_eval.Clear();
336
337 if( m_unitLabel )
339}
340
341
342void UNIT_BINDER::ChangeValue( int aValue )
343{
344 double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
346 setPrecision( displayValue, false ),
347 false, m_dataType );
348
349 if( displayValue == 0 && m_negativeZero )
350 ChangeValue( wxT( "-" ) + textValue );
351 else
352 ChangeValue( textValue );
353}
354
355
357{
358 double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
360 setPrecision( displayValue, false ),
361 false, m_dataType );
362
363 if( displayValue == 0 && m_negativeZero )
364 ChangeValue( wxT( "-" ) + textValue );
365 else
366 ChangeValue( textValue );
367}
368
369
371{
372 ChangeDoubleValue( aValue.AsDegrees() );
373}
374
375
376void UNIT_BINDER::ChangeValue( const wxString& aValue )
377{
378 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
379 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
380
381 if( textEntry )
382 textEntry->ChangeValue( aValue );
383 else if( staticText )
384 staticText->SetLabel( aValue );
385
386 if( m_allowEval )
387 m_eval.Clear();
388
389 if( m_unitLabel )
391}
392
393
395{
396 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
397 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
398 wxString value;
399
400 if( textEntry )
401 {
402 if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
403 value = m_eval.Result();
404 else
405 value = textEntry->GetValue();
406 }
407 else if( staticText )
408 {
409 value = staticText->GetLabel();
410 }
411 else
412 {
413 return 0;
414 }
415
416 long long int displayValue = EDA_UNIT_UTILS::UI::ValueFromString( m_iuScale, m_units, value,
417 m_dataType );
418 return m_originTransforms.FromDisplay( displayValue, m_coordType );
419}
420
421
422double UNIT_BINDER::setPrecision( double aValue, bool aValueUsesUserUnits )
423{
424 if( m_precision > 1 )
425 {
426 int scale = pow( 10, m_precision );
427 int64_t tmp = aValue;
428 if( !aValueUsesUserUnits )
429 {
431 }
432
433 aValue = static_cast<double>( tmp ) / scale;
434
435 if( !aValueUsesUserUnits )
437 }
438
439 return aValue;
440}
441
442
444{
445 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
446 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
447 wxString value;
448
449 if( textEntry )
450 {
451 if( m_needsEval && m_eval.Process( textEntry->GetValue() ) )
452 value = m_eval.Result();
453 else
454 value = textEntry->GetValue();
455 }
456 else if( staticText )
457 {
458 value = staticText->GetLabel();
459 }
460 else
461 {
462 return 0.0;
463 }
464
466 value, m_dataType );
467 displayValue = setPrecision( displayValue, false );
468
469 return m_originTransforms.FromDisplay( displayValue, m_coordType );
470}
471
472
474{
476}
477
478
480{
481 wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl );
482
483 if( te )
484 return te->GetValue() == INDETERMINATE_STATE || te->GetValue() == INDETERMINATE_ACTION;
485
486 return false;
487}
488
489
491{
492 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
493 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
494
495 if( m_allowEval )
496 return m_eval.OriginalText();
497 else if( textEntry )
498 return textEntry->GetValue();
499 else if( staticText )
500 return staticText->GetLabel();
501 else
502 return wxEmptyString;
503}
504
505
506void UNIT_BINDER::SetLabel( const wxString& aLabel )
507{
508 m_label->SetLabel( aLabel );
509}
510
511
512void UNIT_BINDER::Enable( bool aEnable )
513{
514 m_label->Enable( aEnable );
515 m_valueCtrl->Enable( aEnable );
516
517 if( m_unitLabel )
518 m_unitLabel->Enable( aEnable );
519}
520
521
522void UNIT_BINDER::Show( bool aShow, bool aResize )
523{
524 m_label->Show( aShow );
525 m_valueCtrl->Show( aShow );
526
527 if( m_unitLabel )
528 m_unitLabel->Show( aShow );
529
530 if( aResize )
531 {
532 if( aShow )
533 {
534 m_label->SetSize( -1, -1 );
535 m_valueCtrl->SetSize( -1, -1 );
536
537 if( m_unitLabel )
538 m_unitLabel->SetSize( -1, -1 );
539 }
540 else
541 {
542 m_label->SetSize( 0, 0 );
543 m_valueCtrl->SetSize( 0, 0 );
544
545 if( m_unitLabel )
546 m_unitLabel->SetSize( 0, 0 );
547 }
548 }
549}
550
double AsDegrees() const
Definition: eda_angle.h:149
The base frame for deriving all KiCad main window classes.
The base class for create windows for drawing purpose.
wxString OriginalText() const
wxString Result() const
bool Process(const wxString &aString)
A class to perform either relative or absolute display origin transforms for a single axis of a point...
virtual int FromDisplay(int aValue, COORD_TYPES_T aCoordType) const
virtual int ToDisplay(int aValue, COORD_TYPES_T aCoordType) const
EDA_UNITS GetUserUnits() const
ORIGIN_TRANSFORMS::COORD_TYPES_T m_coordType
Type of coordinate for display origin transforms.
Definition: unit_binder.h:248
EDA_BASE_FRAME * m_frame
The bound widgets.
Definition: unit_binder.h:221
virtual void ChangeDoubleValue(double aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion WITHOUT trigger...
wxString GetOriginalText() const
Return the pre-evaluated text (or the current text if evaluation is not supported).
void onKillFocus(wxFocusEvent &aEvent)
virtual long long int GetValue()
Return the current value in Internal Units.
void Enable(bool aEnable)
Enable/disable the label, widget and units label.
void onClick(wxMouseEvent &aEvent)
virtual void SetPrecision(int aLength)
Normally not needed, but can be used to set the precision when using internal units that are floats (...
UNIT_BINDER(EDA_DRAW_FRAME *aParent, wxStaticText *aLabel, wxWindow *aValueCtrl, wxStaticText *aUnitLabel, bool aAllowEval=true)
Definition: unit_binder.cpp:41
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...
bool m_allowEval
Definition: unit_binder.h:238
wxString m_errorMessage
Definition: unit_binder.h:235
virtual EDA_ANGLE GetAngleValue()
bool m_negativeZero
Indicates "-0" should be displayed for 0.
Definition: unit_binder.h:231
int m_precision
0 to 6
Definition: unit_binder.h:233
wxStaticText * m_unitLabel
Can be nullptr.
Definition: unit_binder.h:226
wxWindow * m_valueCtrl
Definition: unit_binder.h:225
void onSetFocus(wxFocusEvent &aEvent)
wxStaticText * m_label
Definition: unit_binder.h:224
ORIGIN_TRANSFORMS & m_originTransforms
A reference to an ORIGIN_TRANSFORMS object.
Definition: unit_binder.h:245
long m_selStart
Selection start and end of the original text.
Definition: unit_binder.h:241
EDA_UNITS m_units
Definition: unit_binder.h:230
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.
bool IsIndeterminate() const
Return true if the control holds the indeterminate value (for instance, if it represents a multiple s...
bool m_needsEval
Definition: unit_binder.h:239
void SetDataType(EDA_DATA_TYPE aDataType)
Used to override the datatype of the displayed property (default is DISTANCE)
void delayedFocusHandler(wxCommandEvent &aEvent)
EDA_DATA_TYPE m_dataType
Definition: unit_binder.h:232
virtual void SetAngleValue(const EDA_ANGLE &aValue)
void SetLabel(const wxString &aLabel)
virtual void ChangeAngleValue(const EDA_ANGLE &aValue)
virtual void SetDoubleValue(double aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
~UNIT_BINDER() override
Definition: unit_binder.cpp:96
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.
virtual void ChangeValue(int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion WITHOUT trigger...
virtual void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
void Show(bool aShow, bool aResize=false)
Show/hide the label, widget and units label.
NUMERIC_EVALUATOR m_eval
Definition: unit_binder.h:237
const EDA_IU_SCALE & m_iuScale
Definition: unit_binder.h:229
void onUnitsChanged(wxCommandEvent &aEvent)
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
This file is part of the common library.
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
EDA_DATA_TYPE
The type of unit.
Definition: eda_units.h:36
EDA_UNITS
Definition: eda_units.h:43
double FromUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Return in internal units the value "val" given in a real unit such as "in", "mm" or "deg".
Definition: eda_units.cpp:393
long long int ValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: eda_units.cpp:530
wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Returns the string from aValue according to aUnits (inch, mm ...) for display.
Definition: eda_units.cpp:233
double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:451
double ToUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Function To_User_Unit convert aValue in internal units to the appropriate user units defined by aUnit...
Definition: eda_units.cpp:198
wxString GetLabel(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
Definition: eda_units.cpp:129
int GetUserUnits()
Return the currently selected user unit value for the interface.
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
const int scale
#define INDETERMINATE_ACTION
Definition: ui_common.h:43
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition: ui_common.h:42
wxDEFINE_EVENT(DELAY_FOCUS, wxCommandEvent)
wxString valueDescriptionFromLabel(wxStaticText *aLabel)