KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_sim_model.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 Mikolaj Wielgus
5 * Copyright (C) 2022 CERN
6 * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
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 3
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 * https://www.gnu.org/licenses/gpl-3.0.html
21 * or you may search the http://www.gnu.org website for the version 3 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
27#include <dialog_sim_model.h>
28#include <sim/sim_property.h>
31#include <sim/sim_model.h>
32#include <sim/sim_model_kibis.h>
36#include <grid_tricks.h>
39#include <kiplatform/ui.h>
40#include <confirm.h>
41#include <string_utils.h>
42#include <locale_io.h>
43#include <wx/filedlg.h>
44#include <wx/textfile.h>
45#include <fmt/format.h>
46
48
49
50bool equivalent( SIM_MODEL::DEVICE_T a, SIM_MODEL::DEVICE_T b )
51{
52 // A helper to handle SPICE's use of 'E' and 'H' for voltage sources and 'F' and 'G' for
53 // current sources
54 return a == b
55 || SIM_MODEL::DeviceInfo( a ).description == SIM_MODEL::DeviceInfo( b ).description;
56};
57
58
59template <typename T_symbol, typename T_field>
60DIALOG_SIM_MODEL<T_symbol, T_field>::DIALOG_SIM_MODEL( wxWindow* aParent, T_symbol& aSymbol,
61 std::vector<T_field>& aFields ) :\
62 DIALOG_SIM_MODEL_BASE( aParent ),
63 m_symbol( aSymbol ),
64 m_fields( aFields ),
65 m_libraryModelsMgr( &Prj() ),
66 m_builtinModelsMgr( &Prj() ),
67 m_prevModel( nullptr ),
68 m_curModelType( SIM_MODEL::TYPE::NONE ),
69 m_scintillaTricksCode( nullptr ),
70 m_scintillaTricksSubckt( nullptr ),
71 m_firstCategory( nullptr ),
72 m_prevParamGridSelection( nullptr ),
73 m_lastParamGridWidth( 0 ),
74 m_inKillFocus( false )
75{
76 m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
77
78 for( LIB_PIN* pin : aSymbol.GetAllLibPins() )
79 {
80 // De Morgan conversions are equivalences, not additional items to simulate
81 if( !pin->GetParent()->HasConversion() || pin->GetConvert() < 2 )
82 m_sortedPartPins.push_back( pin );
83 }
84
85 std::sort( m_sortedPartPins.begin(), m_sortedPartPins.end(),
86 []( const LIB_PIN* lhs, const LIB_PIN* rhs )
87 {
88 // We sort by StrNumCmp because SIM_MODEL_BASE sorts with it too.
89 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
90 } );
91
92 m_waveformChoice->Clear();
93 m_deviceChoice->Clear();
94 m_deviceTypeChoice->Clear();
95
96 m_scintillaTricksCode = new SCINTILLA_TRICKS( m_codePreview, wxT( "{}" ), false );
97 m_scintillaTricksSubckt = new SCINTILLA_TRICKS( m_subckt, wxT( "()" ), false );
98
99 m_paramGridMgr->Bind( wxEVT_PG_SELECTED, &DIALOG_SIM_MODEL::onParamGridSelectionChange, this );
100
101 wxPropertyGrid* grid = m_paramGrid->GetGrid();
102
103 // In wx 3.0 the color will be wrong sometimes.
104 grid->SetCellDisabledTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
105
106 grid->Bind( wxEVT_SET_FOCUS, &DIALOG_SIM_MODEL::onParamGridSetFocus, this );
107 grid->Bind( wxEVT_UPDATE_UI, &DIALOG_SIM_MODEL::onUpdateUI, this );
108
109 grid->DedicateKey( WXK_RETURN );
110 grid->DedicateKey( WXK_NUMPAD_ENTER );
111 grid->DedicateKey( WXK_UP );
112 grid->DedicateKey( WXK_DOWN );
113
114#if wxCHECK_VERSION( 3, 3, 0 )
115 grid->AddActionTrigger( wxPGKeyboardActions::Edit, WXK_RETURN );
116 grid->AddActionTrigger( wxPGKeyboardActions::NextProperty, WXK_RETURN );
117 grid->AddActionTrigger( wxPGKeyboardActions::Edit, WXK_NUMPAD_ENTER );
118 grid->AddActionTrigger( wxPGKeyboardActions::NextProperty, WXK_NUMPAD_ENTER );
119#else
120 grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_RETURN );
121 grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RETURN );
122 grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_NUMPAD_ENTER );
123 grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_NUMPAD_ENTER );
124#endif
125
126 m_pinAssignmentsGrid->PushEventHandler( new GRID_TRICKS( m_pinAssignmentsGrid ) );
127
129
130 // Now all widgets have the size fixed, call FinishDialogSettings
132}
133
134
135template <typename T_symbol, typename T_field>
137{
138 // Disable all properties. This is necessary because some of their methods are called after
139 // destruction of DIALOG_SIM_MODEL, oddly. When disabled, they never access their models.
140 for( wxPropertyGridIterator it = m_paramGrid->GetIterator(); !it.AtEnd(); ++it )
141 {
142 SIM_PROPERTY* prop = dynamic_cast<SIM_PROPERTY*>( *it );
143
144 if( !prop )
145 continue;
146
147 prop->Disable();
148 }
149
150 // Delete the GRID_TRICKS.
151 m_pinAssignmentsGrid->PopEventHandler( true );
152
153 delete m_scintillaTricksCode;
154 delete m_scintillaTricksSubckt;
155}
156
157
158template <typename T_symbol, typename T_field>
160{
161 wxCommandEvent dummyEvent;
162 wxString deviceType;
163 wxString modelType;
164 wxString modelParams;
165 wxString pinMap;
166 bool storeInValue = false;
167
168 wxString msg;
169 WX_STRING_REPORTER reporter( &msg );
170
171 auto setFieldValue =
172 [&]( const wxString& aFieldName, const wxString& aValue )
173 {
174 for( T_field& field : m_fields )
175 {
176 if( field.GetName() == aFieldName )
177 {
178 field.SetText( aValue );
179 return;
180 }
181 }
182
183 m_fields.emplace_back( &m_symbol, -1, aFieldName );
184 m_fields.back().SetText( aValue );
185 };
186
187 // Infer RLC and VI models if they aren't specified
188 if( SIM_MODEL::InferSimModel( m_symbol, &m_fields, false, SIM_VALUE_GRAMMAR::NOTATION::SI,
189 &deviceType, &modelType, &modelParams, &pinMap ) )
190 {
191 setFieldValue( SIM_DEVICE_TYPE_FIELD, deviceType );
192
193 if( !modelType.IsEmpty() )
194 setFieldValue( SIM_TYPE_FIELD, modelType );
195
196 setFieldValue( SIM_PARAMS_FIELD, modelParams );
197
198 setFieldValue( SIM_PINS_FIELD, pinMap );
199
200 storeInValue = true;
201
202 // In case the storeInValue checkbox is turned off (if it's left on then we'll overwrite
203 // this field with the actual value):
204 m_fields[ VALUE_FIELD ].SetText( wxT( "${SIM.PARAMS}" ) );
205 }
206
207 std::string libraryFilename = SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY::LIBRARY_FIELD );
208
209 if( libraryFilename != "" )
210 {
211 // The model is sourced from a library, optionally with instance overrides.
212 m_rbLibraryModel->SetValue( true );
213
214 if( !loadLibrary( libraryFilename ) )
215 {
216 m_libraryPathText->ChangeValue( libraryFilename );
217 m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields, reporter );
218
219 // load library will mangle the set reporter
220 m_libraryModelsMgr.CreateModel( nullptr, m_sortedPartPins, m_fields, reporter );
221
222 m_modelNameChoice->Append( _( "<unknown>" ) );
223 m_modelNameChoice->SetSelection( 0 );
224 }
225 else
226 {
227 std::string modelName = SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY::NAME_FIELD );
228 int modelIdx = m_modelNameChoice->FindString( modelName );
229
230 if( modelIdx == wxNOT_FOUND )
231 {
232 DisplayErrorMessage( this, wxString::Format( _( "No model named '%s' in library." ),
233 modelName ) );
234
235 // Default to first item in library
236 m_modelNameChoice->SetSelection( 0 );
237 }
238 else
239 {
240 m_modelNameChoice->SetSelection( modelIdx );
241 }
242
243 m_curModelType = curModel().GetType();
244 }
245
246 if( isIbisLoaded() && ( m_modelNameChoice->GetSelection() >= 0 ) )
247 {
248 int idx = m_modelNameChoice->GetSelection();
249 auto kibismodel = dynamic_cast<SIM_MODEL_KIBIS*>( &m_libraryModelsMgr.GetModels()[idx].get() );
250
251 if( kibismodel )
252 {
253 onModelNameChoice( dummyEvent ); // refresh list of pins
254
255 int i = 0;
256
257 for( const std::pair<std::string, std::string>& strs : kibismodel->GetIbisPins() )
258 {
259 if( strs.first == SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY_KIBIS::PIN_FIELD ) )
260 {
261 auto kibisLibrary = static_cast<const SIM_LIBRARY_KIBIS*>( library() );
262
263 kibismodel->ChangePin( *kibisLibrary, strs.first );
264 m_pinCombobox->SetSelection( static_cast<int>( i ) );
265 break;
266 }
267 i++;
268 }
269
270 if( i < static_cast<int>( kibismodel->GetIbisPins().size() ) )
271 {
272 onPinCombobox( dummyEvent ); // refresh list of models
273
274 m_pinModelCombobox->SetStringSelection(
276 }
277
279 {
280 kibismodel->SwitchSingleEndedDiff( true );
281 m_differentialCheckbox->SetValue( true );
282 }
283 else
284 {
285 kibismodel->SwitchSingleEndedDiff( false );
286 m_differentialCheckbox->SetValue( false );
287 }
288 }
289 }
290 }
292 || !SIM_MODEL::GetFieldValue( &m_fields, SIM_TYPE_FIELD ).empty() )
293 {
294 // The model is sourced from the instance.
295 m_rbBuiltinModel->SetValue( true );
296
297 msg.clear();
298 m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields, reporter );
299
300 if( reporter.HasMessage() )
301 DisplayErrorMessage( this, msg );
302 }
303
304 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
305 {
306 if( m_rbBuiltinModel->GetValue() && type == m_curModelType )
307 {
308 msg.clear();
309 m_builtinModelsMgr.CreateModel( m_fields, m_sortedPartPins, false, reporter );
310
311 if( reporter.HasMessage() )
312 {
313 DisplayErrorMessage( this, _( "Failed to read simulation model from fields." )
314 + wxT( "\n\n" ) + msg );
315 }
316 }
317 else
318 {
319 m_builtinModelsMgr.CreateModel( type, m_sortedPartPins, reporter );
320 }
321
322 SIM_MODEL::DEVICE_T deviceTypeT = SIM_MODEL::TypeInfo( type ).deviceType;
323
324 if( !m_curModelTypeOfDeviceType.count( deviceTypeT ) )
325 m_curModelTypeOfDeviceType[deviceTypeT] = type;
326 }
327
328 if( storeInValue )
329 curModel().SetIsStoredInValue( true );
330
331 m_saveInValueCheckbox->SetValue( curModel().IsStoredInValue() );
332
333 onRadioButton( dummyEvent );
334 return DIALOG_SIM_MODEL_BASE::TransferDataToWindow();
335}
336
337
338template <typename T_symbol, typename T_field>
340{
341 m_pinAssignmentsGrid->CommitPendingChanges();
342 m_paramGrid->GetGrid()->CommitChangesFromEditor();
343
344 if( !DIALOG_SIM_MODEL_BASE::TransferDataFromWindow() )
345 return false;
346
347 std::string path;
348 std::string name;
349
350 if( m_rbLibraryModel->GetValue() )
351 {
352 path = m_libraryPathText->GetValue();
353 wxFileName fn( path );
354
355 if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( ".." ) )
356 path = fn.GetFullPath();
357
358 if( !m_modelNameChoice->IsEmpty() )
359 name = m_modelNameChoice->GetStringSelection().ToStdString();
360 else if( dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( &curModel() ) )
362 }
363
366
367 if( isIbisLoaded() )
368 {
369 SIM_MODEL_KIBIS* ibismodel = static_cast<SIM_MODEL_KIBIS*>(
370 &m_libraryModelsMgr.GetModels().at( m_modelNameChoice->GetSelection() ).get() );
371
372 if( ibismodel )
373 {
374 std::string pins;
375 std::string modelName = std::string( m_pinModelCombobox->GetValue().c_str() );
376 std::string differential;
377
378 if( m_pinCombobox->GetSelection() >= 0 )
379 pins = ibismodel->GetIbisPins().at( m_pinCombobox->GetSelection() ).first;
380
381 if( ibismodel->CanDifferential() && m_differentialCheckbox->GetValue() )
382 differential = "1";
383
387 }
388 }
389
390 if( curModel().GetType() == SIM_MODEL::TYPE::RAWSPICE )
391 {
392 if( m_modelNotebook->GetSelection() == 0 )
393 updateModelCodeTab( &curModel() );
394
395 wxString code = m_codePreview->GetText().Trim( true ).Trim( false );
396 curModel().SetParamValue( "model", std::string( code.ToUTF8() ) );
397 }
398
399 curModel().SetIsStoredInValue( m_saveInValueCheckbox->GetValue() );
400
401 curModel().WriteFields( m_fields );
402
403 return true;
404}
405
406
407template <typename T_symbol, typename T_field>
409{
410 SIM_MODEL* model = &curModel();
411
412 updateIbisWidgets( model );
413 updateBuiltinModelWidgets( model );
414 updateModelParamsTab( model );
415 updateModelCodeTab( model );
416 updatePinAssignments( model );
417
418 std::string ref = SIM_MODEL::GetFieldValue( &m_fields, SIM_REFERENCE_FIELD );
419
420 m_modelPanel->Layout();
421 m_pinAssignmentsPanel->Layout();
422 m_parametersPanel->Layout();
423 m_codePanel->Layout();
424
425 SendSizeEvent( wxSEND_EVENT_POST );
426
427 m_prevModel = &curModel();
428}
429
430
431template <typename T_symbol, typename T_field>
433{
434 SIM_MODEL_KIBIS* modelkibis = isIbisLoaded() ? dynamic_cast<SIM_MODEL_KIBIS*>( aModel )
435 : nullptr;
436
437 m_pinLabel->Show( isIbisLoaded() );
438 m_pinCombobox->Show( isIbisLoaded() );
439 m_pinModelLabel->Show( isIbisLoaded() );
440 m_pinModelCombobox->Show( isIbisLoaded() );
441 m_waveformLabel->Show( isIbisLoaded() );
442 m_waveformChoice->Show( isIbisLoaded() );
443
444 if( aModel != m_prevModel )
445 {
446 m_waveformChoice->Clear();
447
448 if( isIbisLoaded() )
449 {
450 for( SIM_MODEL::TYPE type : { SIM_MODEL::TYPE::KIBIS_DEVICE,
451 SIM_MODEL::TYPE::KIBIS_DRIVER_DC,
452 SIM_MODEL::TYPE::KIBIS_DRIVER_RECT,
453 SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS } )
454 {
455 SIM_MODEL::DEVICE_T deviceType = SIM_MODEL::TypeInfo( type ).deviceType;
456 const std::string& deviceTypeDesc = SIM_MODEL::DeviceInfo( deviceType ).description;
457
458 if( deviceType == aModel->GetDeviceType()
459 || deviceTypeDesc == aModel->GetDeviceInfo().description )
460 {
461 m_waveformChoice->Append( SIM_MODEL::TypeInfo( type ).description );
462
463 if( type == aModel->GetType() )
464 m_waveformChoice->SetSelection( m_waveformChoice->GetCount() - 1 );
465 }
466 }
467 }
468 }
469
470 m_differentialCheckbox->Show( isIbisLoaded() && modelkibis && modelkibis->CanDifferential() );
471 m_modelNameLabel->SetLabel( isIbisLoaded() ? _( "Component:" ) : _( "Model:" ) );
472}
473
474
475template <typename T_symbol, typename T_field>
477{
478 // Change the Type choice to match the current device type.
479 if( aModel != m_prevModel )
480 {
481 m_deviceChoice->Clear();
482 m_deviceTypeChoice->Clear();
483
484 if( m_rbBuiltinModel->GetValue() )
485 {
486 for( SIM_MODEL::DEVICE_T deviceType : SIM_MODEL::DEVICE_T_ITERATOR() )
487 {
488 if( !SIM_MODEL::DeviceInfo( deviceType ).showInMenu )
489 continue;
490
491 m_deviceChoice->Append( SIM_MODEL::DeviceInfo( deviceType ).description );
492
493 if( equivalent( deviceType, aModel->GetDeviceType() ) )
494 m_deviceChoice->SetSelection( m_deviceChoice->GetCount() - 1 );
495 }
496
497 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
498 {
499 if( type == SIM_MODEL::TYPE::KIBIS_DEVICE
500 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_DC
501 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_RECT
502 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS )
503 {
504 continue;
505 }
506
507 SIM_MODEL::DEVICE_T deviceType = SIM_MODEL::TypeInfo( type ).deviceType;
508 const std::string& deviceTypeDesc = SIM_MODEL::DeviceInfo( deviceType ).description;
509
510 if( deviceType == aModel->GetDeviceType()
511 || deviceTypeDesc == aModel->GetDeviceInfo().description )
512 {
513 m_deviceTypeChoice->Append( SIM_MODEL::TypeInfo( type ).description );
514
515 if( type == aModel->GetType() )
516 m_deviceTypeChoice->SetSelection( m_deviceTypeChoice->GetCount() - 1 );
517 }
518 }
519 }
520 }
521
522 m_deviceTypeChoice->Enable( !m_rbLibraryModel->GetValue() );
523
524 if( dynamic_cast<SIM_MODEL_RAW_SPICE*>( aModel ) )
525 m_modelNotebook->SetSelection( 1 );
526 else
527 m_modelNotebook->SetSelection( 0 );
528
529 if( aModel->HasPrimaryValue() )
530 {
531 const SIM_MODEL::PARAM& primary = aModel->GetParam( 0 );
532
533 m_saveInValueCheckbox->SetLabel( wxString::Format( _( "Save parameter '%s (%s)' in Value "
534 "field" ),
535 primary.info.description,
536 primary.info.name ) );
537 m_saveInValueCheckbox->Enable( true );
538 }
539 else
540 {
541 m_saveInValueCheckbox->SetLabel( _( "Save primary parameter in Value field" ) );
542 m_saveInValueCheckbox->SetValue( false );
543 m_saveInValueCheckbox->Enable( false );
544 }
545}
546
547
548template <typename T_symbol, typename T_field>
550{
551 if( aModel != m_prevModel )
552 {
553 // This wxPropertyGridManager column and header stuff has to be here because it segfaults in
554 // the constructor.
555
556 m_paramGridMgr->SetColumnCount( PARAM_COLUMN::END_ );
557
558 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::DESCRIPTION, _( "Parameter" ) );
559 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::UNIT, _( "Unit" ) );
560 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::DEFAULT, _( "Default" ) );
561 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::TYPE, _( "Type" ) );
562
563 m_paramGridMgr->ShowHeader();
564
565
566 m_paramGrid->Clear();
567
568 m_firstCategory = m_paramGrid->Append( new wxPropertyCategory( "Geometry" ) );
569 m_paramGrid->HideProperty( "Geometry" );
570
571 m_paramGrid->Append( new wxPropertyCategory( "AC" ) );
572 m_paramGrid->HideProperty( "AC" );
573
574 m_paramGrid->Append( new wxPropertyCategory( "DC" ) );
575 m_paramGrid->HideProperty( "DC" );
576
577 m_paramGrid->Append( new wxPropertyCategory( "S-Parameters" ) );
578 m_paramGrid->HideProperty( "S-Parameters" );
579
580 m_paramGrid->Append( new wxPropertyCategory( "Capacitance" ) );
581 m_paramGrid->HideProperty( "Capacitance" );
582
583 m_paramGrid->Append( new wxPropertyCategory( "Temperature" ) );
584 m_paramGrid->HideProperty( "Temperature" );
585
586 m_paramGrid->Append( new wxPropertyCategory( "Noise" ) );
587 m_paramGrid->HideProperty( "Noise" );
588
589 m_paramGrid->Append( new wxPropertyCategory( "Distributed Quantities" ) );
590 m_paramGrid->HideProperty( "Distributed Quantities" );
591
592 m_paramGrid->Append( new wxPropertyCategory( "Waveform" ) );
593 m_paramGrid->HideProperty( "Waveform" );
594
595 m_paramGrid->Append( new wxPropertyCategory( "Limiting Values" ) );
596 m_paramGrid->HideProperty( "Limiting Values" );
597
598 m_paramGrid->Append( new wxPropertyCategory( "Advanced" ) );
599 m_paramGrid->HideProperty( "Advanced" );
600
601 m_paramGrid->Append( new wxPropertyCategory( "Flags" ) );
602 m_paramGrid->HideProperty( "Flags" );
603
604 m_paramGrid->CollapseAll();
605
606 for( int i = 0; i < aModel->GetParamCount(); ++i )
607 addParamPropertyIfRelevant( aModel, i );
608
609 m_paramGrid->CollapseAll();
610 m_paramGrid->Expand( "AC" );
611 m_paramGrid->Expand( "Waveform" );
612 }
613
614 adjustParamGridColumns( m_paramGrid->GetGrid()->GetSize().GetX(), true );
615
616 // Set all properties to default colors.
617 // Update properties in models that have autofill.
618 for( wxPropertyGridIterator it = m_paramGrid->GetIterator(); !it.AtEnd(); ++it )
619 {
620 wxColour bgCol = m_paramGrid->GetGrid()->GetPropertyDefaultCell().GetBgCol();
621 wxColour fgCol = m_paramGrid->GetGrid()->GetPropertyDefaultCell().GetFgCol();
622
623 for( int col = 0; col < m_paramGridMgr->GetColumnCount(); ++col )
624 {
625 ( *it )->GetCell( col ).SetBgCol( bgCol );
626 ( *it )->GetCell( col ).SetFgCol( fgCol );
627 }
628
629 SIM_PROPERTY* prop = dynamic_cast<SIM_PROPERTY*>( *it );
630
631 if( !prop )
632 continue;
633
634 const SIM_MODEL::PARAM& param = prop->GetParam();
635
636 // Model values other than the currently edited value may have changed. Update them.
637 // This feature is called "autofill" and present only in certain models. Don't do it for
638 // models that don't have it for performance reasons.
639 if( aModel->HasAutofill() )
640 ( *it )->SetValueFromString( param.value );
641 }
642}
643
644
645template <typename T_symbol, typename T_field>
647{
648 if( dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( aModel ) )
649 return;
650
651 wxString text;
652 SPICE_ITEM item;
653
654 item.modelName = m_modelNameChoice->GetStringSelection();
655
656 if( m_rbBuiltinModel->GetValue() || item.modelName == "" )
657 item.modelName = m_fields.at( REFERENCE_FIELD ).GetText();
658
659 text << aModel->SpiceGenerator().Preview( item );
660
661 m_codePreview->SetText( text );
662 m_codePreview->SelectNone();
663}
664
665
666template <typename T_symbol, typename T_field>
668{
669 removeOrphanedPinAssignments( aModel );
670
671 // Reset the grid.
672
673 m_pinAssignmentsGrid->ClearRows();
674 m_pinAssignmentsGrid->AppendRows( static_cast<int>( m_sortedPartPins.size() ) );
675
676 for( int row = 0; row < m_pinAssignmentsGrid->GetNumberRows(); ++row )
677 m_pinAssignmentsGrid->SetCellValue( row, PIN_COLUMN::MODEL, _( "Not Connected" ) );
678
679 // Now set up the grid values in the Model column.
680 for( int modelPinIndex = 0; modelPinIndex < aModel->GetPinCount(); ++modelPinIndex )
681 {
682 wxString symbolPinNumber = aModel->GetPin( modelPinIndex ).symbolPinNumber;
683
684 if( symbolPinNumber == "" )
685 continue;
686
687 int symbolPinRow = findSymbolPinRow( symbolPinNumber );
688
689 if( symbolPinRow == -1 )
690 continue;
691
692 wxString modelPinString = getModelPinString( aModel, modelPinIndex );
693 m_pinAssignmentsGrid->SetCellValue( symbolPinRow, PIN_COLUMN::MODEL, modelPinString );
694 }
695
696 // Set up the Symbol column grid values and Model column cell editors with dropdown options.
697 for( int ii = 0; ii < m_pinAssignmentsGrid->GetNumberRows(); ++ii )
698 {
699 wxString symbolPinString = getSymbolPinString( ii );
700
701 m_pinAssignmentsGrid->SetReadOnly( ii, PIN_COLUMN::SYMBOL );
702 m_pinAssignmentsGrid->SetCellValue( ii, PIN_COLUMN::SYMBOL, symbolPinString );
703
704 wxString curModelPinString = m_pinAssignmentsGrid->GetCellValue( ii, PIN_COLUMN::MODEL );
705
706 std::vector<BITMAPS> modelPinIcons;
707 wxArrayString modelPinChoices;
708
709 for( int jj = 0; jj < aModel->GetPinCount(); ++jj )
710 {
711 if( aModel->GetPin( jj ).symbolPinNumber != "" )
712 modelPinIcons.push_back( PinShapeGetBitmap( GRAPHIC_PINSHAPE::LINE ) );
713 else
714 modelPinIcons.push_back( BITMAPS::INVALID_BITMAP );
715
716 modelPinChoices.Add( getModelPinString( aModel, jj ) );
717 }
718
719 modelPinIcons.push_back( BITMAPS::INVALID_BITMAP );
720 modelPinChoices.Add( _( "Not Connected" ) );
721
722 // Using `new` here shouldn't cause a memory leak because `SetCellEditor()` calls
723 // `DecRef()` on its last editor.
724 m_pinAssignmentsGrid->SetCellEditor( ii, PIN_COLUMN::MODEL,
725 new GRID_CELL_ICON_TEXT_POPUP( modelPinIcons,
726 modelPinChoices ) );
727 }
728
729 // TODO: Show a preview of the symbol with the pin numbers shown.
730
731 if( aModel->GetType() == SIM_MODEL::TYPE::SUBCKT )
732 {
733 SIM_MODEL_SUBCKT* subckt = static_cast<SIM_MODEL_SUBCKT*>( aModel );
734 m_subckt->SetText( subckt->GetSpiceCode() );
735 m_subckt->SetEditable( false );
736 }
737 else
738 {
739 m_subcktLabel->Show( false );
740 m_subckt->Show( false );
741 }
742}
743
744
745template <typename T_symbol, typename T_field>
747{
748 for( int i = 0; i < aModel->GetPinCount(); ++i )
749 {
750 if( !m_symbol.GetPin( aModel->GetPin( i ).symbolPinNumber ) )
751 aModel->SetPinSymbolPinNumber( i, "" );
752 }
753}
754
755
756template <typename T_symbol, typename T_field>
757bool DIALOG_SIM_MODEL<T_symbol, T_field>::loadLibrary( const wxString& aLibraryPath,
758 bool aForceReload )
759{
760 if( m_prevLibrary == aLibraryPath && !aForceReload )
761 return true;
762
763 wxString msg;
764 WX_STRING_REPORTER reporter( &msg );
765
766 m_libraryModelsMgr.SetForceFullParse();
767 m_libraryModelsMgr.SetLibrary( aLibraryPath, reporter );
768
769 if( reporter.HasMessage() )
770 {
771 DisplayErrorMessage( this, msg );
772 return false;
773 }
774
775 std::string modelName = SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY::NAME_FIELD );
776
777 for( const auto& [baseModelName, baseModel] : library()->GetModels() )
778 {
779 if( baseModelName == modelName )
780 m_libraryModelsMgr.CreateModel( &baseModel, m_sortedPartPins, m_fields, reporter );
781 else
782 m_libraryModelsMgr.CreateModel( &baseModel, m_sortedPartPins, reporter );
783 }
784
785 if( reporter.HasMessage() )
786 DisplayErrorMessage( this, msg );
787
788 m_rbLibraryModel->SetValue( true );
789 m_libraryPathText->ChangeValue( aLibraryPath );
790
791 wxArrayString modelNames;
792
793 for( const auto& [name, model] : library()->GetModels() )
794 modelNames.Add( name );
795
796 m_modelNameChoice->Clear();
797 m_modelNameChoice->Append( modelNames );
798
799 if( isIbisLoaded() )
800 {
801 wxArrayString emptyArray;
802 m_pinModelCombobox->Set( emptyArray );
803 m_pinCombobox->Set( emptyArray );
804 m_pinModelCombobox->SetSelection( -1 );
805 m_pinCombobox->SetSelection( -1 );
806 }
807
808 m_prevLibrary = aLibraryPath;
809 return true;
810}
811
812
813template <typename T_symbol, typename T_field>
815 int aParamIndex )
816{
817 if( aModel->GetParam( aParamIndex ).info.dir == SIM_MODEL::PARAM::DIR_OUT )
818 return;
819
820 switch( aModel->GetParam( aParamIndex ).info.category )
821 {
822 case CATEGORY::AC:
823 m_paramGrid->HideProperty( "AC", false );
824 m_paramGrid->AppendIn( "AC", newParamProperty( aModel, aParamIndex ) );
825 break;
826
827 case CATEGORY::DC:
828 m_paramGrid->HideProperty( "DC", false );
829 m_paramGrid->AppendIn( "DC", newParamProperty( aModel, aParamIndex ) );
830 break;
831
832 case CATEGORY::S_PARAM:
833 m_paramGrid->HideProperty( "S-Parameters", false );
834 m_paramGrid->AppendIn( "S-Parameters", newParamProperty( aModel, aParamIndex ) );
835 break;
836
837 case CATEGORY::CAPACITANCE:
838 m_paramGrid->HideProperty( "Capacitance", false );
839 m_paramGrid->AppendIn( "Capacitance", newParamProperty( aModel, aParamIndex ) );
840 break;
841
842 case CATEGORY::TEMPERATURE:
843 m_paramGrid->HideProperty( "Temperature", false );
844 m_paramGrid->AppendIn( "Temperature", newParamProperty( aModel, aParamIndex ) );
845 break;
846
847 case CATEGORY::NOISE:
848 m_paramGrid->HideProperty( "Noise", false );
849 m_paramGrid->AppendIn( "Noise", newParamProperty( aModel, aParamIndex ) );
850 break;
851
852 case CATEGORY::DISTRIBUTED_QUANTITIES:
853 m_paramGrid->HideProperty( "Distributed Quantities", false );
854 m_paramGrid->AppendIn( "Distributed Quantities", newParamProperty( aModel, aParamIndex ) );
855 break;
856
857 case CATEGORY::WAVEFORM:
858 m_paramGrid->HideProperty( "Waveform", false );
859 m_paramGrid->AppendIn( "Waveform", newParamProperty( aModel, aParamIndex ) );
860 break;
861
862 case CATEGORY::GEOMETRY:
863 m_paramGrid->HideProperty( "Geometry", false );
864 m_paramGrid->AppendIn( "Geometry", newParamProperty( aModel, aParamIndex ) );
865 break;
866
867 case CATEGORY::LIMITING_VALUES:
868 m_paramGrid->HideProperty( "Limiting Values", false );
869 m_paramGrid->AppendIn( "Limiting Values", newParamProperty( aModel, aParamIndex ) );
870 break;
871
872 case CATEGORY::ADVANCED:
873 m_paramGrid->HideProperty( "Advanced", false );
874 m_paramGrid->AppendIn( "Advanced", newParamProperty( aModel, aParamIndex ) );
875 break;
876
877 case CATEGORY::FLAGS:
878 m_paramGrid->HideProperty( "Flags", false );
879 m_paramGrid->AppendIn( "Flags", newParamProperty( aModel, aParamIndex ) );
880 break;
881
882 default:
883 m_paramGrid->Insert( m_firstCategory, newParamProperty( aModel, aParamIndex ) );
884 break;
885
886 case CATEGORY::INITIAL_CONDITIONS:
887 case CATEGORY::SUPERFLUOUS:
888 return;
889 }
890}
891
892
893template <typename T_symbol, typename T_field>
895 int aParamIndex ) const
896{
897 const SIM_MODEL::PARAM& param = aModel->GetParam( aParamIndex );
898 wxString paramDescription;
899
900 if( param.info.description == "" )
901 paramDescription = wxString::Format( "%s", param.info.name );
902 else
903 paramDescription = wxString::Format( "%s (%s)", param.info.description, param.info.name );
904
905 wxPGProperty* prop = nullptr;
906
907 switch( param.info.type )
908 {
910 // TODO.
911 prop = new SIM_BOOL_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex );
912 prop->SetAttribute( wxPG_BOOL_USE_CHECKBOX, true );
913 break;
914
916 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
918 break;
919
921 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
923 break;
924
925 //case TYPE_COMPLEX:
926 // break;
927
929 if( param.info.enumValues.empty() )
930 {
931 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel,
932 aParamIndex, SIM_VALUE::TYPE_STRING );
933 }
934 else
935 {
936 prop = new SIM_ENUM_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex );
937 }
938 break;
939
940 default:
941 prop = new wxStringProperty( paramDescription, param.info.name );
942 break;
943 }
944
945 prop->SetAttribute( wxPG_ATTR_UNITS, wxString::FromUTF8( param.info.unit.c_str() ) );
946
947 // Legacy due to the way we extracted the parameters from Ngspice.
948 prop->SetCell( 3, wxString::FromUTF8( param.info.defaultValue ) );
949
950 wxString typeStr;
951
952 switch( param.info.type )
953 {
954 case SIM_VALUE::TYPE_BOOL: typeStr = wxT( "Bool" ); break;
955 case SIM_VALUE::TYPE_INT: typeStr = wxT( "Int" ); break;
956 case SIM_VALUE::TYPE_FLOAT: typeStr = wxT( "Float" ); break;
957 case SIM_VALUE::TYPE_COMPLEX: typeStr = wxT( "Complex" ); break;
958 case SIM_VALUE::TYPE_STRING: typeStr = wxT( "String" ); break;
959 case SIM_VALUE::TYPE_BOOL_VECTOR: typeStr = wxT( "Bool Vector" ); break;
960 case SIM_VALUE::TYPE_INT_VECTOR: typeStr = wxT( "Int Vector" ); break;
961 case SIM_VALUE::TYPE_FLOAT_VECTOR: typeStr = wxT( "Float Vector" ); break;
962 case SIM_VALUE::TYPE_COMPLEX_VECTOR: typeStr = wxT( "Complex Vector" ); break;
963 }
964
965 prop->SetCell( PARAM_COLUMN::TYPE, typeStr );
966
967 return prop;
968}
969
970
971template <typename T_symbol, typename T_field>
972int DIALOG_SIM_MODEL<T_symbol, T_field>::findSymbolPinRow( const wxString& aSymbolPinNumber ) const
973{
974 for( int row = 0; row < static_cast<int>( m_sortedPartPins.size() ); ++row )
975 {
976 LIB_PIN* pin = m_sortedPartPins[row];
977
978 if( pin->GetNumber() == aSymbolPinNumber )
979 return row;
980 }
981
982 return -1;
983}
984
985
986template <typename T_symbol, typename T_field>
988{
989 if( m_rbLibraryModel->GetValue() )
990 {
991 int sel = m_modelNameChoice->GetSelection();
992
993 if( sel >= 0 && sel < (int) m_libraryModelsMgr.GetModels().size() )
994 return m_libraryModelsMgr.GetModels().at( sel ).get();
995 }
996 else
997 {
998 if( (int) m_curModelType < (int) m_builtinModelsMgr.GetModels().size() )
999 return m_builtinModelsMgr.GetModels().at( (int) m_curModelType );
1000 }
1001
1002 return m_builtinModelsMgr.GetModels().at( (int) SIM_MODEL::TYPE::NONE );
1003}
1004
1005
1006template <typename T_symbol, typename T_field>
1008{
1009 if( m_libraryModelsMgr.GetLibraries().size() == 1 )
1010 return &m_libraryModelsMgr.GetLibraries().begin()->second.get();
1011
1012 return nullptr;
1013}
1014
1015
1016template <typename T_symbol, typename T_field>
1018{
1019 LIB_PIN* pin = m_sortedPartPins.at( symbolPinIndex );
1020 wxString pinNumber;
1021 wxString pinName;
1022
1023 if( pin )
1024 {
1025 pinNumber = pin->GetShownNumber();
1026 pinName = pin->GetShownName();
1027 }
1028
1029 if( !pinName.IsEmpty() && pinName != pinNumber )
1030 pinNumber += wxString::Format( wxT( " (%s)" ), pinName );
1031
1032 return pinNumber;
1033}
1034
1035
1036template <typename T_symbol, typename T_field>
1038 int aModelPinIndex ) const
1039{
1040 const wxString& pinName = aModel->GetPin( aModelPinIndex ).name;
1041
1042 LOCALE_IO toggle;
1043
1044 wxString pinNumber = wxString::Format( "%d", aModelPinIndex + 1 );
1045
1046 if( !pinName.IsEmpty() && pinName != pinNumber )
1047 pinNumber += wxString::Format( wxT( " (%s)" ), pinName );
1048
1049 return pinNumber;
1050}
1051
1052
1053template <typename T_symbol, typename T_field>
1054int DIALOG_SIM_MODEL<T_symbol, T_field>::getModelPinIndex( const wxString& aModelPinString ) const
1055{
1056 if( aModelPinString == "Not Connected" )
1058
1059 int length = aModelPinString.Find( " " );
1060
1061 if( length == wxNOT_FOUND )
1062 length = static_cast<int>( aModelPinString.Length() );
1063
1064 long result = 0;
1065 aModelPinString.Mid( 0, length ).ToCLong( &result );
1066
1067 return static_cast<int>( result - 1 );
1068}
1069
1070
1071template <typename T_symbol, typename T_field>
1073{
1074 bool fromLibrary = m_rbLibraryModel->GetValue();
1075
1076 m_pathLabel->Enable( fromLibrary );
1077 m_libraryPathText->Enable( fromLibrary );
1078 m_browseButton->Enable( fromLibrary );
1079 m_modelNameLabel->Enable( fromLibrary );
1080 m_modelNameChoice->Enable( fromLibrary );
1081 m_pinLabel->Enable( fromLibrary );
1082 m_pinCombobox->Enable( fromLibrary );
1083 m_differentialCheckbox->Enable( fromLibrary );
1084 m_pinModelLabel->Enable( fromLibrary );
1085 m_pinModelCombobox->Enable( fromLibrary );
1086 m_waveformLabel->Enable( fromLibrary );
1087 m_waveformChoice->Enable( fromLibrary );
1088
1089 m_deviceLabel->Enable( !fromLibrary );
1090 m_deviceChoice->Enable( !fromLibrary );
1091 m_deviceTypeLabel->Enable( !fromLibrary );
1092
1093 m_prevModel = nullptr; // Ensure the Model panel will be rebuild after updating other params.
1094 updateWidgets();
1095}
1096
1097
1098template <typename T_symbol, typename T_field>
1100{
1101 if( m_rbLibraryModel->GetValue() )
1102 {
1103 wxString path = m_libraryPathText->GetValue();
1104
1105 if( !path.IsEmpty() )
1106 {
1107 try
1108 {
1109 loadLibrary( path );
1110 updateWidgets();
1111 }
1112 catch( const IO_ERROR& )
1113 {
1114 // TODO: Add an infobar to report the error?
1115 }
1116 }
1117 }
1118}
1119
1120
1121template <typename T_symbol, typename T_field>
1123{
1124 if( !m_inKillFocus )
1125 {
1126 m_inKillFocus = true;
1127
1128 wxCommandEvent dummy;
1129 onLibraryPathTextEnter( dummy );
1130
1131 m_inKillFocus = false;
1132 }
1133}
1134
1135
1136template <typename T_symbol, typename T_field>
1138{
1139 static wxString s_mruPath;
1140
1141 wxString path = s_mruPath.IsEmpty() ? Prj().GetProjectPath() : s_mruPath;
1142 wxFileDialog dlg( this, _( "Browse Models" ), path );
1143
1144 if( dlg.ShowModal() == wxID_CANCEL )
1145 return;
1146
1147 path = dlg.GetPath();
1148 wxFileName fn( path );
1149
1150 s_mruPath = fn.GetPath();
1151
1152 if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( wxS( ".." ) ) )
1153 path = fn.GetFullPath();
1154
1155 loadLibrary( path, true );
1156 updateWidgets();
1157}
1158
1159
1160template <typename T_symbol, typename T_field>
1162{
1163 if( isIbisLoaded() )
1164 {
1165 wxArrayString pinLabels;
1166 SIM_MODEL_KIBIS* modelkibis = dynamic_cast<SIM_MODEL_KIBIS*>( &curModel() );
1167
1168 wxCHECK2( modelkibis, return );
1169
1170 for( std::pair<wxString, wxString> strs : modelkibis->GetIbisPins() )
1171 pinLabels.Add( strs.first + wxT( " - " ) + strs.second );
1172
1173 m_pinCombobox->Set( pinLabels );
1174
1175 wxArrayString emptyArray;
1176 m_pinModelCombobox->Set( emptyArray );
1177 }
1178
1179 updateWidgets();
1180}
1181
1182
1183template <typename T_symbol, typename T_field>
1185{
1186 wxArrayString modelLabels;
1187
1188 SIM_MODEL_KIBIS& ibisModel = static_cast<SIM_MODEL_KIBIS&>( curModel() );
1189
1190 std::vector<std::pair<std::string, std::string>> strs = ibisModel.GetIbisPins();
1191 std::string pinNumber = strs.at( m_pinCombobox->GetSelection() ).first;
1192
1193 const SIM_LIBRARY_KIBIS* ibisLibrary = dynamic_cast<const SIM_LIBRARY_KIBIS*>( library() );
1194
1195 ibisModel.ChangePin( *ibisLibrary, pinNumber );
1196
1197 ibisModel.m_enableDiff = ibisLibrary->isPinDiff( ibisModel.GetComponentName(), pinNumber );
1198
1199 for( wxString modelName : ibisModel.GetIbisModels() )
1200 modelLabels.Add( modelName );
1201
1202 m_pinModelCombobox->Set( modelLabels );
1203
1204 if( m_pinModelCombobox->GetCount() == 1 )
1205 m_pinModelCombobox->SetSelection( 0 );
1206 else
1207 m_pinModelCombobox->SetSelection( -1 );
1208
1209 updateWidgets();
1210}
1211
1212
1213template <typename T_symbol, typename T_field>
1215{
1216 m_pinCombobox->SetSelection( m_pinCombobox->FindString( m_pinCombobox->GetValue() ) );
1217
1218 onPinModelCombobox( aEvent );
1219}
1220
1221
1222template <typename T_symbol, typename T_field>
1224{
1225 updateWidgets();
1226}
1227
1228
1229template <typename T_symbol, typename T_field>
1231{
1232 m_pinModelCombobox->SetSelection( m_pinModelCombobox->FindString( m_pinModelCombobox->GetValue() ) );
1233}
1234
1235template <typename T_symbol, typename T_field>
1237{
1238 SIM_MODEL_KIBIS* modelkibis = dynamic_cast<SIM_MODEL_KIBIS*>( &curModel() );
1239
1240 wxCHECK( modelkibis, /* void */ );
1241
1242 bool diff = m_differentialCheckbox->GetValue() && modelkibis->CanDifferential();
1243 modelkibis->SwitchSingleEndedDiff( diff );
1244
1245 updateWidgets();
1246}
1247
1248
1249template <typename T_symbol, typename T_field>
1251{
1252 for( SIM_MODEL::DEVICE_T deviceType : SIM_MODEL::DEVICE_T_ITERATOR() )
1253 {
1254 if( SIM_MODEL::DeviceInfo( deviceType ).description == m_deviceChoice->GetStringSelection() )
1255 {
1256 m_curModelType = m_curModelTypeOfDeviceType.at( deviceType );
1257 break;
1258 }
1259 }
1260
1261 updateWidgets();
1262}
1263
1264
1265template <typename T_symbol, typename T_field>
1267{
1268 SIM_MODEL::DEVICE_T deviceType = curModel().GetDeviceType();
1269 wxString typeDescription = m_waveformChoice->GetStringSelection();
1270
1271 for( SIM_MODEL::TYPE type : { SIM_MODEL::TYPE::KIBIS_DEVICE,
1272 SIM_MODEL::TYPE::KIBIS_DRIVER_DC,
1273 SIM_MODEL::TYPE::KIBIS_DRIVER_RECT,
1274 SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS } )
1275 {
1276 if( equivalent( deviceType, SIM_MODEL::TypeInfo( type ).deviceType )
1277 && typeDescription == SIM_MODEL::TypeInfo( type ).description )
1278 {
1279 int idx = m_modelNameChoice->GetSelection();
1280
1281 auto& baseModel = static_cast<SIM_MODEL_KIBIS&>( m_libraryModelsMgr.GetModels()[idx].get() );
1282
1283 m_libraryModelsMgr.SetModel( idx, std::make_unique<SIM_MODEL_KIBIS>( type, baseModel ) );
1284
1285 try
1286 {
1287 m_libraryModelsMgr.GetModels()[idx].get().ReadDataFields( &m_fields, m_sortedPartPins );
1288 }
1289 catch( IO_ERROR& err )
1290 {
1291 DisplayErrorMessage( this, err.What() );
1292 }
1293
1294 m_curModelType = type;
1295 break;
1296 }
1297 }
1298
1299 m_curModelTypeOfDeviceType.at( deviceType ) = m_curModelType;
1300 updateWidgets();
1301}
1302
1303
1304template <typename T_symbol, typename T_field>
1306{
1307 SIM_MODEL::DEVICE_T deviceType = curModel().GetDeviceType();
1308 wxString typeDescription = m_deviceTypeChoice->GetStringSelection();
1309
1310 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1311 {
1312 if( equivalent( deviceType, SIM_MODEL::TypeInfo( type ).deviceType )
1313 && typeDescription == SIM_MODEL::TypeInfo( type ).description )
1314 {
1315 m_curModelType = type;
1316 break;
1317 }
1318 }
1319
1320 m_curModelTypeOfDeviceType.at( deviceType ) = m_curModelType;
1321 updateWidgets();
1322}
1323
1324
1325template <typename T_symbol, typename T_field>
1327{
1328 updateModelCodeTab( &curModel() );
1329}
1330
1331
1332template <typename T_symbol, typename T_field>
1334{
1335 int symbolPinIndex = aEvent.GetRow();
1336 wxString oldModelPinName = aEvent.GetString();
1337 wxString modelPinName = m_pinAssignmentsGrid->GetCellValue( aEvent.GetRow(), aEvent.GetCol() );
1338
1339 int oldModelPinIndex = getModelPinIndex( oldModelPinName );
1340 int modelPinIndex = getModelPinIndex( modelPinName );
1341
1342 if( oldModelPinIndex != SIM_MODEL::PIN::NOT_CONNECTED )
1343 curModel().SetPinSymbolPinNumber( oldModelPinIndex, "" );
1344
1345 if( modelPinIndex != SIM_MODEL::PIN::NOT_CONNECTED )
1346 {
1347 curModel().SetPinSymbolPinNumber( modelPinIndex,
1348 std::string( m_sortedPartPins.at( symbolPinIndex )->GetShownNumber().ToUTF8() ) );
1349 }
1350
1351 updatePinAssignments( &curModel() );
1352
1353 aEvent.Skip();
1354}
1355
1356
1357template <typename T_symbol, typename T_field>
1359{
1360 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_pinAssignmentsGrid );
1361
1362 int gridWidth = KIPLATFORM::UI::GetUnobscuredSize( m_pinAssignmentsGrid ).x;
1363 m_pinAssignmentsGrid->SetColSize( PIN_COLUMN::MODEL, gridWidth / 2 );
1364 m_pinAssignmentsGrid->SetColSize( PIN_COLUMN::SYMBOL, gridWidth / 2 );
1365
1366 aEvent.Skip();
1367}
1368
1369
1370template <typename T_symbol, typename T_field>
1372{
1373 // By default, when a property grid is focused, the textbox is not immediately focused until
1374 // Tab key is pressed. This is inconvenient, so we fix that here.
1375
1376 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1377 wxPGProperty* selected = grid->GetSelection();
1378
1379 if( !selected )
1380 selected = grid->wxPropertyGridInterface::GetFirst();
1381
1382#if wxCHECK_VERSION( 3, 3, 0 )
1383 if( selected )
1384 grid->DoSelectProperty( selected, wxPGSelectPropertyFlags::Focus );
1385#else
1386 if( selected )
1387 grid->DoSelectProperty( selected, wxPG_SEL_FOCUS );
1388#endif
1389
1390 aEvent.Skip();
1391}
1392
1393
1394template <typename T_symbol, typename T_field>
1396{
1397 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1398
1399 // Jump over categories.
1400 if( grid->GetSelection() && grid->GetSelection()->IsCategory() )
1401 {
1402 wxPGProperty* selection = grid->GetSelection();
1403
1404 // If the new selection is immediately above the previous selection, we jump up. Otherwise
1405 // we jump down. We do this by simulating up or down arrow keys.
1406
1407 wxPropertyGridIterator it = grid->GetIterator( wxPG_ITERATE_VISIBLE, selection );
1408 it.Next();
1409
1410 wxKeyEvent* keyEvent = new wxKeyEvent( wxEVT_KEY_DOWN );
1411
1412 if( *it == m_prevParamGridSelection )
1413 {
1414 if( !selection->IsExpanded() )
1415 {
1416 grid->Expand( selection );
1417 keyEvent->m_keyCode = WXK_DOWN;
1418 wxQueueEvent( grid, keyEvent );
1419
1420 // Does not work for some reason.
1421 /*m_paramGrid->DoSelectProperty( selection->Item( selection->GetChildCount() - 1 ),
1422 wxPG_SEL_FOCUS );*/
1423 }
1424 else
1425 {
1426 keyEvent->m_keyCode = WXK_UP;
1427 wxQueueEvent( grid, keyEvent );
1428 }
1429 }
1430 else
1431 {
1432 if( !selection->IsExpanded() )
1433 grid->Expand( selection );
1434
1435 keyEvent->m_keyCode = WXK_DOWN;
1436 wxQueueEvent( grid, keyEvent );
1437 }
1438
1439 m_prevParamGridSelection = grid->GetSelection();
1440 return;
1441 }
1442
1443 wxWindow* editorControl = grid->GetEditorControl();
1444
1445 if( !editorControl )
1446 {
1447 m_prevParamGridSelection = grid->GetSelection();
1448 return;
1449 }
1450
1451 // Without this the user had to press tab before they could edit the field.
1452 editorControl->SetFocus();
1453 m_prevParamGridSelection = grid->GetSelection();
1454}
1455
1456
1457template <typename T_symbol, typename T_field>
1459{
1460 // This is currently patched in wxPropertyGrid::ScrollWindow() in the Mac wxWidgets fork.
1461 // However, we may need this version if it turns out to be an issue on other platforms and
1462 // we can't get it upstreamed.
1463#if 0
1464 // It's a shame to do this on the UpdateUI event, but neither the wxPropertyGridManager,
1465 // wxPropertyGridPage, wxPropertyGrid, nor the wxPropertyGrid's GetCanvas() window appear
1466 // to get scroll events.
1467
1468 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1469 wxTextCtrl* ctrl = grid->GetEditorTextCtrl();
1470
1471 if( ctrl )
1472 {
1473 wxRect ctrlRect = ctrl->GetScreenRect();
1474 wxRect gridRect = grid->GetScreenRect();
1475
1476 if( ctrlRect.GetTop() < gridRect.GetTop() || ctrlRect.GetBottom() > gridRect.GetBottom() )
1477 grid->ClearSelection();
1478 }
1479#endif
1480}
1481
1482
1483template <typename T_symbol, typename T_field>
1485{
1486 wxPropertyGrid* grid = m_paramGridMgr->GetGrid();
1487 int margin = 15;
1488 int indent = 20;
1489
1490 if( aWidth != m_lastParamGridWidth || aForce )
1491 {
1492 m_lastParamGridWidth = aWidth;
1493
1494 grid->FitColumns();
1495
1496 std::vector<int> colWidths;
1497
1498 for( size_t ii = 0; ii < grid->GetColumnCount(); ii++ )
1499 {
1500 if( ii == PARAM_COLUMN::DESCRIPTION )
1501 colWidths.push_back( grid->GetState()->GetColumnWidth( ii ) + margin + indent );
1502 else if( ii == PARAM_COLUMN::VALUE )
1503 colWidths.push_back( std::max( 72, grid->GetState()->GetColumnWidth( ii ) ) + margin );
1504 else
1505 colWidths.push_back( 60 + margin );
1506
1507 aWidth -= colWidths[ ii ];
1508 }
1509
1510 for( size_t ii = 0; ii < grid->GetColumnCount(); ii++ )
1511 grid->SetColumnProportion( ii, colWidths[ ii ] );
1512
1513 grid->ResetColumnSizes();
1514 grid->RefreshEditor();
1515 }
1516}
1517
1518
1519template <typename T_symbol, typename T_field>
1521{
1522 adjustParamGridColumns( event.GetSize().GetX(), false );
1523
1524 event.Skip();
1525}
1526
1527
1528
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
@ INVALID_BITMAP
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
Class DIALOG_SIM_MODEL_BASE.
wxPropertyGridManager * m_paramGridMgr
STD_BITMAP_BUTTON * m_browseButton
wxStyledTextCtrl * m_subckt
wxPropertyGridPage * m_paramGrid
wxStyledTextCtrl * m_codePreview
void onTypeChoice(wxCommandEvent &aEvent) override
void onRadioButton(wxCommandEvent &aEvent) override
void updateBuiltinModelWidgets(SIM_MODEL *aModel)
std::vector< LIB_PIN * > m_sortedPartPins
DIALOG_SIM_MODEL(wxWindow *aParent, T_symbol &aSymbol, std::vector< T_field > &aFields)
bool loadLibrary(const wxString &aLibraryPath, bool aForceReload=false)
wxString getModelPinString(SIM_MODEL *aModel, int aModelPinIndex) const
void removeOrphanedPinAssignments(SIM_MODEL *aModel)
void onWaveformChoice(wxCommandEvent &aEvent) override
void onPinAssignmentsGridCellChange(wxGridEvent &aEvent) override
void onPageChanging(wxNotebookEvent &event) override
void onParamGridSelectionChange(wxPropertyGridEvent &aEvent)
void onUpdateUI(wxUpdateUIEvent &aEvent)
int findSymbolPinRow(const wxString &aSymbolPinNumber) const
void adjustParamGridColumns(int aWidth, bool aForce)
void updateIbisWidgets(SIM_MODEL *aModel)
void onDeviceTypeChoice(wxCommandEvent &aEvent) override
SCINTILLA_TRICKS * m_scintillaTricksSubckt
const SIM_LIBRARY * library() const
SIM_MODEL & curModel() const
int getModelPinIndex(const wxString &aModelPinString) const
void onSizeParamGrid(wxSizeEvent &event) override
void updateModelCodeTab(SIM_MODEL *aModel)
bool TransferDataToWindow() override
void onLibraryPathTextEnter(wxCommandEvent &aEvent) override
void onBrowseButtonClick(wxCommandEvent &aEvent) override
void addParamPropertyIfRelevant(SIM_MODEL *aModel, int aParamIndex)
wxString getSymbolPinString(int aSymbolPinNumber) const
void onPinCombobox(wxCommandEvent &event) override
wxPGProperty * newParamProperty(SIM_MODEL *aModel, int aParamIndex) const
void onPinComboboxTextEnter(wxCommandEvent &event) override
bool TransferDataFromWindow() override
void onDifferentialCheckbox(wxCommandEvent &event) override
void onPinModelComboboxTextEnter(wxCommandEvent &event) override
void onLibraryPathTextKillFocus(wxFocusEvent &aEvent) override
void onModelNameChoice(wxCommandEvent &aEvent) override
void updateModelParamsTab(SIM_MODEL *aModel)
SCINTILLA_TRICKS * m_scintillaTricksCode
void onParamGridSetFocus(wxFocusEvent &aEvent)
void onPinAssignmentsGridSize(wxSizeEvent &aEvent) override
void onPinModelCombobox(wxCommandEvent &event) override
void updatePinAssignments(SIM_MODEL *aModel)
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:61
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:143
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
bool isPinDiff(const std::string &aComp, const std::string &aPinNumber) const
static constexpr auto DIFF_FIELD
static constexpr auto MODEL_FIELD
static constexpr auto PIN_FIELD
static constexpr auto LIBRARY_FIELD
Definition: sim_library.h:35
static constexpr auto NAME_FIELD
Definition: sim_library.h:36
bool ChangePin(const SIM_LIBRARY_KIBIS &aLib, std::string aPinNumber)
update the list of available models based on the pin number.
std::vector< std::string > GetIbisModels() const
std::string GetComponentName() const
bool CanDifferential() const
void SwitchSingleEndedDiff(bool aDiff) override
std::vector< std::pair< std::string, std::string > > GetIbisPins() const
std::string GetSpiceCode() const
static bool InferSimModel(T_symbol &aSymbol, std::vector< T_field > *aFields, bool aResolve, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
Definition: sim_model.cpp:1140
static INFO TypeInfo(TYPE aType)
Definition: sim_model.cpp:97
int GetPinCount() const
Definition: sim_model.h:476
void ReadDataFields(const std::vector< T > *aFields, const std::vector< LIB_PIN * > &aPins)
static void SetFieldValue(std::vector< T > &aFields, const wxString &aFieldName, const std::string &aValue)
Definition: sim_model.cpp:652
const SPICE_GENERATOR & SpiceGenerator() const
Definition: sim_model.h:439
static std::string GetFieldValue(const std::vector< T > *aFields, const wxString &aFieldName, bool aResolve=true)
Definition: sim_model.cpp:621
virtual const PARAM & GetParam(unsigned aParamIndex) const
Definition: sim_model.cpp:785
static TYPE ReadTypeFromFields(const std::vector< T > &aFields, REPORTER &aReporter)
Definition: sim_model.cpp:381
int GetParamCount() const
Definition: sim_model.h:486
void SetPinSymbolPinNumber(int aPinIndex, const std::string &aSymbolPinNumber)
Definition: sim_model.cpp:755
DEVICE_INFO GetDeviceInfo() const
Definition: sim_model.h:465
DEVICE_T GetDeviceType() const
Definition: sim_model.h:468
static DEVICE_INFO DeviceInfo(DEVICE_T aDeviceType)
Definition: sim_model.cpp:56
const PIN & GetPin(unsigned aIndex) const
Definition: sim_model.h:477
virtual bool HasAutofill() const
Definition: sim_model.h:505
virtual bool HasPrimaryValue() const
Definition: sim_model.h:506
TYPE GetType() const
Definition: sim_model.h:469
const SIM_MODEL::PARAM & GetParam() const
Definition: sim_property.h:60
@ TYPE_BOOL
Definition: sim_value.h:67
@ TYPE_FLOAT_VECTOR
Definition: sim_value.h:75
@ TYPE_BOOL_VECTOR
Definition: sim_value.h:73
@ TYPE_INT
Definition: sim_value.h:68
@ TYPE_FLOAT
Definition: sim_value.h:69
@ TYPE_INT_VECTOR
Definition: sim_value.h:74
@ TYPE_COMPLEX_VECTOR
Definition: sim_value.h:76
@ TYPE_STRING
Definition: sim_value.h:71
@ TYPE_COMPLEX
Definition: sim_value.h:70
virtual std::string Preview(const SPICE_ITEM &aItem) const
void SetBitmap(const wxBitmapBundle &aBmp)
A wrapper for reporting to a wxString object.
Definition: reporter.h:164
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:71
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:305
This file is part of the common library.
static bool empty(const wxTextEntryBase *aCtrl)
bool equivalent(SIM_MODEL::DEVICE_T a, SIM_MODEL::DEVICE_T b)
#define _(s)
@ NONE
Definition: kibis.h:54
PROJECT & Prj()
Definition: kicad.cpp:576
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: gtk/ui.cpp:195
wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:151
BITMAPS PinShapeGetBitmap(GRAPHIC_PINSHAPE aShape)
Definition: pin_type.cpp:255
SIM_MODEL::TYPE TYPE
Definition: sim_model.cpp:53
#define SIM_PINS_FIELD
Definition: sim_model.h:54
#define SIM_DEVICE_TYPE_FIELD
Definition: sim_model.h:52
#define SIM_TYPE_FIELD
Definition: sim_model.h:53
#define SIM_REFERENCE_FIELD
Definition: sim_model.h:49
#define SIM_PARAMS_FIELD
Definition: sim_model.h:55
std::vector< FAB_LAYER_COLOR > dummy
std::vector< std::string > enumValues
Definition: sim_model.h:388
SIM_VALUE::TYPE type
Definition: sim_model.h:379
std::string defaultValue
Definition: sim_model.h:382
std::string description
Definition: sim_model.h:383
std::string value
Definition: sim_model.h:400
const INFO & info
Definition: sim_model.h:401
std::string symbolPinNumber
Definition: sim_model.h:308
const std::string name
Definition: sim_model.h:307
static constexpr auto NOT_CONNECTED
Definition: sim_model.h:310
std::string modelName
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".