KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <wx/clipbrd.h>
23#include <wx/combobox.h>
24#include <wx/stattext.h>
25#include <wx/textentry.h>
26#include <eda_units.h>
27#include <eda_draw_frame.h>
28#include <confirm.h>
29#include <dialog_shim.h>
30
31#include "widgets/unit_binder.h"
33#include "wx/dcclient.h"
34
35using namespace EDA_UNIT_UTILS::UI;
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, bool aBindFrameEvents ) :
43 UNIT_BINDER( aParent, aParent, aLabel, aValueCtrl, aUnitLabel, allowEval, aBindFrameEvents )
44{
45}
46
47UNIT_BINDER::UNIT_BINDER( UNITS_PROVIDER* aUnitsProvider, wxWindow* aEventSource, wxStaticText* aLabel,
48 wxWindow* aValueCtrl, wxStaticText* aUnitLabel, bool aAllowEval,
49 bool aBindFocusEvent ) :
50 m_bindFocusEvent( aBindFocusEvent ),
51 m_label( aLabel ),
52 m_valueCtrl( aValueCtrl ),
53 m_eventSource( aEventSource ),
54 m_unitLabel( aUnitLabel ),
55 m_iuScale( &aUnitsProvider->GetIuScale() ),
56 m_units( aUnitsProvider->GetUserUnits() ),
57 m_negativeZero( false ),
59 m_precision( 0 ),
60 m_eval( aUnitsProvider->GetUserUnits() ),
61 m_allowEval( aAllowEval && ( !m_valueCtrl || dynamic_cast<wxTextEntry*>( m_valueCtrl ) ) ),
62 m_needsEval( false ),
63 m_selStart( 0 ),
64 m_selEnd( 0 ),
65 m_unitsInValue( false ),
66 m_originTransforms( aUnitsProvider->GetOriginTransforms() ),
67 m_coordType( ORIGIN_TRANSFORMS::NOT_A_COORD ),
68 m_dialogShim( nullptr )
69{
70 // Two evaluators on one control would fight over its text, and only this one knows the units
71 if( TEXT_CTRL_EVAL* evalCtrl = m_allowEval ? dynamic_cast<TEXT_CTRL_EVAL*>( m_valueCtrl ) : nullptr )
72 evalCtrl->DeferToBinder();
73
74 if( m_valueCtrl )
75 {
76 // Register the UNIT_BINDER for control state save/restore
77 wxWindow* parent = m_valueCtrl->GetParent();
78
79 while( parent && !dynamic_cast<DIALOG_SHIM*>( parent ) )
80 parent = parent->GetParent();
81
82 if( parent )
83 {
84 m_dialogShim = static_cast<DIALOG_SHIM*>( parent );
85 m_dialogShim->RegisterUnitBinder( this, m_valueCtrl );
86 }
87 }
88
89 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
90
91 if( textEntry )
92 {
93 wxClientDC dc( m_valueCtrl );
94
95 wxSize minSize = m_valueCtrl->GetMinSize();
96 int minWidth;
97
98 if( m_iuScale->IU_PER_MM <= schIUScale.IU_PER_MM )
99 {
100 // PLEditor and SCH editors don't need as much room
101 minWidth = dc.GetTextExtent( wxT( "XXX.XXXXX" ) ).GetWidth();
102 }
103 else
104 {
105 // Gives enough room to display a value in inches in PCBNew
106 // 3 digits + '.' + 8 digits
107 minWidth = dc.GetTextExtent( wxT( "XXX.XXXXXXXX" ) ).GetWidth();
108 }
109
110 if( minSize.GetWidth() < minWidth )
111 m_valueCtrl->SetMinSize( wxSize( minWidth, minSize.GetHeight() ) );
112
113 // Use ChangeValue() instead of SetValue() so we don't generate events.
114 if( m_negativeZero )
115 textEntry->ChangeValue( wxT( "-0" ) );
116 else
117 textEntry->ChangeValue( wxT( "0" ) );
118 }
119
120 if( m_unitLabel )
121 m_unitLabel->SetLabel( EDA_UNIT_UTILS::GetLabel( m_units, m_dataType ) );
122
123 if( m_valueCtrl )
124 {
125 m_valueCtrl->Connect( wxEVT_DESTROY, wxWindowDestroyEventHandler( UNIT_BINDER::onValueCtrlDestroyed ),
126 nullptr, this );
127 m_valueCtrl->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ), nullptr, this );
128 m_valueCtrl->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ), nullptr, this );
129 m_valueCtrl->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( UNIT_BINDER::onClick ), nullptr, this );
130 m_valueCtrl->Connect( wxEVT_COMBOBOX, wxCommandEventHandler( UNIT_BINDER::onComboBox ), nullptr, this );
131 }
132
133 if( m_bindFocusEvent )
134 Connect( DELAY_FOCUS, wxCommandEventHandler( UNIT_BINDER::delayedFocusHandler ), nullptr, this );
135
136 if( m_eventSource )
137 {
138 m_eventSource->Connect( EDA_EVT_UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
139 nullptr, this );
140 }
141}
142
143
145{
146 if( m_dialogShim )
147 m_dialogShim->UnregisterUnitBinder( this );
148
149 if( m_valueCtrl )
150 {
151 m_valueCtrl->Disconnect( wxEVT_DESTROY, wxWindowDestroyEventHandler( UNIT_BINDER::onValueCtrlDestroyed ),
152 nullptr, this );
153 m_valueCtrl->Disconnect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ), nullptr, this );
154 m_valueCtrl->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ), nullptr, this );
155 m_valueCtrl->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( UNIT_BINDER::onClick ), nullptr, this );
156 m_valueCtrl->Disconnect( wxEVT_COMBOBOX, wxCommandEventHandler( UNIT_BINDER::onComboBox ), nullptr, this );
157 }
158
159 if( m_bindFocusEvent )
160 Disconnect( DELAY_FOCUS, wxCommandEventHandler( UNIT_BINDER::delayedFocusHandler ), nullptr, this );
161
162 if( m_eventSource )
163 {
164 m_eventSource->Disconnect( EDA_EVT_UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
165 nullptr, this );
166 }
167}
168
169
171{
172 m_units = aUnits;
173
174 m_eval.SetDefaultUnits( m_units );
175 m_eval.LocaleChanged(); // In case locale changed since last run
176
177 if( m_unitLabel )
179}
180
181
182void UNIT_BINDER::SetPrecision( int aLength )
183{
184 m_precision = std::min( aLength, 6 );
185}
186
187
189{
190 m_dataType = aDataType;
191
192 if( m_unitLabel )
194}
195
196
197void UNIT_BINDER::onUnitsChanged( wxCommandEvent& aEvent )
198{
199 if( !UnitsInvariant() )
200 {
201 if( EDA_BASE_FRAME* provider = static_cast<EDA_BASE_FRAME*>( aEvent.GetClientData() ) )
202 {
203 int temp = GetIntValue();
204
205 wxComboBox* const combo = dynamic_cast<wxComboBox*>( m_valueCtrl );
206 std::vector<long long int> comboValues;
207
208 // Read out the current values
209 if( combo )
210 {
211 for( unsigned int i = 0; i < combo->GetCount(); i++ )
212 {
213 const wxString value = combo->GetString( i );
214 long long int conv = ValueFromString( *m_iuScale, m_units, value, m_dataType );
215 comboValues.push_back( conv );
216 }
217 }
218
219 SetUnits( provider->GetUserUnits() );
220 m_iuScale = &provider->GetIuScale();
221
222 // Re-populate the combo box with updated values
223 if( combo )
224 SetOptionsList( comboValues );
225
226 if( !IsIndeterminate() )
227 SetValue( temp );
228 }
229 }
230
231 aEvent.Skip();
232}
233
234
235void UNIT_BINDER::onClick( wxMouseEvent& aEvent )
236{
237 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
238
239 if( textEntry && ( textEntry->GetValue() == INDETERMINATE_ACTION
240 || textEntry->GetValue() == INDETERMINATE_STATE ) )
241 {
242 // These are tokens, not strings, so do a select all
243 textEntry->SelectAll();
244 }
245
246 // Needed at least on Windows to avoid hanging
247 aEvent.Skip();
248}
249
250
251void UNIT_BINDER::onComboBox( wxCommandEvent& aEvent )
252{
253 wxComboBox* combo = dynamic_cast<wxComboBox*>( m_valueCtrl );
254 wxCHECK( combo, /*void*/ );
255
256 const wxString value = combo->GetStringSelection();
257 const long long int conv = ValueFromString( *m_iuScale, m_units, value, m_dataType );
258
259 CallAfter(
260 [this, conv]
261 {
262 SetValue( conv );
263 } );
264
265 aEvent.Skip();
266}
267
268
269void UNIT_BINDER::onValueCtrlDestroyed( wxWindowDestroyEvent& aEvent )
270{
271 // The bound control is being destroyed before this binder. Drop the dialog registration and
272 // the control reference so neither the binder destructor nor SaveControlState() touches the
273 // freed window.
274 if( aEvent.GetEventObject() == m_valueCtrl )
275 {
276 if( m_dialogShim )
277 {
278 m_dialogShim->UnregisterUnitBinder( this );
279 m_dialogShim = nullptr;
280 }
281
282 m_valueCtrl = nullptr;
283 }
284
285 aEvent.Skip();
286}
287
288
289void UNIT_BINDER::onSetFocus( wxFocusEvent& aEvent )
290{
291 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
292
293 if( textEntry )
294 {
295 if( m_allowEval )
296 {
297 wxString oldStr = m_eval.OriginalText();
298
299 if( oldStr.length() && oldStr != textEntry->GetValue() )
300 {
301 textEntry->ChangeValue( oldStr );
302 textEntry->SetSelection( m_selStart, m_selEnd );
303 }
304
305 m_needsEval = true;
306 }
307
308 if( textEntry->GetValue() == INDETERMINATE_ACTION
309 || textEntry->GetValue() == INDETERMINATE_STATE )
310 {
311 // These are tokens, not strings, so do a select all
312 textEntry->SelectAll();
313 }
314 }
315
316 aEvent.Skip();
317}
318
319
320void UNIT_BINDER::onKillFocus( wxFocusEvent& aEvent )
321{
322 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
323
324 if( m_allowEval && textEntry )
325 {
326 wxString value = textEntry->GetValue();
327 bool success = m_eval.Process( value );
328
329 if( success && !value.IsEmpty() )
330 {
331 textEntry->GetSelection( &m_selStart, &m_selEnd );
332
333 value = m_eval.Result();
334
335 if( m_unitsInValue && !value.IsEmpty() )
336 {
338 value += wxT( " " );
339
341 }
342
343 textEntry->ChangeValue( value );
344
345#ifdef __WXGTK__
346 // Manually copy the selected text to the primary selection clipboard
347 if( wxTheClipboard->Open() )
348 {
349 wxString sel = textEntry->GetStringSelection();
350 bool clipTarget = wxTheClipboard->IsUsingPrimarySelection();
351 wxTheClipboard->UsePrimarySelection( true );
352 wxTheClipboard->SetData( new wxTextDataObject( sel ) );
353 wxTheClipboard->UsePrimarySelection( clipTarget );
354 wxTheClipboard->Close();
355 }
356#endif
357 }
358
359 m_needsEval = false;
360 }
361
362 aEvent.Skip();
363}
364
365
366static wxString valueDescriptionFromLabel( wxStaticText* aLabel )
367{
368 wxString desc = aLabel->GetLabel();
369
370 desc.EndsWith( wxT( ":" ), &desc );
371 return desc;
372}
373
374
375void UNIT_BINDER::delayedFocusHandler( wxCommandEvent& )
376{
377 if( !m_errorMessage.IsEmpty() )
379
380 m_errorMessage = wxEmptyString;
381 m_valueCtrl->SetFocus();
382}
383
384
386 double aBound, EDA_UNITS aDisplayUnits,
387 EDA_DATA_TYPE aDataType )
388{
389 // StringFromValue scales the user value once per dimension (twice for area, thrice for
390 // volume), so invert that same chain to bring the bound into internal units.
391 int conversions = 1;
392
393 switch( aDataType )
394 {
395 case EDA_DATA_TYPE::AREA: conversions = 2; break;
396 case EDA_DATA_TYPE::VOLUME: conversions = 3; break;
397 case EDA_DATA_TYPE::UNITLESS: conversions = 0; break;
398 default: conversions = 1; break;
399 }
400
401 double boundIU = aBound;
402
403 for( int i = 0; i < conversions; ++i )
404 boundIU = FromUserUnit( aIuScale, aBoundUnits, boundIU );
405
406 // GetText has no UNITLESS label, so asking for units text would assert
407 bool addUnitsText = aDataType != EDA_DATA_TYPE::UNITLESS;
408
409 return { boundIU, StringFromValue( aIuScale, aDisplayUnits, boundIU, addUnitsText, aDataType ) };
410}
411
412
413bool UNIT_BINDER::Validate( double aMin, double aMax, EDA_UNITS aUnits )
414{
415 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
416
417 if( !textEntry
418 || textEntry->GetValue() == INDETERMINATE_ACTION
419 || textEntry->GetValue() == INDETERMINATE_STATE )
420 {
421 return true;
422 }
423
424 // Note: aMin and aMax are not always given in internal units
425 RANGE_BOUND minBound = ConvertRangeBound( *m_iuScale, aUnits, aMin, m_units, m_dataType );
426 RANGE_BOUND maxBound = ConvertRangeBound( *m_iuScale, aUnits, aMax, m_units, m_dataType );
427
428 if( GetValue() < minBound.internalUnits )
429 {
430 m_errorMessage = wxString::Format( _( "%s must be at least %s." ),
432 minBound.displayText );
433
434 textEntry->SelectAll();
435
436 // Don't focus directly; we might be inside a KillFocus event handler
437 wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
438
439 return false;
440 }
441
442 if( GetValue() > maxBound.internalUnits )
443 {
444 m_errorMessage = wxString::Format( _( "%s must be less than %s." ),
446 maxBound.displayText );
447
448 textEntry->SelectAll();
449
450 // Don't focus directly; we might be inside a KillFocus event handler
451 wxPostEvent( this, wxCommandEvent( DELAY_FOCUS ) );
452
453 return false;
454 }
455
456 return true;
457}
458
459
460void UNIT_BINDER::SetValue( long long int aValue )
461{
462 double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
463 wxString textValue = StringFromValue( *m_iuScale, m_units, displayValue, false, m_dataType );
464 wxString formula = formulaYielding( aValue );
465
466 if( displayValue == 0 && m_negativeZero )
467 SetValue( wxT( "-" ) + textValue );
468 else
469 SetValue( textValue );
470
471 if( !formula.IsEmpty() )
472 m_eval.Process( formula );
473}
474
475
476void UNIT_BINDER::SetDoubleValue( double aValue )
477{
478 double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
479 wxString textValue = StringFromValue( *m_iuScale, m_units, setPrecision( displayValue, false ), false,
480 m_dataType );
481
482 if( displayValue == 0 && !std::signbit( displayValue ) && m_negativeZero )
483 SetValue( wxT( "-" ) + textValue );
484 else
485 SetValue( textValue );
486}
487
488
490{
491 SetDoubleValue( m_originTransforms.ToDisplay( aValue, m_coordType ) );
492}
493
494
495void UNIT_BINDER::SetValue( const wxString& aValue )
496{
497 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
498 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
499
500 wxString value = aValue;
501
502 if( m_unitsInValue && !value.IsEmpty() )
503 {
505 value += wxT( " " );
506
508 }
509
510 if( textEntry )
511 textEntry->SetValue( value );
512 else if( staticText )
513 staticText->SetLabel( value );
514
515 if( m_allowEval )
516 m_eval.Clear();
517
518 if( m_unitLabel )
520
521}
522
523
524wxString UNIT_BINDER::getTextForValue( long long int aValue ) const
525{
526 const double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
527 wxString textValue = StringFromValue( *m_iuScale, m_units, setPrecision( displayValue, false ), false,
528 m_dataType );
529
530 if( displayValue == 0 && m_negativeZero )
531 textValue = wxT( "-" ) + textValue;
532
533 return textValue;
534}
535
536
537wxString UNIT_BINDER::getTextForDoubleValue( double aValue ) const
538{
539 const double displayValue = m_originTransforms.ToDisplay( aValue, m_coordType );
540 wxString textValue = StringFromValue( *m_iuScale, m_units, setPrecision( displayValue, false ), false,
541 m_dataType );
542
543 if( displayValue == 0 && !std::signbit( displayValue ) && m_negativeZero )
544 textValue = wxT( "-" ) + textValue;
545
546 return textValue;
547}
548
549
550wxString UNIT_BINDER::formulaYielding( long long int aValue ) const
551{
552 wxString formula = m_eval.OriginalText();
553
554 // Evaluate afresh as the units may have changed since the expression was entered
555 if( !m_allowEval || m_needsEval || formula.IsEmpty() || !m_eval.Process( formula ) )
556 return wxEmptyString;
557
558 long long int displayValue = ValueFromString( *m_iuScale, m_units, m_eval.Result(), m_dataType );
559
560 if( std::abs( m_originTransforms.FromDisplay( displayValue, m_coordType ) - aValue ) > 1 )
561 return wxEmptyString;
562
563 return formula;
564}
565
566
567void UNIT_BINDER::ChangeValue( int aValue )
568{
569 wxString formula = formulaYielding( aValue );
570
571 ChangeValue( getTextForValue( aValue ) );
572
573 if( !formula.IsEmpty() )
574 m_eval.Process( formula );
575}
576
577
579{
581}
582
583
585{
586 ChangeDoubleValue( m_originTransforms.ToDisplay( aValue, m_coordType ) );
587}
588
589
590void UNIT_BINDER::ChangeValue( const wxString& aValue )
591{
592 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
593 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
594
595 wxString value = aValue;
596
597 if( m_unitsInValue && !value.IsEmpty() )
598 {
600 value += wxT( " " );
601
603 }
604
605 if( textEntry )
606 textEntry->ChangeValue( value );
607 else if( staticText )
608 staticText->SetLabel( value );
609
610 if( m_allowEval )
611 m_eval.Clear();
612
613 if( m_unitLabel )
615}
616
617
619{
620 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
621
622 if( !m_allowEval || !textEntry )
623 return wxEmptyString;
624
625 // The control holds the expression while it is being edited and its result afterwards
626 wxString formula = m_needsEval ? textEntry->GetValue() : m_eval.OriginalText();
627 double plainNumber;
628
629 formula.Trim( true ).Trim( false );
630
631 if( formula.IsEmpty() || formula.ToDouble( &plainNumber ) || formula.ToCDouble( &plainNumber ) )
632 return wxEmptyString;
633
634 return m_eval.Process( formula ) ? formula : wxString();
635}
636
637
638bool UNIT_BINDER::RestoreFormula( const wxString& aFormula )
639{
640 if( !m_allowEval || !dynamic_cast<wxTextEntry*>( m_valueCtrl ) || aFormula.IsEmpty() )
641 return false;
642
643 if( !m_eval.Process( aFormula ) )
644 {
645 // A failed expression must not be handed back to the control on the next focus
646 m_eval.Clear();
647 return false;
648 }
649
650 // ChangeValue() resets the evaluator, so the expression has to be processed again afterwards
651 ChangeValue( m_eval.Result() );
652 m_eval.Process( aFormula );
653 m_needsEval = false;
654
655 return true;
656}
657
658
659void UNIT_BINDER::RestoreValue( int aValue, const wxString& aFormula )
660{
661 RestoreFormula( aFormula );
662 ChangeValue( aValue );
663}
664
665
666long long int UNIT_BINDER::GetValue() const
667{
668 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
669 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
670 wxString value;
671
672 if( textEntry )
673 {
674 value = textEntry->GetValue();
675
676 if( m_needsEval && !value.IsEmpty() && m_eval.Process( value ) )
677 value = m_eval.Result();
678 else
679 value = textEntry->GetValue();
680 }
681 else if( staticText )
682 {
683 value = staticText->GetLabel();
684 }
685 else
686 {
687 return 0;
688 }
689
690 long long int displayValue = ValueFromString( *m_iuScale, m_units, value, m_dataType );
691 return m_originTransforms.FromDisplay( displayValue, m_coordType );
692}
693
694
695double UNIT_BINDER::setPrecision( double aValue, bool aValueUsesUserUnits ) const
696{
697 if( m_precision > 1 )
698 {
699 int scale = pow( 10, m_precision );
700 int64_t tmp = aValue;
701
702 if( !aValueUsesUserUnits )
703 tmp = ToUserUnit( *m_iuScale, m_units, aValue ) * scale;
704
705 aValue = static_cast<double>( tmp ) / scale;
706
707 if( !aValueUsesUserUnits )
708 aValue = FromUserUnit( *m_iuScale, m_units, aValue );
709 }
710
711 return aValue;
712}
713
714
716{
717 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_valueCtrl );
718 wxStaticText* staticText = dynamic_cast<wxStaticText*>( m_valueCtrl );
719 wxString value;
720
721 if( textEntry )
722 {
723 value = textEntry->GetValue();
724
725 if( m_needsEval && !value.IsEmpty() && m_eval.Process( value ) )
726 value = m_eval.Result();
727 else
728 value = textEntry->GetValue();
729 }
730 else if( staticText )
731 {
732 value = staticText->GetLabel();
733 }
734 else
735 {
736 return 0.0;
737 }
738
739 double displayValue = DoubleValueFromString( *m_iuScale, m_units, value, m_dataType );
740 displayValue = setPrecision( displayValue, false );
741
742 return m_originTransforms.FromDisplay( displayValue, m_coordType );
743}
744
745
750
751
752void UNIT_BINDER::SetOptionsList( std::span<const long long int> aOptions )
753{
754 wxComboBox* cb = dynamic_cast<wxComboBox*>( m_valueCtrl );
755 wxCHECK( cb, /* void */ );
756
757 cb->Clear();
758
759 for( long long int value : aOptions )
760 cb->Append( getTextForValue( value ) );
761}
762
763
764void UNIT_BINDER::SetDoubleOptionsList( std::span<const double> aOptions )
765{
766 wxComboBox* cb = dynamic_cast<wxComboBox*>( m_valueCtrl );
767 wxCHECK( cb, /* void */ );
768
769 cb->Clear();
770
771 for( double value : aOptions )
772 cb->Append( getTextForDoubleValue( value ) );
773}
774
775
777{
778 wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl );
779
780 if( te )
781 return te->GetValue() == INDETERMINATE_STATE || te->GetValue() == INDETERMINATE_ACTION;
782
783 return false;
784}
785
786
788{
789 if( wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl ) )
790 return te->GetValue().IsEmpty();
791
792 return false;
793}
794
795
797{
798 if( wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl ) )
799 return te->SetValue( wxEmptyString );
800}
801
802
803void UNIT_BINDER::SetLabel( const wxString& aLabel )
804{
805 m_label->SetLabel( aLabel );
806}
807
808
809void UNIT_BINDER::Enable( bool aEnable )
810{
811 if( m_label )
812 m_label->Enable( aEnable );
813
814 m_valueCtrl->Enable( aEnable );
815
816 if( m_unitLabel )
817 m_unitLabel->Enable( aEnable );
818}
819
820
821void UNIT_BINDER::Show( bool aShow, bool aResize )
822{
823 m_label->Show( aShow );
824 m_valueCtrl->Show( aShow );
825
826 if( m_unitLabel )
827 m_unitLabel->Show( aShow );
828
829 if( aResize )
830 {
831 if( aShow )
832 {
833 m_label->SetSize( -1, -1 );
834 m_valueCtrl->SetSize( -1, -1 );
835
836 if( m_unitLabel )
837 m_unitLabel->SetSize( -1, -1 );
838 }
839 else
840 {
841 m_label->SetSize( 0, 0 );
842 m_valueCtrl->SetSize( 0, 0 );
843
844 if( m_unitLabel )
845 m_unitLabel->SetSize( 0, 0 );
846 }
847 }
848}
849
850
852 UNIT_BINDER( aParent, nullptr, nullptr, nullptr, true, false )
853{
854 m_unitsInValue = true;
855}
856
857
861
863{
864 m_valueCtrl = aControl;
865
866 if( m_valueCtrl )
867 {
868 m_valueCtrl->Bind( wxEVT_SET_FOCUS, &PROPERTY_EDITOR_UNIT_BINDER::onSetFocus, this );
869 m_valueCtrl->Bind( wxEVT_KILL_FOCUS, &PROPERTY_EDITOR_UNIT_BINDER::onKillFocus, this );
870 m_valueCtrl->Bind( wxEVT_LEFT_UP, &PROPERTY_EDITOR_UNIT_BINDER::onClick, this );
871
872 m_valueCtrl->Bind( wxEVT_SHOW,
873 [&]( wxShowEvent& e )
874 {
875 if( !e.IsShown() )
876 SetControl( nullptr );
877 } );
878 }
879}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
The base frame for deriving all KiCad main window classes.
The base class for create windows for drawing purpose.
A class to perform either relative or absolute display origin transforms for a single axis of a point...
void SetControl(wxWindow *aControl)
PROPERTY_EDITOR_UNIT_BINDER(EDA_DRAW_FRAME *aParent)
wxTextCtrl wrapper to handle math expression evaluation.
double setPrecision(double aValue, bool aValueUsesUserUnits) const
When m_precision > 0 truncate the value aValue to show only m_precision digits in mantissa.
ORIGIN_TRANSFORMS::COORD_TYPES_T m_coordType
Type of coordinate for display origin transforms.
wxString getTextForValue(long long int aValue) const
virtual void ChangeDoubleValue(double aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion WITHOUT trigger...
void onKillFocus(wxFocusEvent &aEvent)
void Enable(bool aEnable)
Enable/disable the label, widget and units label.
void onClick(wxMouseEvent &aEvent)
DIALOG_SHIM * m_dialogShim
The dialog this binder registered itself with, or nullptr if it isn't owned by a dialog.
virtual void SetPrecision(int aLength)
Normally not needed, but can be used to set the precision when using internal units that are floats (...
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...
virtual wxString GetFormula() const
Return the expression the user entered if it is more than a plain number, otherwise an empty string.
virtual double GetDoubleValue() const
Return the current value in Internal Units.
wxString getTextForDoubleValue(double aValue) const
const EDA_IU_SCALE * m_iuScale
Currently used units.
wxString m_errorMessage
virtual EDA_ANGLE GetAngleValue()
bool m_negativeZero
Indicates "-0" should be displayed for 0.
virtual long long int GetValue() const
Return the current value in Internal Units.
wxString formulaYielding(long long int aValue) const
Return the retained expression if it evaluates to aValue, otherwise an empty string.
int m_precision
0 to 6.
static RANGE_BOUND ConvertRangeBound(const EDA_IU_SCALE &aIuScale, EDA_UNITS aBoundUnits, double aBound, EDA_UNITS aDisplayUnits, EDA_DATA_TYPE aDataType)
Convert a range bound and format its display string honoring aDataType.
wxStaticText * m_unitLabel
Can be nullptr.
bool m_unitsInValue
Units label should be included in value text.
wxWindow * m_valueCtrl
wxWindow * m_eventSource
void onSetFocus(wxFocusEvent &aEvent)
void onComboBox(wxCommandEvent &aEvent)
UNIT_BINDER(EDA_DRAW_FRAME *aParent, wxStaticText *aLabel, wxWindow *aValueCtrl, wxStaticText *aUnitLabel, bool aAllowEval=true, bool aBindFocusEvent=true)
wxStaticText * m_label
The bound widgets.
virtual void RestoreValue(int aValue, const wxString &aFormula)
Set a value (in Internal Units) as ChangeValue() does and reinstate the expression that produced it.
virtual bool RestoreFormula(const wxString &aFormula)
Show the result of an expression returned by GetFormula() and retain the expression so that it reappe...
ORIGIN_TRANSFORMS & m_originTransforms
A reference to an ORIGIN_TRANSFORMS object.
bool m_bindFocusEvent
long m_selStart
Selection start and end of the original text.
virtual void SetDoubleOptionsList(std::span< const double > aOptions)
void onValueCtrlDestroyed(wxWindowDestroyEvent &aEvent)
EDA_UNITS m_units
virtual void SetOptionsList(std::span< const long long int > aOptions)
Set the list of options for a combobox control.
bool IsIndeterminate() const
Return true if the control holds the indeterminate value (for instance, if it represents a multiple s...
void SetDataType(EDA_DATA_TYPE aDataType)
Used to override the datatype of the displayed property (default is DISTANCE)
int GetIntValue() const
void delayedFocusHandler(wxCommandEvent &aEvent)
EDA_DATA_TYPE m_dataType
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.
virtual ~UNIT_BINDER() override
bool UnitsInvariant() const
Definition unit_binder.h:70
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
bool IsNull() const
Return true if the control holds no value (ie: empty string, not 0).
void onUnitsChanged(wxCommandEvent &aEvent)
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
const int minSize
Push and Shove router track width and via size dialog.
#define _(s)
@ DEGREES_T
Definition eda_angle.h:30
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:34
EDA_UNITS
Definition eda_units.h:44
static wxString valueDescriptionFromLabel(wxStaticText *aLabel)
KICOMMON_API double FromUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Return in internal units the value aValue given in a real unit such as "in", "mm",...
KICOMMON_API long long int ValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue in aUnits to internal units used by the application.
KICOMMON_API wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Return the string from aValue according to aUnits (inch, mm ...) for display.
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue to a double.
KICOMMON_API double ToUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Convert aValue in internal units to the appropriate user units defined by aUnit.
KICOMMON_API wxString GetLabel(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
const int scale
A range bound converted to internal units together with its user-facing display string.
#define INDETERMINATE_ACTION
Definition ui_common.h:48
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:47
wxDEFINE_EVENT(DELAY_FOCUS, wxCommandEvent)
static wxString valueDescriptionFromLabel(wxStaticText *aLabel)