KiCad PCB EDA Suite
panel_board_stackup.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) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <macros.h> // arrayDim definition
26#include <pcb_edit_frame.h>
27#include <board.h>
32#include <wx/log.h>
33#include <wx/rawbmp.h>
34#include <math/util.h> // for KiROUND
35
36#include "panel_board_stackup.h"
37#include <panel_setup_layers.h>
39#include <bitmaps.h>
40#include <wx/clipbrd.h>
41#include <wx/dataobj.h>
43#include <wx/wupdlock.h>
44#include <wx/richmsgdlg.h>
45#include <wx/dcclient.h>
46#include <wx/treebook.h>
47#include <wx/textdlg.h>
48
49#include <locale_io.h>
50#include <eda_list_dialog.h>
51#include <string_utils.h> // for UIDouble2Str()
52
53
54// Some wx widget ID to know what widget has fired a event:
55#define ID_INCREMENT 256 // space between 2 ID type. Bigger than the layer count max
56
57// The actual widget IDs are the base id + the row index.
58// they are used in events to know the row index of the control that fired the event
60{
61 ID_ITEM_MATERIAL = 10000, // Be sure it is higher than other IDs
62 // used in the board setup dialog
66};
67
68// Default colors to draw icons:
69static wxColor copperColor( 220, 180, 30 );
70static wxColor dielectricColor( 75, 120, 75 );
71static wxColor pasteColor( 200, 200, 200 );
72
73static void drawBitmap( wxBitmap& aBitmap, wxColor aColor );
74
75
77 PANEL_SETUP_LAYERS* aPanelLayers ):
78 PANEL_SETUP_BOARD_STACKUP_BASE( aParent->GetTreebook() ),
79 m_delectricMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_DIELECTRIC ),
80 m_solderMaskMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_SOLDERMASK ),
81 m_silkscreenMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_SILKSCREEN )
82{
83 m_parentDialog = aParent;
84 m_frame = aFrame;
85 m_panelLayers = aPanelLayers;
88
90
92
93 // Calculates a good size for color swatches (icons) in this dialog
94 wxClientDC dc( this );
95 m_colorSwatchesSize = dc.GetTextExtent( wxT( "XX" ) );
96 m_colorIconsSize = dc.GetTextExtent( wxT( "XXXX" ) );
97
98 // Calculates a good size for wxTextCtrl to enter Epsilon R and Loss tan
99 // ("0.0000000" + margins)
100 m_numericFieldsSize = dc.GetTextExtent( wxT( "X.XXXXXXX" ) );
101 m_numericFieldsSize.y = -1; // Use default for the vertical size
102
103 // Calculates a minimal size for wxTextCtrl to enter a dim with units
104 // ("000.0000000 mils" + margins)
105 m_numericTextCtrlSize = dc.GetTextExtent( wxT( "XXX.XXXXXXX mils" ) );
106 m_numericTextCtrlSize.y = -1; // Use default for the vertical size
107
108 // The grid column containing the lock checkbox is kept to a minimal
109 // size. So we use a wxStaticBitmap: set the bitmap itself
111
112 // Gives a minimal size of wxTextCtrl showing dimensions+units
113 m_tcCTValue->SetMinSize( m_numericTextCtrlSize );
114
115 // Prepare dielectric layer type: layer type keyword is "core" or "prepreg"
116 m_core_prepreg_choice.Add( _( "Core" ) );
117 m_core_prepreg_choice.Add( _( "PrePreg" ) );
118
119 buildLayerStackPanel( true );
120 synchronizeWithBoard( true );
122}
123
124
126{
128}
129
130
132{
133 int oldBoardWidth = static_cast<int>( m_frame->ValueFromString( m_tcCTValue->GetValue() ) );
136 setDefaultLayerWidths( oldBoardWidth );
138 Layout();
139}
140
141
143{
144 // The list of items that can be modified:
145 std::vector< BOARD_STACKUP_ROW_UI_ITEM* > items_candidate;
146
147 // Some dielectric layers can have a locked thickness, so calculate the min
148 // acceptable thickness
149 int min_thickness = 0;
150
152 {
153 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
154
155 if( !item->IsThicknessEditable() || !ui_item.m_isEnabled )
156 continue;
157
158 // We are looking for locked thickness items only:
159 wxCheckBox* cb_box = dynamic_cast<wxCheckBox*> ( ui_item.m_ThicknessLockCtrl );
160
161 if( cb_box && !cb_box->GetValue() )
162 {
163 items_candidate.push_back( &ui_item );
164 continue;
165 }
166
167 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
168
169 int item_thickness = m_frame->ValueFromString( textCtrl->GetValue() );
170 min_thickness += item_thickness;
171 }
172
173 wxString title;
174
175 if( min_thickness == 0 )
176 {
177 title.Printf( _( "Enter board thickness in %s:" ),
178 EDA_UNIT_UTILS::GetText( m_frame->GetUserUnits() ).Trim( false ) );
179 }
180 else
181 {
182 title.Printf( _( "Enter expected board thickness (min value %s):" ),
183 m_frame->StringFromValue( min_thickness, true ) );
184 }
185
186 wxTextEntryDialog dlg( this, title, _( "Adjust Unlocked Dielectric Layers" ) );
187
188 if( dlg.ShowModal() != wxID_OK )
189 return;
190
191 int iu_thickness = m_frame->ValueFromString( dlg.GetValue() );
192
193 if( iu_thickness < min_thickness )
194 {
195 wxMessageBox( wxString::Format( _("Value too small (min value %s)." ),
196 m_frame->StringFromValue( min_thickness, true ) ) );
197 return;
198 }
199
200 // Now adjust not locked dielectric thickness layers:
201
202 if( items_candidate.size() )
203 {
204 setDefaultLayerWidths( iu_thickness );
205 }
206 else
207 {
208 wxMessageBox( _( "All dielectric thickness layers are locked" ) );
209 }
210
212}
213
214
216{
217 // Disconnect Events connected to items in m_controlItemsList
218 for( wxControl* item: m_controlItemsList )
219 {
220 wxBitmapComboBox* cb = dynamic_cast<wxBitmapComboBox*>( item );
221
222 if( cb )
223 {
224 cb->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED,
225 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onColorSelected ),
226 nullptr, this );
227 }
228
229 wxButton* matButt = dynamic_cast<wxButton*>( item );
230
231 if( matButt )
232 {
233 matButt->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED,
234 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onMaterialChange ),
235 nullptr, this );
236 }
237
238 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( item );
239
240 if( textCtrl )
241 {
242 textCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED,
243 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ),
244 nullptr, this );
245 }
246 }
247}
248
249
251{
252 wxArrayString headers;
253 headers.Add( _( "Layers" ) );
254
255 // Build Dielectric layers list:
256 std::vector<wxArrayString> d_list;
257 std::vector<int> rows; // indexes of row values for each selectable item
258 int row = -1;
259
261 {
262 row++;
263
264 if( !item.m_isEnabled )
265 continue;
266
267 BOARD_STACKUP_ITEM* brd_stackup_item = item.m_Item;
268
269 if( brd_stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
270 {
271 wxArrayString d_item;
272
273 if( brd_stackup_item->GetSublayersCount() > 1 )
274 {
275 d_item.Add( wxString::Format( _( "Layer '%s' (sublayer %d/%d)" ),
276 brd_stackup_item->FormatDielectricLayerName(),
277 item.m_SubItem+1,
278 brd_stackup_item->GetSublayersCount() ) );
279 }
280 else
281 {
282 d_item.Add( brd_stackup_item->FormatDielectricLayerName() );
283 }
284
285 d_list.emplace_back( d_item );
286 rows.push_back( row );
287 }
288 }
289
290 EDA_LIST_DIALOG dlg( m_parentDialog, _( "Add Dielectric Layer" ), headers, d_list,
291 wxEmptyString, false /* do not sort the list: it is **expected** in stack order */);
292 dlg.SetListLabel( _( "Select layer to add:" ) );
293 dlg.HideFilter();
294
295 if( dlg.ShowModal() == wxID_OK && dlg.GetSelection() >= 0 )
296 {
297 row = rows[ dlg.GetSelection() ];
298
299 BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[row].m_Item;
300 int new_sublayer = m_rowUiItemsList[row].m_SubItem;
301
302 // Insert a new item after the selected item
303 brd_stackup_item->AddDielectricPrms( new_sublayer+1 );
304
307 }
308}
309
310
312{
313 wxArrayString headers;
314 headers.Add( _( "Layers" ) );
315
316 // Build deletable Dielectric layers list.
317 // A layer can be deleted if there are 2 (or more) dielectric sub-layers
318 // between 2 copper layers
319 std::vector<wxArrayString> d_list;
320 std::vector<int> rows; // indexes of row values for each selectable item
321 int row = 0; // row index in m_rowUiItemsList of items in choice list
322
323 // Build the list of dielectric layers:
324 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
325 {
326 if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC ||
327 item->GetSublayersCount() <= 1 )
328 {
329 row++;
330 continue;
331 }
332
333 for( int ii = 0; ii < item->GetSublayersCount(); ii++ )
334 {
335 wxArrayString d_item;
336
337 d_item.Add( wxString::Format( _( "Layer '%s' sublayer %d/%d" ),
338 item->FormatDielectricLayerName(),
339 ii+1,
340 item->GetSublayersCount() ) );
341
342 d_list.emplace_back( d_item );
343 rows.push_back( row++ );
344 }
345 }
346
347 EDA_LIST_DIALOG dlg( m_parentDialog, _( "Remove Dielectric Layer" ), headers, d_list,
348 wxEmptyString, false /* do not sort the list: it is **expected** in stack order */ );
349 dlg.SetListLabel( _( "Select layer to remove:" ) );
350 dlg.HideFilter();
351
352 if( dlg.ShowModal() == wxID_OK && dlg.GetSelection() >= 0 )
353 {
354 row = rows[ dlg.GetSelection() ];
355 BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[ row ].m_Item;
356 int sublayer = m_rowUiItemsList[ row ].m_SubItem;
357
358 // Remove the selected sub item for the selected dielectric layer
359 brd_stackup_item->RemoveDielectricPrms( sublayer );
360
363 }
364}
365
366
367void PANEL_SETUP_BOARD_STACKUP::onRemoveDielUI( wxUpdateUIEvent& event )
368{
369 // The m_buttonRemoveDielectricLayer wxButton is enabled only if a dielectric
370 // layer can be removed, i.e. if dielectric layers have sublayers
371 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
372 {
373 if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC )
374 continue;
375
376 if( item->GetSublayersCount() > 1 )
377 {
378 event.Enable( true );
379 return;
380 }
381 }
382
383 event.Enable( false );
384}
385
386
388{
390 return;
391
392 // Build a ASCII representation of stackup and copy it in the clipboard
393 wxString report = BuildStackupReport( m_stackup, m_frame->GetUserUnits() );
394
395 wxLogNull doNotLog; // disable logging of failed clipboard actions
396
397 if( wxTheClipboard->Open() )
398 {
399 // This data objects are held by the clipboard,
400 // so do not delete them in the app.
401 wxTheClipboard->SetData( new wxTextDataObject( report ) );
402 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
403 wxTheClipboard->Close();
404 }
405}
406
407
409{
411 const BOARD_STACKUP_ITEM* item = row.m_Item;
412 const wxBitmapComboBox* choice = dynamic_cast<wxBitmapComboBox*>( row.m_ColorCtrl );
413 wxASSERT( choice );
414
415 int idx = choice ? choice->GetSelection() : 0;
416
417 if( IsCustomColorIdx( item->GetType(), idx ) )
418 return m_rowUiItemsList[aRow].m_UserColor.ToColour();
419 else
420 return GetStandardColor( item->GetType(), idx ).ToColour();
421}
422
423
425{
426 // This function tries to set the PCB thickness to the parameter and uses a fixed prepreg thickness
427 // of 0.1 mm. The core thickness is calculated accordingly as long as it also stays above 0.1mm.
428 // If the core thickness would be smaller than the default pregreg thickness given here,
429 // both are reduced towards zero to arrive at the correct PCB width
430 const int prePregDefaultThickness = pcbIUScale.mmToIU( 0.1 );
431
432 int copperLayerCount = GetCopperLayerCount();
433
434 // This code is for a symmetrical PCB stackup with even copper layer count
435 // If asymmetric stackups were to be implemented, the following layer count calculations
436 // for dielectric/core layers might need adjustments.
437 wxASSERT( copperLayerCount % 2 == 0 );
438
439 int dielectricLayerCount = copperLayerCount - 1;
440 int coreLayerCount = copperLayerCount / 2 - 1;
441
442 wxASSERT( dielectricLayerCount > 0 );
443
444 bool currentLayerIsCore = false;
445
446 // start with prepreg layer on the outside, except when creating two-layer-board
447 if( copperLayerCount == 2 )
448 {
449 coreLayerCount = 1;
450 currentLayerIsCore = true;
451 }
452
453 wxASSERT( coreLayerCount > 0 );
454
455 int prePregLayerCount = dielectricLayerCount - coreLayerCount;
456
457 int totalWidthOfFixedItems = 0;
458
460 {
461 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
462
463 if( !item->IsThicknessEditable() || !ui_item.m_isEnabled )
464 continue;
465
466 wxCheckBox* cbLock = dynamic_cast<wxCheckBox*>( ui_item.m_ThicknessLockCtrl );
467 wxChoice* layerType = dynamic_cast<wxChoice*>( ui_item.m_LayerTypeCtrl );
468
469 if( ( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && !layerType ) || item->GetType() == BS_ITEM_TYPE_SOLDERMASK
470 || item->GetType() == BS_ITEM_TYPE_COPPER || ( cbLock && cbLock->GetValue() ) )
471 {
472 // secondary dielectric layers, mask and copper layers and locked layers will be counted as fixed width
473 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
474 int item_thickness = m_frame->ValueFromString( textCtrl->GetValue() );
475
476 totalWidthOfFixedItems += item_thickness;
477 }
478 }
479
480 // Width that hasn't been allocated by fixed items
481 int remainingWidth =
482 targetThickness - totalWidthOfFixedItems - ( prePregDefaultThickness * prePregLayerCount );
483
484 int prePregThickness = prePregDefaultThickness;
485 int coreThickness = remainingWidth / coreLayerCount;
486
487 if( coreThickness < prePregThickness )
488 {
489 // There's not enough room for prepreg and core layers of at least 0.1 mm, so adjust both down
490 remainingWidth = targetThickness - totalWidthOfFixedItems;
491 prePregThickness = coreThickness = std::max( 0, remainingWidth / dielectricLayerCount );
492 }
493
495 {
496 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
497
498 if( item->GetType() != BS_ITEM_TYPE_DIELECTRIC || !ui_item.m_isEnabled )
499 continue;
500
501 wxChoice* layerType = dynamic_cast<wxChoice*>( ui_item.m_LayerTypeCtrl );
502
503 if( !layerType )
504 {
505 // ignore secondary dielectric layers
506 continue;
507 }
508
509 wxCheckBox* cbLock = dynamic_cast<wxCheckBox*>( ui_item.m_ThicknessLockCtrl );
510
511 if( cbLock && cbLock->GetValue() )
512 {
513 currentLayerIsCore = !currentLayerIsCore;
514
515 // Don't override width of locked layer
516 continue;
517 }
518
519 int layerThickness = currentLayerIsCore ? coreThickness : prePregThickness;
520
521 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
522 layerType->SetSelection( currentLayerIsCore ? 0 : 1 );
523 textCtrl->SetValue( m_frame->StringFromValue( layerThickness ) );
524
525 currentLayerIsCore = !currentLayerIsCore;
526 }
527}
528
529
531{
532 int thickness = 0;
533
535 {
536 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
537
538 if( !item->IsThicknessEditable() || !ui_item.m_isEnabled )
539 continue;
540
541 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
542 int item_thickness = m_frame->ValueFromString( textCtrl->GetValue() );
543
544 thickness += item_thickness;
545 }
546
547 wxString thicknessStr = m_frame->StringFromValue( thickness, true );
548
549 // The text in the event will translate to the value for the text control
550 // and is only updated if it changed
551 m_tcCTValue->ChangeValue( thicknessStr );
552
553 return thickness;
554}
555
556
558{
559 return ( m_choiceCopperLayers->GetSelection() + 1 ) * 2;
560}
561
562
564{
565 int copperCount = GetCopperLayerCount();
566
567 wxASSERT( copperCount >= 2 );
568
570 m_enabledLayers &= ~LSET::InternalCuMask();
571
572 for( int i = 1; i < copperCount - 1; i++ )
573 m_enabledLayers.set( F_Cu + i );
574}
575
576
578{
579 const BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor();
580
581 if( aFullSync )
582 {
583 m_choiceCopperLayers->SetSelection( ( m_board->GetCopperLayerCount() / 2 ) - 1 );
584 m_impedanceControlled->SetValue( brd_stackup.m_HasDielectricConstrains );
585 }
586
587 for( BOARD_STACKUP_ROW_UI_ITEM& ui_row_item : m_rowUiItemsList )
588 {
589 BOARD_STACKUP_ITEM* item = ui_row_item.m_Item;
590 int sub_item = ui_row_item.m_SubItem;
591
592 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
593 {
594 wxChoice* choice = dynamic_cast<wxChoice*>( ui_row_item.m_LayerTypeCtrl );
595
596 if( choice )
597 choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 );
598 }
599
600 if( item->IsMaterialEditable() )
601 {
602 wxTextCtrl* matName = dynamic_cast<wxTextCtrl*>( ui_row_item.m_MaterialCtrl );
603
604 if( matName )
605 {
606 if( IsPrmSpecified( item->GetMaterial( sub_item ) ) )
607 matName->ChangeValue( item->GetMaterial( sub_item ) );
608 else
609 matName->ChangeValue( wxGetTranslation( NotSpecifiedPrm() ) );
610 }
611 }
612
613 if( item->IsThicknessEditable() )
614 {
615 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_ThicknessCtrl );
616
617 if( textCtrl )
618 textCtrl->ChangeValue( m_frame->StringFromValue( item->GetThickness( sub_item ), true ) );
619
620 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
621 {
622 wxCheckBox* cb_box = dynamic_cast<wxCheckBox*> ( ui_row_item.m_ThicknessLockCtrl );
623
624 if( cb_box )
625 cb_box->SetValue( item->IsThicknessLocked( sub_item ) );
626 }
627 }
628
629 if( item->IsColorEditable() )
630 {
631 auto bm_combo = dynamic_cast<wxBitmapComboBox*>( ui_row_item.m_ColorCtrl );
632 int selected = 0; // The "not specified" item
633
634 if( item->GetColor().StartsWith( wxT( "#" ) ) ) // User defined color
635 {
636 COLOR4D custom_color( item->GetColor() );
637
638 ui_row_item.m_UserColor = custom_color;
639
640 selected = GetColorUserDefinedListIdx( item->GetType() );
641
642 if( bm_combo ) // Update user color shown in the wxBitmapComboBox
643 {
644 bm_combo->SetString( selected, item->GetColor() );
645 wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
646 LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D(), custom_color );
647 bm_combo->SetItemBitmap( selected, layerbmp );
648 }
649 }
650 else
651 {
652 if( bm_combo )
653 {
654 // Note: don't use bm_combo->FindString() because the combo strings are
655 // translated.
656 for( int ii = 0; ii < GetStandardColorCount( item->GetType()); ii++ )
657 {
658 if( GetStandardColorName( item->GetType(), ii ) == item->GetColor() )
659 {
660 selected = ii;
661 break;
662 }
663 }
664 }
665 }
666
667 if( bm_combo )
668 bm_combo->SetSelection( selected );
669 }
670
671 if( item->HasEpsilonRValue() )
672 {
673 wxString txt = UIDouble2Str( item->GetEpsilonR( sub_item ) );
674 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_EpsilonCtrl );
675
676 if( textCtrl )
677 textCtrl->ChangeValue( txt );
678 }
679
680 if( item->HasLossTangentValue() )
681 {
682 wxString txt = UIDouble2Str( item->GetLossTangent( sub_item ) );
683 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_LossTgCtrl );
684
685 if( textCtrl )
686 textCtrl->ChangeValue( txt );
687 }
688 }
689
690 // Now enable/disable stackup items, according to the m_enabledLayers config
692
694}
695
696
698{
699 // Now enable/disable stackup items, according to the m_enabledLayers config
700 // Calculate copper layer count from m_enabledLayers, and *do not use* brd_stackup
701 // for that, because it is not necessary up to date
702 // (for instance after modifying the layer count from the panel layers in dialog)
704 int copperLayersCount = copperMask.count();
705
707 {
708 bool show_item;
709 BOARD_STACKUP_ITEM* item = ui_row_item.m_Item;
710
711 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
712 // the m_DielectricLayerId is not a copper layer id, it is a dielectric idx from 1
713 show_item = item->GetDielectricLayerId() < copperLayersCount;
714 else
715 show_item = m_enabledLayers[item->GetBrdLayerId()];
716
717 item->SetEnabled( show_item );
718
719 ui_row_item.m_isEnabled = show_item;
720
721 // Show or not items of this row:
722 ui_row_item.m_Icon->Show( show_item );
723 ui_row_item.m_LayerName->Show( show_item );
724 ui_row_item.m_LayerTypeCtrl->Show( show_item );
725 ui_row_item.m_MaterialCtrl->Show( show_item );
726
727 if( ui_row_item.m_MaterialButt )
728 ui_row_item.m_MaterialButt->Show( show_item );
729
730 ui_row_item.m_ThicknessCtrl->Show( show_item );
731 ui_row_item.m_ThicknessLockCtrl->Show( show_item );
732 ui_row_item.m_ColorCtrl->Show( show_item );
733 ui_row_item.m_EpsilonCtrl->Show( show_item );
734 ui_row_item.m_LossTgCtrl->Show( show_item );
735 }
736}
737
738
739void PANEL_SETUP_BOARD_STACKUP::addMaterialChooser( wxWindowID aId, const wxString* aMaterialName,
740 BOARD_STACKUP_ROW_UI_ITEM& aUiRowItem )
741{
742 wxBoxSizer* bSizerMat = new wxBoxSizer( wxHORIZONTAL );
743 m_fgGridSizer->Add( bSizerMat, 1, wxRIGHT|wxEXPAND, 4 );
744 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY );
745
746 if( aMaterialName )
747 {
748 if( IsPrmSpecified( *aMaterialName ) )
749 textCtrl->ChangeValue( *aMaterialName );
750 else
751 textCtrl->ChangeValue( wxGetTranslation( NotSpecifiedPrm() ) );
752 }
753
754 textCtrl->SetMinSize( m_numericTextCtrlSize );
755 bSizerMat->Add( textCtrl, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
756
757 wxButton* m_buttonMat = new wxButton( m_scGridWin, aId, _( "..." ), wxDefaultPosition,
758 wxDefaultSize, wxBU_EXACTFIT );
759 bSizerMat->Add( m_buttonMat, 0, wxALIGN_CENTER_VERTICAL, 2 );
760
761 m_buttonMat->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
762 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onMaterialChange ),
763 nullptr, this );
764 m_controlItemsList.push_back( m_buttonMat );
765
766 aUiRowItem.m_MaterialCtrl = textCtrl;
767 aUiRowItem.m_MaterialButt = m_buttonMat;
768}
769
770
772{
773 wxStaticText* emptyText = new wxStaticText( m_scGridWin, wxID_ANY, wxEmptyString );
774 m_fgGridSizer->Add( emptyText, 0, wxALIGN_CENTER_VERTICAL );
775 return emptyText;
776}
777
778
780 BOARD_STACKUP_ITEM* aStackupItem,
781 int aSublayerIdx )
782{
783 wxASSERT( aStackupItem );
784 wxASSERT( aSublayerIdx >= 0 && aSublayerIdx < aStackupItem->GetSublayersCount() );
785
786 BOARD_STACKUP_ROW_UI_ITEM ui_row_item( aStackupItem, aSublayerIdx );
787 BOARD_STACKUP_ITEM* item = aStackupItem;
788 int row = aRow;
789
790 // Add color swatch icon. The color will be updated later,
791 // when all widgets are initialized
792 wxStaticBitmap* bitmap = new wxStaticBitmap( m_scGridWin, wxID_ANY, wxNullBitmap );
793 m_fgGridSizer->Add( bitmap, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 4 );
794 ui_row_item.m_Icon = bitmap;
795
796 ui_row_item.m_isEnabled = true;
797
798 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
799 {
800 wxString lname = item->FormatDielectricLayerName();
801
802 if( item->GetSublayersCount() > 1 )
803 {
804 lname << wxT( " (" ) << aSublayerIdx+1 << wxT( "/" )
805 << item->GetSublayersCount() << wxT( ")" );
806 }
807
808 wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname );
809 m_fgGridSizer->Add( st_text, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
810 ui_row_item.m_LayerName = st_text;
811
812 // For a dielectric layer, the layer type choice is not for each sublayer,
813 // only for the first (aSublayerIdx = 0), and is common to all sublayers
814 if( aSublayerIdx == 0 )
815 {
816 wxChoice* choice = new wxChoice( m_scGridWin, wxID_ANY, wxDefaultPosition,
817 wxDefaultSize, m_core_prepreg_choice );
818 choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 );
819 m_fgGridSizer->Add( choice, 1, wxEXPAND|wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
820
821 ui_row_item.m_LayerTypeCtrl = choice;
822 }
823 else
824 {
825 ui_row_item.m_LayerTypeCtrl = addSpacer();
826 }
827 }
828 else
829 {
830 item->SetLayerName( m_board->GetLayerName( item->GetBrdLayerId() ) );
831 wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, item->GetLayerName() );
832 m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 1 );
833 st_text->Show( true );
834 ui_row_item.m_LayerName = st_text;
835
836 wxString lname;
837
838 if( item->GetTypeName() == KEY_COPPER )
839 lname = _( "Copper" );
840 else
841 lname = wxGetTranslation( item->GetTypeName() );
842
843 st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname );
844 m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
845 ui_row_item.m_LayerTypeCtrl = st_text;
846 }
847
848 if( item->IsMaterialEditable() )
849 {
850 wxString matName = item->GetMaterial( aSublayerIdx );
851 addMaterialChooser( ID_ITEM_MATERIAL+row, &matName, ui_row_item );
852 }
853 else
854 {
855 ui_row_item.m_MaterialCtrl = addSpacer();
856 }
857
858 if( item->IsThicknessEditable() )
859 {
860 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, ID_ITEM_THICKNESS+row );
861 textCtrl->SetMinSize( m_numericTextCtrlSize );
862 textCtrl->ChangeValue( m_frame->StringFromValue( item->GetThickness( aSublayerIdx ), true ) );
863 m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
864 m_controlItemsList.push_back( textCtrl );
865 textCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED,
866 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ),
867 nullptr, this );
868 ui_row_item.m_ThicknessCtrl = textCtrl;
869
870 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
871 {
872 wxCheckBox* cb_box = new wxCheckBox( m_scGridWin, ID_ITEM_THICKNESS_LOCKED+row,
873 wxEmptyString );
874 cb_box->SetValue( item->IsThicknessLocked( aSublayerIdx ) );
875 m_fgGridSizer->Add( cb_box, 0, wxALIGN_CENTER_VERTICAL, 2 );
876 ui_row_item.m_ThicknessLockCtrl = cb_box;
877 }
878 else
879 {
880 ui_row_item.m_ThicknessLockCtrl = addSpacer();
881 }
882 }
883 else
884 {
885 ui_row_item.m_ThicknessCtrl = addSpacer();
886 ui_row_item.m_ThicknessLockCtrl = addSpacer();
887 }
888
889 if( item->IsColorEditable() )
890 {
891 if( item->GetColor().StartsWith( wxT( "#" ) ) ) // User defined color
892 {
893 ui_row_item.m_UserColor = COLOR4D( item->GetColor() ).ToColour();
894 }
895 else
896 ui_row_item.m_UserColor = GetDefaultUserColor( item->GetType() );
897
898 wxBitmapComboBox* bm_combo = createColorBox( item, row );
899 int selected = 0; // The "not specified" item
900
901 m_fgGridSizer->Add( bm_combo, 1, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 2 );
902
903 if( item->GetColor().StartsWith( wxT( "#" ) ) )
904 {
905 selected = GetColorUserDefinedListIdx( item->GetType() );
906 bm_combo->SetString( selected, item->GetColor() );
907 }
908 else
909 {
910 // Note: don't use bm_combo->FindString() because the combo strings are translated.
911 for( int ii = 0; ii < GetStandardColorCount( item->GetType()); ii++ )
912 {
913 if( GetStandardColorName( item->GetType(), ii ) == item->GetColor() )
914 {
915 selected = ii;
916 break;
917 }
918 }
919 }
920
921 bm_combo->SetSelection( selected );
922 ui_row_item.m_ColorCtrl = bm_combo;
923 }
924 else
925 {
926 ui_row_item.m_ColorCtrl = addSpacer();
927 }
928
929 if( item->HasEpsilonRValue() )
930 {
931 wxString txt = UIDouble2Str( item->GetEpsilonR( aSublayerIdx ) );
932 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString,
933 wxDefaultPosition, m_numericFieldsSize );
934 textCtrl->ChangeValue( txt );
935 m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
936 ui_row_item.m_EpsilonCtrl = textCtrl;
937 }
938 else
939 {
940 ui_row_item.m_EpsilonCtrl = addSpacer();
941 }
942
943 if( item->HasLossTangentValue() )
944 {
945 wxString txt = UIDouble2Str( item->GetLossTangent( aSublayerIdx ) );;
946 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString,
947 wxDefaultPosition, m_numericFieldsSize );
948 textCtrl->ChangeValue( txt );
949 m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
950 ui_row_item.m_LossTgCtrl = textCtrl;
951 }
952 else
953 {
954 ui_row_item.m_LossTgCtrl = addSpacer();
955 }
956
957 return ui_row_item;
958}
959
960
962{
963 wxWindowUpdateLocker locker( m_scGridWin );
964 m_scGridWin->Hide();
965
966 // Rebuild the stackup for the dialog, after dielectric parameters list is modified
967 // (added/removed):
968
969 // First, delete all ui objects, because wxID values will be no longer valid for many widgets
971 m_controlItemsList.clear();
972
973 // Delete widgets (handled by the wxPanel parent)
975 {
976 // This remove and delete the current ui_item.m_MaterialCtrl sizer
977 ui_item.m_MaterialCtrl->SetSizer( nullptr );
978
979 // Delete other widgets
980 delete ui_item.m_Icon; // Color icon in first column (column 1)
981 delete ui_item.m_LayerName; // string shown in column 2
982 delete ui_item.m_LayerTypeCtrl; // control shown in column 3
983 delete ui_item.m_MaterialCtrl; // control shown in column 4, with m_MaterialButt
984 delete ui_item.m_MaterialButt; // control shown in column 4, with m_MaterialCtrl
985 delete ui_item.m_ThicknessCtrl; // control shown in column 5
986 delete ui_item.m_ThicknessLockCtrl;// control shown in column 6
987 delete ui_item.m_ColorCtrl; // control shown in column 7
988 delete ui_item.m_EpsilonCtrl; // control shown in column 8
989 delete ui_item.m_LossTgCtrl; // control shown in column 9
990 }
991
992 m_rowUiItemsList.clear();
993
994 // In order to recreate a clean grid layer list, we have to delete and
995 // recreate the sizer m_fgGridSizer (just deleting items in this size is not enough)
996 // therefore we also have to add the "old" title items to the newly recreated m_fgGridSizer:
997 m_scGridWin->SetSizer( nullptr ); // This remove and delete the current m_fgGridSizer
998
999 m_fgGridSizer = new wxFlexGridSizer( 0, 9, 0, 2 );
1000 m_fgGridSizer->SetFlexibleDirection( wxHORIZONTAL );
1001 m_fgGridSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
1002 m_fgGridSizer->SetHGap( 6 );
1003 m_scGridWin->SetSizer( m_fgGridSizer );
1004
1005 // Re-add "old" title items:
1006 const int sizer_flags = wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_CENTER_HORIZONTAL;
1007 m_fgGridSizer->Add( m_staticTextLayer, 0, sizer_flags, 2 );
1008 m_fgGridSizer->Add( m_staticTextType, 0, sizer_flags, 2 );
1009 m_fgGridSizer->Add( m_staticTextLayerId, 0, sizer_flags, 5 );
1010 m_fgGridSizer->Add( m_staticTextMaterial, 0, sizer_flags, 2 );
1011 m_fgGridSizer->Add( m_staticTextThickness, 0, sizer_flags, 2 );
1012 m_fgGridSizer->Add( m_bitmapLockThickness, 0, sizer_flags, 1 );
1013 m_fgGridSizer->Add( m_staticTextColor, 0, sizer_flags, 2 );
1014 m_fgGridSizer->Add( m_staticTextEpsilonR, 0, sizer_flags, 2 );
1015 m_fgGridSizer->Add( m_staticTextLossTg, 0, sizer_flags, 2 );
1016
1017
1018 // Now, rebuild the widget list from the new m_stackup items:
1019 buildLayerStackPanel( false );
1020
1021 // Now enable/disable stackup items, according to the m_enabledLayers config
1023
1024 m_scGridWin->Layout();
1025 m_scGridWin->Show();
1026}
1027
1028
1029void PANEL_SETUP_BOARD_STACKUP::buildLayerStackPanel( bool aCreatedInitialStackup )
1030{
1031 // Build a full stackup for the dialog, with a active copper layer count
1032 // = current board layer count to calculate a reasonable default stackup:
1033 if( aCreatedInitialStackup )
1034 {
1035 // Creates a full BOARD_STACKUP with 32 copper layers.
1036 // extra layers will be hidden later.
1037 // but if the number of layer is changed in the dialog, the corresponding
1038 // widgets will be available with their previous values.
1040 const BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor();
1041
1042 // Now initialize all stackup items to the board values, when exist
1043 for( BOARD_STACKUP_ITEM* item: m_stackup.GetList() )
1044 {
1045 // Search for board settings:
1046 for( BOARD_STACKUP_ITEM* board_item: brd_stackup.GetList() )
1047 {
1048 if( item->GetBrdLayerId() != UNDEFINED_LAYER )
1049 {
1050 if( item->GetBrdLayerId() == board_item->GetBrdLayerId() )
1051 {
1052 *item = *board_item;
1053 break;
1054 }
1055 }
1056 else // dielectric layer: see m_DielectricLayerId for identification
1057 {
1058 // Compare dielectric layer with dielectric layer
1059 if( board_item->GetBrdLayerId() != UNDEFINED_LAYER )
1060 continue;
1061
1062 if( item->GetDielectricLayerId() == board_item->GetDielectricLayerId() )
1063 {
1064 *item = *board_item;
1065 break;
1066 }
1067 }
1068 }
1069 }
1070 }
1071
1072 int row = 0;
1073
1074 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1075 {
1076 for( int sub_idx = 0; sub_idx < item->GetSublayersCount(); sub_idx++ )
1077 {
1078 BOARD_STACKUP_ROW_UI_ITEM ui_row_item = createRowData( row, item, sub_idx );
1079 m_rowUiItemsList.emplace_back( ui_row_item );
1080
1081 row++;
1082 }
1083 }
1084
1086}
1087
1088
1089// Transfer current UI settings to m_stackup but not to the board
1091{
1092 wxString error_msg;
1093 bool success = true;
1094 double value;
1095 int row = 0;
1096
1098 {
1099 // Skip stackup items useless for the current board
1100 if( !ui_item.m_isEnabled )
1101 {
1102 row++;
1103 continue;
1104 }
1105
1106 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
1107 int sub_item = ui_item.m_SubItem;
1108
1109 // Add sub layer if there is a new sub layer:
1110 while( item->GetSublayersCount() <= sub_item )
1111 item->AddDielectricPrms( item->GetSublayersCount() );
1112
1113 if( sub_item == 0 ) // Name only main layer
1114 item->SetLayerName( ui_item.m_LayerName->GetLabel() );
1115
1116 if( item->HasEpsilonRValue() )
1117 {
1118 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_EpsilonCtrl );
1119 wxString txt = textCtrl->GetValue();
1120
1121 if( txt.ToDouble( &value ) && value >= 0.0 )
1122 item->SetEpsilonR( value, sub_item );
1123 else if( txt.ToCDouble( &value ) && value >= 0.0 )
1124 item->SetEpsilonR( value, sub_item );
1125 else
1126 {
1127 success = false;
1128 error_msg << _( "Incorrect value for Epsilon R (Epsilon R must be positive or "
1129 "null if not used)" );
1130 }
1131 }
1132
1133 if( item->HasLossTangentValue() )
1134 {
1135 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_LossTgCtrl );
1136 wxString txt = textCtrl->GetValue();
1137
1138 if( txt.ToDouble( &value ) && value >= 0.0 )
1139 item->SetLossTangent( value, sub_item );
1140 else if( txt.ToCDouble( &value ) && value >= 0.0 )
1141 item->SetLossTangent( value, sub_item );
1142 else
1143 {
1144 success = false;
1145
1146 if( !error_msg.IsEmpty() )
1147 error_msg << wxT( "\n" );
1148
1149 error_msg << _( "Incorrect value for Loss tg (Loss tg must be positive or null "
1150 "if not used)" );
1151 }
1152 }
1153
1154 if( item->IsMaterialEditable() )
1155 {
1156 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_MaterialCtrl );
1157 item->SetMaterial( textCtrl->GetValue(), sub_item );
1158
1159 // Ensure the not specified mat name is the keyword, not its translation
1160 // to avoid any issue is the language setting changes
1161 if( !IsPrmSpecified( item->GetMaterial( sub_item ) ) )
1162 item->SetMaterial( NotSpecifiedPrm(), sub_item );
1163 }
1164
1165 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1166 {
1167 // Choice is Core or Prepreg. Sublayers have no choice:
1168 wxChoice* choice = dynamic_cast<wxChoice*>( ui_item.m_LayerTypeCtrl );
1169
1170 if( choice )
1171 {
1172 int idx = choice->GetSelection();
1173
1174 if( idx == 0 )
1175 item->SetTypeName( KEY_CORE );
1176 else
1177 item->SetTypeName( KEY_PREPREG );
1178 }
1179 }
1180
1181 if( item->IsThicknessEditable() )
1182 {
1183 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
1184 int new_thickness = m_frame->ValueFromString( textCtrl->GetValue() );
1185
1186 item->SetThickness( new_thickness, sub_item );
1187
1188 if( new_thickness < 0 )
1189 {
1190 success = false;
1191
1192 if( !error_msg.IsEmpty() )
1193 error_msg << wxT( "\n" );
1194
1195 error_msg << _( "A layer thickness is < 0. Fix it" );
1196 }
1197
1198 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1199 {
1200 // Dielectric thickness layer can have a locked thickness:
1201 wxCheckBox* cb_box = static_cast<wxCheckBox*>( ui_item.m_ThicknessLockCtrl );
1202 item->SetThicknessLocked( cb_box && cb_box->GetValue(), sub_item );
1203 }
1204 }
1205
1206 if( sub_item == 0 && item->IsColorEditable() )
1207 {
1208 wxBitmapComboBox* choice = dynamic_cast<wxBitmapComboBox*>( ui_item.m_ColorCtrl );
1209
1210 if( choice )
1211 {
1212 int idx = choice->GetSelection();
1213
1214 if( IsCustomColorIdx( item->GetType(), idx ) )
1215 item->SetColor( ui_item.m_UserColor.ToHexString() );
1216 else
1217 item->SetColor( GetStandardColorName( item->GetType(), idx ) );
1218 }
1219 }
1220
1221 row++;
1222 }
1223
1224 if( !success )
1225 {
1226 wxMessageBox( error_msg, _( "Errors" ) );
1227 return false;
1228 }
1229
1231
1232 return true;
1233}
1234
1235
1237{
1239 return false;
1240
1241 // NOTE: Copper layer count is transferred via PANEL_SETUP_LAYERS even though it is configured
1242 // on this page, because the logic for confirming deletion of board items on deleted layers is
1243 // on that panel and it doesn't make sense to split it up.
1244
1246
1247 STRING_FORMATTER old_stackup;
1248
1249 // FormatBoardStackup() (using FormatInternalUnits()) expects a "C" locale
1250 // to execute some tests. So switch to the suitable locale
1252 brd_stackup.FormatBoardStackup( &old_stackup, m_board, 0 );
1253
1254 brd_stackup.m_FinishType = m_stackup.m_FinishType;
1259
1260 // copy enabled items to the new board stackup
1261 brd_stackup.RemoveAll();
1262
1263 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1264 {
1265 if( item->IsEnabled() )
1266 brd_stackup.Add( new BOARD_STACKUP_ITEM( *item ) );
1267 }
1268
1269 STRING_FORMATTER new_stackup;
1270 brd_stackup.FormatBoardStackup( &new_stackup, m_board, 0 );
1271
1272 bool modified = old_stackup.GetString() != new_stackup.GetString();
1273 int thickness = brd_stackup.BuildBoardThicknessFromStackup();
1274
1275 if( m_brdSettings->GetBoardThickness() != thickness )
1276 {
1277 m_brdSettings->SetBoardThickness( thickness );
1278 modified = true;
1279 }
1280
1282 {
1284 modified = true;
1285 }
1286
1287 if( modified )
1288 m_frame->OnModify();
1289
1290 return true;
1291}
1292
1293
1295{
1296 BOARD* savedBrd = m_board;
1297 m_board = aBoard;
1298
1299 BOARD_DESIGN_SETTINGS* savedSettings = m_brdSettings;
1300 m_brdSettings = &aBoard->GetDesignSettings();
1301
1303
1305 synchronizeWithBoard( true );
1307
1308 m_brdSettings = savedSettings;
1309 m_board = savedBrd;
1310}
1311
1312
1314{
1315 // First, verify the list of layers currently in stackup:
1316 // if it does not mach the list of layers set in PANEL_SETUP_LAYERS
1317 // rebuild the panel
1318
1319 // the current enabled layers in PANEL_SETUP_LAYERS
1320 // Note: the number of layer can change, but not the layers properties
1322
1323 if( m_enabledLayers != layersList )
1324 {
1325 m_enabledLayers = layersList;
1326
1327 synchronizeWithBoard( false );
1328
1329 Layout();
1330 Refresh();
1331 }
1332}
1333
1334
1336{
1337 int idx = event.GetSelection();
1338 int item_id = event.GetId();
1339 int row = item_id - ID_ITEM_COLOR;
1340 BOARD_STACKUP_ITEM* item = m_rowUiItemsList[row].m_Item;
1341
1342 if( idx == GetStandardColorCount( item->GetType()) - 1 ) // Set user color is the last option in list
1343 {
1344 DIALOG_COLOR_PICKER dlg( this, m_rowUiItemsList[row].m_UserColor, true, nullptr,
1345 GetDefaultUserColor( m_rowUiItemsList[row].m_Item->GetType() ) );
1346
1347#ifdef __WXGTK__
1348 // Give a time-slice to close the menu before opening the dialog.
1349 // (Only matters on some versions of GTK.)
1350 wxSafeYield();
1351#endif
1352
1353 if( dlg.ShowModal() == wxID_OK )
1354 {
1355 wxBitmapComboBox* combo = static_cast<wxBitmapComboBox*>( FindWindowById( item_id ) );
1356 COLOR4D color = dlg.GetColor();
1357
1358 m_rowUiItemsList[row].m_UserColor = color;
1359
1360 combo->SetString( idx, color.ToHexString() );
1361
1362 wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
1363 LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), color );
1364 combo->SetItemBitmap( combo->GetCount() - 1, layerbmp );
1365
1366 combo->SetSelection( idx );
1367 }
1368 }
1369
1370 updateIconColor( row );
1371}
1372
1373
1375{
1376 // Ensure m_materialList contains all materials already in use in stackup list
1377 // and add it is missing
1379 return;
1380
1381 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1382 {
1383 DIELECTRIC_SUBSTRATE_LIST* mat_list = nullptr;
1384
1385 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1386 mat_list = &m_delectricMatList;
1387 else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK )
1388 mat_list = &m_solderMaskMatList;
1389 else if( item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
1390 mat_list = &m_silkscreenMatList;
1391
1392 else
1393 continue;
1394
1395 for( int ii = 0; ii < item->GetSublayersCount(); ii++ )
1396 {
1397 int idx = mat_list->FindSubstrate( item->GetMaterial( ii ),
1398 item->GetEpsilonR( ii ),
1399 item->GetLossTangent( ii ) );
1400
1401 if( idx < 0 && !item->GetMaterial().IsEmpty() )
1402 {
1403 // This material is not in list: add it
1404 DIELECTRIC_SUBSTRATE new_mat;
1405 new_mat.m_Name = item->GetMaterial( ii );
1406 new_mat.m_EpsilonR = item->GetEpsilonR( ii );
1407 new_mat.m_LossTangent = item->GetLossTangent( ii );
1408 mat_list->AppendSubstrate( new_mat );
1409 }
1410 }
1411 }
1412
1413 int row = event.GetId() - ID_ITEM_MATERIAL;
1414 BOARD_STACKUP_ITEM* item = m_rowUiItemsList[row].m_Item;
1415 int sub_item = m_rowUiItemsList[row].m_SubItem;
1416 DIELECTRIC_SUBSTRATE_LIST* item_mat_list = nullptr;
1417
1418 switch( item->GetType() )
1419 {
1420 case BS_ITEM_TYPE_DIELECTRIC: item_mat_list = &m_delectricMatList; break;
1421 case BS_ITEM_TYPE_SOLDERMASK: item_mat_list = &m_solderMaskMatList; break;
1422 case BS_ITEM_TYPE_SILKSCREEN: item_mat_list = &m_silkscreenMatList; break;
1423 default: item_mat_list = nullptr; break;
1424 }
1425
1426 wxCHECK( item_mat_list, /* void */ );
1427
1428 DIALOG_DIELECTRIC_MATERIAL dlg( this, *item_mat_list );
1429
1430 if( dlg.ShowModal() != wxID_OK )
1431 return;
1432
1434
1435 if( substrate.m_Name.IsEmpty() ) // No substrate specified
1436 return;
1437
1438 // Update Name, Epsilon R and Loss tg
1439 item->SetMaterial( substrate.m_Name, sub_item );
1440 item->SetEpsilonR( substrate.m_EpsilonR, sub_item );
1441 item->SetLossTangent( substrate.m_LossTangent, sub_item );
1442
1443 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_MaterialCtrl );
1444 textCtrl->ChangeValue( item->GetMaterial( sub_item ) );
1445
1446 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC
1447 && !item->GetColor().StartsWith( "#" ) /* User defined color */ )
1448 {
1449 if( substrate.m_Name.IsSameAs( "PTFE" )
1450 || substrate.m_Name.IsSameAs( "Teflon" ) )
1451 {
1452 item->SetColor( "PTFE natural" );
1453 }
1454 else if( substrate.m_Name.IsSameAs( "Polyimide" )
1455 || substrate.m_Name.IsSameAs( "Kapton" ) )
1456 {
1457 item->SetColor( "Polyimide" );
1458 }
1459 else if( substrate.m_Name.IsSameAs( "Al" ) )
1460 {
1461 item->SetColor( "Aluminum" );
1462 }
1463 else
1464 {
1465 item->SetColor( "FR4 natural" );
1466 }
1467 }
1468
1469 wxBitmapComboBox* picker = static_cast<wxBitmapComboBox*>( m_rowUiItemsList[row].m_ColorCtrl );
1470
1471 for( int ii = 0; ii < GetStandardColorCount( item->GetType()); ii++ )
1472 {
1473 if( GetStandardColorName( item->GetType(), ii ) == item->GetColor() )
1474 {
1475 picker->SetSelection( ii );
1476 break;
1477 }
1478 }
1479
1480 // some layers have a material choice but not EpsilonR ctrl
1481 if( item->HasEpsilonRValue() )
1482 {
1483 textCtrl = dynamic_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_EpsilonCtrl );
1484
1485 if( textCtrl )
1486 textCtrl->ChangeValue( item->FormatEpsilonR( sub_item ) );
1487 }
1488
1489 // some layers have a material choice but not loss tg ctrl
1490 if( item->HasLossTangentValue() )
1491 {
1492 textCtrl = dynamic_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_LossTgCtrl );
1493
1494 if( textCtrl )
1495 textCtrl->ChangeValue( item->FormatLossTangent( sub_item ) );
1496 }
1497}
1498
1499
1501{
1502 int row = event.GetId() - ID_ITEM_THICKNESS;
1503 wxString value = event.GetString();
1504
1505 BOARD_STACKUP_ITEM* item = GetStackupItem( row );
1506 int idx = GetSublayerId( row );
1507
1508 item->SetThickness( m_frame->ValueFromString( value ), idx );
1509
1511}
1512
1513
1515{
1516 return m_rowUiItemsList[aRow].m_Item;
1517}
1518
1519
1521{
1522 return m_rowUiItemsList[aRow].m_SubItem;
1523}
1524
1525
1527{
1528 BOARD_STACKUP_ITEM* st_item = dynamic_cast<BOARD_STACKUP_ITEM*>( GetStackupItem( aRow ) );
1529
1530 wxASSERT( st_item );
1531 wxColor color;
1532
1533 if( ! st_item )
1534 return color;
1535
1536 switch( st_item->GetType() )
1537 {
1538 case BS_ITEM_TYPE_COPPER: color = copperColor; break;
1540 case BS_ITEM_TYPE_SOLDERMASK: color = GetSelectedColor( aRow ); break;
1541 case BS_ITEM_TYPE_SILKSCREEN: color = GetSelectedColor( aRow ); break;
1543
1544 default:
1546 wxFAIL_MSG( wxT( "PANEL_SETUP_BOARD_STACKUP::getColorIconItem: unrecognized item type" ) );
1547 break;
1548 }
1549
1550 wxASSERT_MSG( color.IsOk(), wxT( "Invalid color in PCB stackup" ) );
1551
1552 return color;
1553}
1554
1555
1557{
1558 // explicit depth important under MSW. We use R,V,B 24 bits/pixel bitmap
1559 const int bitmap_depth = 24;
1560
1561 if( aRow >= 0 )
1562 {
1563 wxStaticBitmap* st_bitmap = m_rowUiItemsList[aRow].m_Icon;
1564
1565 wxBitmap bmp( m_colorIconsSize.x, m_colorIconsSize.y / 2, bitmap_depth );
1566 drawBitmap( bmp, getColorIconItem( aRow ) );
1567 st_bitmap->SetBitmap( bmp );
1568 return;
1569 }
1570
1571 for( unsigned row = 0; row < m_rowUiItemsList.size(); row++ )
1572 {
1573 wxBitmap bmp( m_colorIconsSize.x, m_colorIconsSize.y / 2, bitmap_depth );
1574 drawBitmap( bmp, getColorIconItem( row ) );
1575 m_rowUiItemsList[row].m_Icon->SetBitmap( bmp );
1576 }
1577}
1578
1579
1581 int aRow )
1582{
1583 wxBitmapComboBox* combo = new wxBitmapComboBox( m_scGridWin, ID_ITEM_COLOR + aRow,
1584 wxEmptyString, wxDefaultPosition,
1585 wxDefaultSize, 0, nullptr, wxCB_READONLY );
1586
1587 // Fills the combo box with choice list + bitmaps
1588 BOARD_STACKUP_ITEM_TYPE itemType = aStackupItem ? aStackupItem->GetType()
1590
1591 for( int ii = 0; ii < GetStandardColorCount( itemType ); ii++ )
1592 {
1593 wxString label;
1594 COLOR4D curr_color;
1595
1596 // Defined colors have a name, the user color uses HTML notation ( i.e. #FF000080)
1597 if( IsCustomColorIdx( itemType, ii )
1598 && aStackupItem && aStackupItem->GetColor().StartsWith( wxT( "#" ) ) )
1599 {
1600 label = aStackupItem->GetColor();
1601 curr_color = COLOR4D( label );
1602 }
1603 else
1604 {
1605 label = wxGetTranslation( GetStandardColorName( itemType, ii ) );
1606 curr_color = GetStandardColor( itemType, ii );
1607 }
1608
1609 wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
1610 LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), curr_color );
1611
1612 combo->Append( label, layerbmp );
1613 }
1614
1615 // Ensure the size of the widget is enough to show the text and the icon
1616 // We have to have a selected item when doing this, because otherwise GTK
1617 // will just choose a random size that might not fit the actual data
1618 // (such as in cases where the font size is very large). So we select
1619 // the longest item (which should be the last item), and size it that way.
1620 int sel = combo->GetSelection();
1621 combo->SetSelection( combo->GetCount() - 1 );
1622
1623 combo->SetMinSize( wxSize( -1, -1 ) );
1624 wxSize bestSize = combo->GetBestSize();
1625
1626 bestSize.x = bestSize.x + m_colorSwatchesSize.x;
1627 combo->SetMinSize( bestSize );
1628 combo->SetSelection( sel );
1629
1630 // add the wxBitmapComboBox to wxControl list, to be able to disconnect the event
1631 // on exit
1632 m_controlItemsList.push_back( combo );
1633
1634 combo->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED,
1635 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onColorSelected ),
1636 nullptr, this );
1637
1638 combo->Bind( wxEVT_COMBOBOX_DROPDOWN,
1639 [combo]( wxCommandEvent& aEvent )
1640 {
1641 combo->SetString( combo->GetCount() - 1, _( "Custom..." ) );
1642 } );
1643
1644 return combo;
1645}
1646
1647
1648void drawBitmap( wxBitmap& aBitmap, wxColor aColor )
1649{
1650 wxNativePixelData data( aBitmap );
1651 wxNativePixelData::Iterator p( data );
1652
1653 for( int yy = 0; yy < data.GetHeight(); yy++ )
1654 {
1655 wxNativePixelData::Iterator rowStart = p;
1656
1657 for( int xx = 0; xx < data.GetWidth(); xx++ )
1658 {
1659 p.Red() = aColor.Red();
1660 p.Green() = aColor.Green();
1661 p.Blue() = aColor.Blue();
1662 ++p;
1663 }
1664
1665 p = rowStart;
1666 p.OffsetY( data, 1 );
1667 }
1668}
1669
1670
int color
Definition: DXF_plotter.cpp:57
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
wxBitmap KiScaledBitmap(BITMAPS aBitmap, wxWindow *aWindow, int aHeight, bool aQuantized)
Construct a wxBitmap from a memory record, scaling it if device DPI demands it.
Definition: bitmap.cpp:156
bool IsPrmSpecified(const wxString &aPrmValue)
BOARD_STACKUP_ITEM_TYPE
Definition: board_stackup.h:41
@ BS_ITEM_TYPE_UNDEFINED
Definition: board_stackup.h:42
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:43
@ BS_ITEM_TYPE_SILKSCREEN
Definition: board_stackup.h:49
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:44
@ BS_ITEM_TYPE_SOLDERPASTE
Definition: board_stackup.h:46
@ BS_ITEM_TYPE_SOLDERMASK
Definition: board_stackup.h:47
wxString BuildStackupReport(BOARD_STACKUP &aStackup, EDA_UNITS aUnits)
Container for design settings for a BOARD object.
int GetBoardThickness() const
The full thickness of the board including copper and masks.
BOARD_STACKUP & GetStackupDescriptor()
void SetBoardThickness(int aThickness)
Manage one layer needed to make a physical board.
Definition: board_stackup.h:90
void AddDielectricPrms(int aDielectricPrmsIdx)
Add (insert) a DIELECTRIC_PRMS item to m_DielectricPrmsList all values are set to default.
wxString GetTypeName() const
int GetSublayersCount() const
double GetEpsilonR(int aDielectricSubLayer=0) const
bool HasEpsilonRValue() const
void SetThickness(int aThickness, int aDielectricSubLayer=0)
bool IsMaterialEditable() const
void SetThicknessLocked(bool aLocked, int aDielectricSubLayer=0)
wxString FormatDielectricLayerName() const
wxString GetLayerName() const
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
bool HasLossTangentValue() const
PCB_LAYER_ID GetBrdLayerId() const
bool IsThicknessEditable() const
wxString GetColor() const
void SetLossTangent(double aTg, int aDielectricSubLayer=0)
int GetThickness(int aDielectricSubLayer=0) const
void SetEnabled(bool aEnable)
BOARD_STACKUP_ITEM_TYPE GetType() const
wxString GetMaterial(int aDielectricSubLayer=0) const
void SetTypeName(const wxString &aName)
bool IsThicknessLocked(int aDielectricSubLayer=0) const
wxString FormatEpsilonR(int aDielectricSubLayer=0) const
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
void SetColor(const wxString &aColorName)
void SetLayerName(const wxString &aName)
void RemoveDielectricPrms(int aDielectricPrmsIdx)
Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList.
int GetDielectricLayerId() const
bool IsColorEditable() const
wxString FormatLossTangent(int aDielectricSubLayer=0) const
double GetLossTangent(int aDielectricSubLayer=0) const
Manage layers needed to make a physical board.
void RemoveAll()
Delete all items in list and clear the list.
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, const BOARD *aBoard, int aNestLevel) const
Write the stackup info on board file.
bool m_CastellatedPads
True if castellated pads exist.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
static LSET StackupAllowedBrdLayers()
int BuildBoardThicknessFromStackup() const
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
void Add(BOARD_STACKUP_ITEM *aItem)
Add a new item in stackup layer.
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
bool m_EdgePlating
True if the edge board is plated.
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
wxString m_FinishType
The name of external copper finish.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:511
int GetCopperLayerCount() const
Definition: board.cpp:499
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:410
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:628
KIGFX::COLOR4D GetColor()
a Dialog to select/change/add a dielectric material from a material list
int AppendSubstrate(DIELECTRIC_SUBSTRATE &aItem)
Append a item in list similar to aItem.
int FindSubstrate(DIELECTRIC_SUBSTRATE *aItem)
Find a item in list similar to aItem.
A dialog which shows:
void SetListLabel(const wxString &aLabel)
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
static void DrawColorSwatch(wxBitmap &aLayerbmp, const COLOR4D &aBackground, const COLOR4D &aColor)
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
static LSET ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition: lset.cpp:801
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:733
Class PANEL_SETUP_BOARD_STACKUP_BASE.
void onAddDielectricLayer(wxCommandEvent &event) override
DIELECTRIC_SUBSTRATE_LIST m_silkscreenMatList
void onExportToClipboard(wxCommandEvent &event) override
bool transferDataFromUIToStackup()
Transfer current UI settings to m_stackup but not to the board.
BOARD_STACKUP_ROW_UI_ITEM createRowData(int aRow, BOARD_STACKUP_ITEM *aStackupItem, int aSublayerIdx)
Creates a BOARD_STACKUP_ROW_UI_ITEM relative to the aStackupItem.
wxControl * addSpacer()
add a Spacer in m_fgGridSizer when a empty cell is needed
std::vector< wxControl * > m_controlItemsList
void onColorSelected(wxCommandEvent &event)
void onCopperLayersSelCount(wxCommandEvent &event) override
void ImportSettingsFrom(BOARD *aBoard)
void OnLayersOptionsChanged(LSET aNewLayerSet)
Must be called if the copper layers count has changed or solder mask, solder paste or silkscreen laye...
void updateIconColor(int aRow=-1)
Update the icons color (swatches in first grid column)
void updateCopperLayerCount()
Updates the enabled copper layers when the dropdown is changed.
wxColor GetSelectedColor(int aRow) const
Return the color currently selected for the row aRow.
void onRemoveDielectricLayer(wxCommandEvent &event) override
int computeBoardThickness()
Recompute the board thickness and update the textbox.
void setDefaultLayerWidths(int targetThickness)
Set the widths of dielectric layers to sensible defaults.
void synchronizeWithBoard(bool aFullSync)
Synchronize the full stackup shown in m_fgGridSizer according to the stackup of the current board and...
void onThicknessChange(wxCommandEvent &event)
BOARD_DESIGN_SETTINGS * m_brdSettings
BOARD_STACKUP_ITEM * GetStackupItem(int aRow)
void showOnlyActiveLayers()
Show or do not show items in m_fgGridSizer according to the stackup of the current board.
DIELECTRIC_SUBSTRATE_LIST m_solderMaskMatList
std::vector< BOARD_STACKUP_ROW_UI_ITEM > m_rowUiItemsList
PANEL_SETUP_LAYERS * m_panelLayers
void onRemoveDielUI(wxUpdateUIEvent &event) override
void buildLayerStackPanel(bool aCreatedInitialStackup)
Populate m_fgGridSizer with items to handle stackup parameters This is a full list: all copper layers...
DIELECTRIC_SUBSTRATE_LIST m_delectricMatList
wxBitmapComboBox * createColorBox(BOARD_STACKUP_ITEM *aStackupItem, int aRow)
creates a bitmap combobox to select a layer color
void disconnectEvents()
disconnect event handlers connected to wxControl items found in list m_controlItemsList
void onAdjustDielectricThickness(wxCommandEvent &event) override
void addMaterialChooser(wxWindowID aId, const wxString *aMaterialName, BOARD_STACKUP_ROW_UI_ITEM &aUiRowItem)
add a control (a wxTextCtrl + a button) in m_fgGridSizer to select a material
void rebuildLayerStackPanel()
Populate m_fgGridSizer with items to handle stackup parameters If previous items are in list,...
void onMaterialChange(wxCommandEvent &event)
PANEL_SETUP_BOARD_STACKUP(PAGED_DIALOG *aParent, PCB_EDIT_FRAME *aFrame, PANEL_SETUP_LAYERS *aPanelLayers)
void SetPhysicalStackupPanel(PANEL_SETUP_BOARD_STACKUP *aPanel)
BOARD * GetBoard() const
The main frame for Pcbnew.
void OnModify() override
Must be called after a board change to set the modified flag.
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:415
const std::string & GetString()
Definition: richio.h:438
wxString StringFromValue(double aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Converts aValue in internal units into a united string.
int ValueFromString(const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Converts aTextValue in aUnits to internal units used by the frame.
EDA_UNITS GetUserUnits() const
#define _(s)
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
wxString GetText(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
Definition: eda_units.cpp:98
static wxColor pasteColor(200, 200, 200)
static wxColor copperColor(220, 180, 30)
#define ID_INCREMENT
static wxColor dielectricColor(75, 120, 75)
static void drawBitmap(wxBitmap &aBitmap, wxColor aColor)
@ ID_ITEM_COLOR
@ ID_ITEM_THICKNESS
@ ID_ITEM_THICKNESS_LOCKED
@ ID_ITEM_MATERIAL
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:74
int GetStandardColorCount(BOARD_STACKUP_ITEM_TYPE aType)
int GetColorUserDefinedListIdx(BOARD_STACKUP_ITEM_TYPE aType)
bool IsCustomColorIdx(BOARD_STACKUP_ITEM_TYPE aType, int aIdx)
KIGFX::COLOR4D GetStandardColor(BOARD_STACKUP_ITEM_TYPE aType, int aIdx)
wxString NotSpecifiedPrm()
#define KEY_PREPREG
#define KEY_COPPER
#define KEY_CORE
const wxString & GetStandardColorName(BOARD_STACKUP_ITEM_TYPE aType, int aIdx)
KIGFX::COLOR4D GetDefaultUserColor(BOARD_STACKUP_ITEM_TYPE aType)
std::string UIDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 We want to avoid scientific ...
BOARD_STACKUP_ITEM * m_Item
constexpr int mmToIU(double mm) const
Definition: base_units.h:89