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