KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pg_editors.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) 2022-2024 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <eda_draw_frame.h>
26#include <widgets/unit_binder.h>
27
28#include <wx/log.h>
29
30const wxString PG_UNIT_EDITOR::EDITOR_NAME = wxS( "KiCadUnitEditor" );
31const wxString PG_CHECKBOX_EDITOR::EDITOR_NAME = wxS( "KiCadCheckboxEditor" );
32const wxString PG_COLOR_EDITOR::EDITOR_NAME = wxS( "KiCadColorEditor" );
33const wxString PG_RATIO_EDITOR::EDITOR_NAME = wxS( "KiCadRatioEditor" );
34
35
37 wxPGTextCtrlEditor(),
38 m_frame( aFrame )
39{
40 m_unitBinder = std::make_unique<PROPERTY_EDITOR_UNIT_BINDER>( m_frame );
41 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
42
44}
45
46
48{
49}
50
51
53{
54 return EDITOR_NAME + aFrame->GetName();
55}
56
57
59{
60 m_frame = aFrame;
61
62 if( aFrame )
63 {
64 m_unitBinder = std::make_unique<PROPERTY_EDITOR_UNIT_BINDER>( m_frame );
65 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
66 }
67 else
68 {
69 m_unitBinder = nullptr;
70 }
71}
72
73
74wxPGWindowList PG_UNIT_EDITOR::CreateControls( wxPropertyGrid* aPropGrid, wxPGProperty* aProperty,
75 const wxPoint& aPos, const wxSize& aSize ) const
76{
77 wxASSERT( m_unitBinder );
78
79#if wxCHECK_VERSION( 3, 3, 0 )
80 wxString text = aProperty->GetValueAsString( wxPGPropValFormatFlags::EditableValue );
81#else
82 wxString text = aProperty->GetValueAsString( wxPG_EDITABLE_VALUE );
83#endif
84 wxWindow* win = aPropGrid->GenerateEditorTextCtrl( aPos, aSize, text, nullptr, 0,
85 aProperty->GetMaxLength() );
86 wxPGWindowList ret( win, nullptr );
87
88 m_unitBinder->SetControl( win );
89 m_unitBinder->RequireEval();
90 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
91
92 if( PGPROPERTY_DISTANCE* prop = dynamic_cast<PGPROPERTY_DISTANCE*>( aProperty ) )
93 {
94 m_unitBinder->SetCoordType( prop->CoordType() );
95 }
96 else if( dynamic_cast<PGPROPERTY_AREA*>( aProperty) != nullptr )
97 {
98 m_unitBinder->SetDataType( EDA_DATA_TYPE::AREA );
99 }
100 else if( dynamic_cast<PGPROPERTY_ANGLE*>( aProperty ) != nullptr )
101 {
103 m_unitBinder->SetUnits( EDA_UNITS::DEGREES );
104 }
105
106 UpdateControl( aProperty, win );
107
108 return ret;
109}
110
111
112void PG_UNIT_EDITOR::UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) const
113{
114 wxVariant var = aProperty->GetValue();
115
116 if( var.GetType() == wxT( "std::optional<int>" ) )
117 {
118 auto* variantData = static_cast<STD_OPTIONAL_INT_VARIANT_DATA*>( var.GetData() );
119
120 if( variantData->Value().has_value() )
121 m_unitBinder->ChangeValue( variantData->Value().value() );
122 else
123 m_unitBinder->ChangeValue( wxEmptyString );
124 }
125 else if( var.GetType() == wxPG_VARIANT_TYPE_LONG )
126 {
127 m_unitBinder->ChangeValue( var.GetLong() );
128 }
129 else if( var.GetType() == wxPG_VARIANT_TYPE_LONGLONG )
130 {
131 m_unitBinder->ChangeDoubleValue( var.GetLongLong().ToDouble() );
132 }
133 else if( var.GetType() == wxPG_VARIANT_TYPE_DOUBLE )
134 {
135 m_unitBinder->ChangeValue( var.GetDouble() );
136 }
137 else if( var.GetType() == wxT( "EDA_ANGLE" ) )
138 {
139 EDA_ANGLE_VARIANT_DATA* angleData = static_cast<EDA_ANGLE_VARIANT_DATA*>( var.GetData() );
140 m_unitBinder->ChangeAngleValue( angleData->Angle() );
141 }
142 else if( !aProperty->IsValueUnspecified() )
143 {
144 wxFAIL_MSG( wxT( "PG_UNIT_EDITOR should only be used with numeric properties!" ) );
145 }
146}
147
148
149bool PG_UNIT_EDITOR::OnEvent( wxPropertyGrid* aPropGrid, wxPGProperty* aProperty,
150 wxWindow* aCtrl, wxEvent& aEvent ) const
151{
152 if( aEvent.GetEventType() == wxEVT_LEFT_UP )
153 {
154 if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( aCtrl ) )
155 {
156 if( !textCtrl->HasFocus() )
157 {
158 textCtrl->SelectAll();
159 return false;
160 }
161 }
162 }
163
164 return wxPGTextCtrlEditor::OnEvent( aPropGrid, aProperty, aCtrl, aEvent );
165}
166
167
168bool PG_UNIT_EDITOR::GetValueFromControl( wxVariant& aVariant, wxPGProperty* aProperty,
169 wxWindow* aCtrl ) const
170{
171 if( !m_unitBinder )
172 return false;
173
174 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( aCtrl );
175 wxCHECK_MSG( textCtrl, false, "PG_UNIT_EDITOR requires a text control!" );
176 wxString textVal = textCtrl->GetValue();
177
178 if( textVal == wxT( "<...>" ) )
179 {
180 aVariant.MakeNull();
181 return true;
182 }
183
184 bool changed;
185
186 if( dynamic_cast<PGPROPERTY_ANGLE*>( aProperty ) != nullptr )
187 {
188 EDA_ANGLE angle = m_unitBinder->GetAngleValue();
189
190 if( aVariant.GetType() == wxT( "EDA_ANGLE" ) )
191 {
192 EDA_ANGLE_VARIANT_DATA* ad = static_cast<EDA_ANGLE_VARIANT_DATA*>( aVariant.GetData() );
193 changed = ( aVariant.IsNull() || angle != ad->Angle() );
194
195 if( changed )
196 {
197 ad->SetAngle( angle );
198 m_unitBinder->SetAngleValue( angle );
199 }
200 }
201 else
202 {
203 changed = ( aVariant.IsNull() || angle.AsDegrees() != aVariant.GetDouble() );
204
205 if( changed )
206 {
207 aVariant = angle.AsDegrees();
208 m_unitBinder->SetValue( angle.AsDegrees() );
209 }
210 }
211 }
212 else if( dynamic_cast<PGPROPERTY_AREA*>( aProperty ) != nullptr )
213 {
214 wxLongLongNative result = m_unitBinder->GetValue();
215 changed = ( aVariant.IsNull() || result != aVariant.GetLongLong() );
216
217 if( changed )
218 {
219 aVariant = result;
220 m_unitBinder->SetDoubleValue( result.ToDouble() );
221 }
222 }
223 else if( aVariant.GetType() == wxT( "std::optional<int>" ) )
224 {
225 auto* variantData = static_cast<STD_OPTIONAL_INT_VARIANT_DATA*>( aVariant.GetData() );
226 std::optional<int> result;
227
228 if( m_unitBinder->IsNull() )
229 {
230 changed = ( aVariant.IsNull() || variantData->Value().has_value() );
231
232 if( changed )
233 {
234 aVariant = wxVariant( std::optional<int>() );
235 m_unitBinder->SetValue( wxEmptyString );
236 }
237 }
238 else
239 {
240 result = std::optional<int>( m_unitBinder->GetValue() );
241 changed = ( aVariant.IsNull() || result != variantData->Value() );
242
243 if( changed )
244 {
245 aVariant = wxVariant( result );
246 m_unitBinder->SetValue( result.value() );
247 }
248 }
249 }
250 else
251 {
252 long result = m_unitBinder->GetValue();
253 changed = ( aVariant.IsNull() || result != aVariant.GetLong() );
254
255 if( changed )
256 {
257 aVariant = result;
258 m_unitBinder->SetValue( result );
259 }
260 }
261
262 // Changing unspecified always causes event (returning
263 // true here should be enough to trigger it).
264 if( !changed && aVariant.IsNull() )
265 changed = true;
266
267 return changed;
268}
269
270
272 wxPGCheckBoxEditor()
273{
274}
275
276
277wxPGWindowList PG_CHECKBOX_EDITOR::CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty,
278 const wxPoint& aPos, const wxSize& aSize ) const
279{
280 // Override wx behavior and toggle unspecified checkboxes to "true"
281 // CreateControls for a checkbox editor is only triggered when the user activates the checkbox
282 // Set the value to false here; the base class will then trigger an event setting it true.
283 if( aProperty->IsValueUnspecified() )
284 aProperty->SetValueFromInt( 0 );
285
286 return wxPGCheckBoxEditor::CreateControls( aGrid, aProperty, aPos, aSize );
287}
288
289
290bool PG_COLOR_EDITOR::OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aWindow,
291 wxEvent& aEvent ) const
292{
293 return false;
294}
295
296
297wxPGWindowList PG_COLOR_EDITOR::CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty,
298 const wxPoint& aPos, const wxSize& aSize ) const
299{
300 auto colorProp = dynamic_cast<PGPROPERTY_COLOR4D*>( aProperty );
301
302 if( !colorProp )
303 return nullptr;
304
306 KIGFX::COLOR4D defColor = colorFromVariant( colorProp->GetDefaultValue() );
307
308 COLOR_SWATCH* editor = new COLOR_SWATCH( aGrid->GetPanel(), color, wxID_ANY,
309 colorProp->GetBackgroundColor(), defColor,
310 SWATCH_LARGE, true );
311 editor->SetPosition( aPos );
312 editor->SetSize( aSize );
313
314 editor->Bind( COLOR_SWATCH_CHANGED,
315 [=]( wxCommandEvent& aEvt )
316 {
317 wxVariant val;
318 auto data = new COLOR4D_VARIANT_DATA( editor->GetSwatchColor() );
319 val.SetData( data );
320 aGrid->ChangePropertyValue( colorProp, val );
321 } );
322
323#if wxCHECK_VERSION( 3, 3, 0 )
324 if( aGrid->GetInternalFlags() & wxPropertyGrid::wxPG_FL_ACTIVATION_BY_CLICK )
325#else
326 if( aGrid->GetInternalFlags() & wxPG_FL_ACTIVATION_BY_CLICK )
327#endif
328 {
329 aGrid->CallAfter(
330 [=]()
331 {
332 editor->GetNewSwatchColor();
333 } );
334 }
335
336 return editor;
337}
338
339
340void PG_COLOR_EDITOR::UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) const
341{
342 if( auto swatch = dynamic_cast<COLOR_SWATCH*>( aCtrl ) )
343 swatch->SetSwatchColor( colorFromProperty( aProperty ), false );
344}
345
346
347KIGFX::COLOR4D PG_COLOR_EDITOR::colorFromVariant( const wxVariant& aVariant ) const
348{
350 COLOR4D_VARIANT_DATA* data = nullptr;
351
352 if( aVariant.IsType( wxS( "COLOR4D" ) ) )
353 {
354 data = static_cast<COLOR4D_VARIANT_DATA*>( aVariant.GetData() );
355 color = data->Color();
356 }
357
358 return color;
359}
360
361
363{
364 return colorFromVariant( aProperty->GetValue() );
365}
366
367
368bool PG_RATIO_EDITOR::GetValueFromControl( wxVariant& aVariant, wxPGProperty* aProperty,
369 wxWindow* aCtrl ) const
370{
371 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( aCtrl );
372 wxCHECK_MSG( textCtrl, false, "PG_RATIO_EDITOR requires a text control!" );
373 wxString textVal = textCtrl->GetValue();
374
375 if( textVal == wxT( "<...>" ) )
376 {
377 aVariant.MakeNull();
378 return true;
379 }
380
381 bool changed;
382
383 if( aVariant.GetType() == wxT( "std::optional<double>" ) )
384 {
385 auto* variantData = static_cast<STD_OPTIONAL_DOUBLE_VARIANT_DATA*>( aVariant.GetData() );
386
387 if( textVal.empty() )
388 {
389 changed = ( aVariant.IsNull() || variantData->Value().has_value() );
390
391 if( changed )
392 aVariant = wxVariant( std::optional<double>() );
393 }
394 else
395 {
396 double dblValue;
397 textVal.ToDouble( &dblValue );
398 std::optional<double> result( dblValue );
399 changed = ( aVariant.IsNull() || result != variantData->Value() );
400
401 if( changed )
402 {
403 aVariant = wxVariant( result );
404 textCtrl->SetValue( wxString::Format( wxS( "%g" ), dblValue ) );
405 }
406 }
407 }
408 else
409 {
410 double result;
411 textVal.ToDouble( &result );
412 changed = ( aVariant.IsNull() || result != aVariant.GetDouble() );
413
414 if( changed )
415 {
416 aVariant = result;
417 textCtrl->SetValue( wxString::Format( wxS( "%g" ), result ) );
418 }
419 }
420
421 // Changing unspecified always causes event (returning
422 // true here should be enough to trigger it).
423 if( !changed && aVariant.IsNull() )
424 changed = true;
425
426 return changed;
427}
428
429
430void PG_RATIO_EDITOR::UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) const
431{
432 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( aCtrl );
433 wxVariant var = aProperty->GetValue();
434
435 wxCHECK_MSG( textCtrl, /*void*/, wxT( "PG_RATIO_EDITOR must be used with a textCtrl!" ) );
436
437 if( var.GetType() == wxT( "std::optional<double>" ) )
438 {
439 auto* variantData = static_cast<STD_OPTIONAL_DOUBLE_VARIANT_DATA*>( var.GetData() );
440 wxString strValue;
441
442 if( variantData->Value().has_value() )
443 strValue = wxString::Format( wxS( "%g" ), variantData->Value().value() );
444
445 textCtrl->ChangeValue( strValue );
446 }
447 else if( var.GetType() == wxPG_VARIANT_TYPE_DOUBLE )
448 {
449 textCtrl->ChangeValue( wxString::Format( wxS( "%g" ), var.GetDouble() ) );
450 }
451 else if( !aProperty->IsValueUnspecified() )
452 {
453 wxFAIL_MSG( wxT( "PG_RATIO_EDITOR should only be used with scale-free numeric properties!" ) );
454 }
455}
int color
Definition: DXF_plotter.cpp:58
const KIGFX::COLOR4D & Color()
A simple color swatch of the kind used to set layer colors.
Definition: color_swatch.h:57
const EDA_ANGLE & Angle()
void SetAngle(const EDA_ANGLE &aAngle)
double AsDegrees() const
Definition: eda_angle.h:155
The base class for create windows for drawing purpose.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition: color4d.h:398
A wxEnumProperty that displays a color next to the enum value.
wxPGWindowList CreateControls(wxPropertyGrid *aGrid, wxPGProperty *aProperty, const wxPoint &aPos, const wxSize &aSize) const override
Definition: pg_editors.cpp:277
static const wxString EDITOR_NAME
Definition: pg_editors.h:75
static const wxString EDITOR_NAME
Definition: pg_editors.h:91
wxPGWindowList CreateControls(wxPropertyGrid *aGrid, wxPGProperty *aProperty, const wxPoint &aPos, const wxSize &aSize) const override
Definition: pg_editors.cpp:297
bool OnEvent(wxPropertyGrid *aGrid, wxPGProperty *aProperty, wxWindow *aWindow, wxEvent &aEvent) const override
Definition: pg_editors.cpp:290
KIGFX::COLOR4D colorFromVariant(const wxVariant &aVariant) const
Definition: pg_editors.cpp:347
void UpdateControl(wxPGProperty *aProperty, wxWindow *aCtrl) const override
Definition: pg_editors.cpp:340
KIGFX::COLOR4D colorFromProperty(wxPGProperty *aProperty) const
Definition: pg_editors.cpp:362
bool GetValueFromControl(wxVariant &aVariant, wxPGProperty *aProperty, wxWindow *aCtrl) const override
Definition: pg_editors.cpp:368
static const wxString EDITOR_NAME
Definition: pg_editors.h:117
void UpdateControl(wxPGProperty *aProperty, wxWindow *aCtrl) const override
Definition: pg_editors.cpp:430
std::unique_ptr< PROPERTY_EDITOR_UNIT_BINDER > m_unitBinder
Definition: pg_editors.h:66
wxPGWindowList CreateControls(wxPropertyGrid *aPropGrid, wxPGProperty *aProperty, const wxPoint &aPos, const wxSize &aSize) const override
Definition: pg_editors.cpp:74
static const wxString EDITOR_NAME
Definition: pg_editors.h:34
void UpdateControl(wxPGProperty *aProperty, wxWindow *aCtrl) const override
Definition: pg_editors.cpp:112
void UpdateFrame(EDA_DRAW_FRAME *aFrame)
When restarting an editor, the instance of PG_UNIT_EDITOR may be the same but the referenced frame is...
Definition: pg_editors.cpp:58
EDA_DRAW_FRAME * m_frame
Definition: pg_editors.h:64
PG_UNIT_EDITOR(EDA_DRAW_FRAME *aFrame)
Definition: pg_editors.cpp:36
bool GetValueFromControl(wxVariant &aVariant, wxPGProperty *aProperty, wxWindow *aCtrl) const override
Definition: pg_editors.cpp:168
wxString m_editorName
Definition: pg_editors.h:68
bool OnEvent(wxPropertyGrid *aPropGrid, wxPGProperty *aProperty, wxWindow *aCtrl, wxEvent &aEvent) const override
Definition: pg_editors.cpp:149
static wxString BuildEditorName(EDA_DRAW_FRAME *aFrame)
Definition: pg_editors.cpp:52
virtual ~PG_UNIT_EDITOR()
Definition: pg_editors.cpp:47
EDA_UNITS GetUserUnits() const
@ SWATCH_LARGE
Definition: color_swatch.h:42
static std::string strValue(double aValue)