KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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 <widgets/wx_panel.h>
33#include <wx/log.h>
34#include <wx/rawbmp.h>
35#include <wx/clipbrd.h>
36#include <wx/wupdlock.h>
37#include <wx/richmsgdlg.h>
38#include <math/util.h> // for KiROUND
39
40#include "panel_board_stackup.h"
41#include <panel_setup_layers.h>
43#include <bitmaps.h>
45#include <wx/textdlg.h>
46
47#include <locale_io.h>
48#include <eda_list_dialog.h>
49#include <string_utils.h> // for UIDouble2Str()
50
51
52// Some wx widget ID to know what widget has fired a event:
53#define ID_INCREMENT 256 // space between 2 ID type. Bigger than the layer count max
54
55// The actual widget IDs are the base id + the row index.
56// they are used in events to know the row index of the control that fired the event
58{
59 ID_ITEM_MATERIAL = 10000, // Be sure it is higher than other IDs
60 // used in the board setup dialog
64};
65
66// Default colors to draw icons:
67static wxColor copperColor( 220, 180, 30 );
68static wxColor dielectricColor( 75, 120, 75 );
69static wxColor pasteColor( 200, 200, 200 );
70
71static void drawBitmap( wxBitmap& aBitmap, wxColor aColor );
72
73
75 PCB_EDIT_FRAME* aFrame,
76 PANEL_SETUP_LAYERS* aPanelLayers ):
77 PANEL_SETUP_BOARD_STACKUP_BASE( aParentWindow ),
78 m_delectricMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_DIELECTRIC ),
79 m_solderMaskMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_SOLDERMASK ),
80 m_silkscreenMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_SILKSCREEN )
81{
82 m_frame = aFrame;
83 m_panelLayers = aPanelLayers;
86
87 m_panel1->SetBorders( false, false, true, true );
88
90
92
93 // Calculates a good size for color swatches (icons) in this dialog
94 m_colorSwatchesSize = GetTextExtent( wxT( "XX" ) );
95 m_colorIconsSize = GetTextExtent( wxT( "XXXX" ) );
96
97 // Calculates a good size for wxTextCtrl to enter Epsilon R and Loss tan
98 // ("0.0000000" + margins)
99 m_numericFieldsSize = GetTextExtent( wxT( "X.XXXXXXX" ) );
100 m_numericFieldsSize.y = -1; // Use default for the vertical size
101
102 // Calculates a minimal size for wxTextCtrl to enter a dim with units
103 // ("000.0000000 mils" + margins)
104 m_numericTextCtrlSize = GetTextExtent( wxT( "XXX.XXXXXXX mils" ) );
105 m_numericTextCtrlSize.y = -1; // Use default for the vertical size
106
107 // The grid column containing the lock checkbox is kept to a minimal
108 // size. So we use a wxStaticBitmap: set the bitmap itself
109 m_bitmapLockThickness->SetBitmap( KiScaledBitmap( BITMAPS::locked, aFrame ) );
110
111 // Gives a minimal size of wxTextCtrl showing dimensions+units
112 m_tcCTValue->SetMinSize( m_numericTextCtrlSize );
113
114 // Prepare dielectric layer type: layer type keyword is "core" or "prepreg"
115 m_core_prepreg_choice.Add( _( "Core" ) );
116 m_core_prepreg_choice.Add( _( "PrePreg" ) );
117
118 buildLayerStackPanel( true );
119 synchronizeWithBoard( true );
121}
122
123
125{
127}
128
129
131{
132 int oldBoardWidth = static_cast<int>( m_frame->ValueFromString( m_tcCTValue->GetValue() ) );
135 setDefaultLayerWidths( oldBoardWidth );
137 Layout();
138}
139
140
142{
143 // The list of items that can be modified:
144 std::vector< BOARD_STACKUP_ROW_UI_ITEM* > items_candidate;
145
146 // Some dielectric layers can have a locked thickness, so calculate the min
147 // acceptable thickness
148 int min_thickness = 0;
149
151 {
152 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
153
154 if( !item->IsThicknessEditable() || !ui_item.m_isEnabled )
155 continue;
156
157 // We are looking for locked thickness items only:
158 wxCheckBox* cb_box = dynamic_cast<wxCheckBox*> ( ui_item.m_ThicknessLockCtrl );
159
160 if( cb_box && !cb_box->GetValue() )
161 {
162 items_candidate.push_back( &ui_item );
163 continue;
164 }
165
166 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
167
168 int item_thickness = m_frame->ValueFromString( textCtrl->GetValue() );
169 min_thickness += item_thickness;
170 }
171
172 wxString title;
173
174 if( min_thickness == 0 )
175 {
176 title.Printf( _( "Enter board thickness in %s:" ),
177 EDA_UNIT_UTILS::GetText( m_frame->GetUserUnits() ).Trim( false ) );
178 }
179 else
180 {
181 title.Printf( _( "Enter expected board thickness (min value %s):" ),
182 m_frame->StringFromValue( min_thickness, true ) );
183 }
184
185 wxTextEntryDialog dlg( this, title, _( "Adjust Unlocked Dielectric Layers" ) );
186
187 if( dlg.ShowModal() != wxID_OK )
188 return;
189
190 int iu_thickness = m_frame->ValueFromString( dlg.GetValue() );
191
192 if( iu_thickness < min_thickness )
193 {
194 wxMessageBox( wxString::Format( _("Value too small (min value %s)." ),
195 m_frame->StringFromValue( min_thickness, true ) ) );
196 return;
197 }
198
199 // Now adjust not locked dielectric thickness layers:
200
201 if( items_candidate.size() )
202 setDefaultLayerWidths( iu_thickness );
203 else
204 wxMessageBox( _( "All dielectric thickness layers are locked" ) );
205
207}
208
209
211{
212 // Disconnect Events connected to items in m_controlItemsList
213 for( wxControl* item: m_controlItemsList )
214 {
215 wxBitmapComboBox* cb = dynamic_cast<wxBitmapComboBox*>( item );
216
217 if( cb )
218 {
219 cb->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED,
220 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onColorSelected ),
221 nullptr, this );
222 }
223
224 wxButton* matButt = dynamic_cast<wxButton*>( item );
225
226 if( matButt )
227 {
228 matButt->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED,
229 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onMaterialChange ),
230 nullptr, this );
231 }
232
233 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( item );
234
235 if( textCtrl )
236 {
237 textCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED,
238 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ),
239 nullptr, this );
240 }
241 }
242}
243
244
246{
247 wxArrayString headers;
248 headers.Add( _( "Layers" ) );
249
250 // Build Dielectric layers list:
251 std::vector<wxArrayString> d_list;
252 std::vector<int> rows; // indexes of row values for each selectable item
253 int row = -1;
254
256 {
257 row++;
258
259 if( !item.m_isEnabled )
260 continue;
261
262 BOARD_STACKUP_ITEM* brd_stackup_item = item.m_Item;
263
264 if( brd_stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
265 {
266 wxArrayString d_item;
267
268 if( brd_stackup_item->GetSublayersCount() > 1 )
269 {
270 d_item.Add( wxString::Format( _( "Layer '%s' (sublayer %d/%d)" ),
271 brd_stackup_item->FormatDielectricLayerName(),
272 item.m_SubItem+1,
273 brd_stackup_item->GetSublayersCount() ) );
274 }
275 else
276 {
277 d_item.Add( brd_stackup_item->FormatDielectricLayerName() );
278 }
279
280 d_list.emplace_back( d_item );
281 rows.push_back( row );
282 }
283 }
284
285 EDA_LIST_DIALOG dlg( PAGED_DIALOG::GetDialog( this ), _( "Add Dielectric Layer" ),
286 headers, d_list, wxEmptyString,
287 false /* do not sort the list: it is **expected** in stackup order */ );
288 dlg.SetListLabel( _( "Select layer to add:" ) );
289 dlg.HideFilter();
290
291 if( dlg.ShowModal() == wxID_OK && dlg.GetSelection() >= 0 )
292 {
293 row = rows[ dlg.GetSelection() ];
294
295 BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[row].m_Item;
296 int new_sublayer = m_rowUiItemsList[row].m_SubItem;
297
298 // Insert a new item after the selected item
299 brd_stackup_item->AddDielectricPrms( new_sublayer+1 );
300
303 }
304}
305
306
308{
309 wxArrayString headers;
310 headers.Add( _( "Layers" ) );
311
312 // Build deletable Dielectric layers list.
313 // A layer can be deleted if there are 2 (or more) dielectric sub-layers
314 // between 2 copper layers
315 std::vector<wxArrayString> d_list;
316 std::vector<int> rows; // indexes of row values for each selectable item
317 int row = 0; // row index in m_rowUiItemsList of items in choice list
318
319 // Build the list of dielectric layers:
320 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
321 {
322 if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC ||
323 item->GetSublayersCount() <= 1 )
324 {
325 row++;
326 continue;
327 }
328
329 for( int ii = 0; ii < item->GetSublayersCount(); ii++ )
330 {
331 wxArrayString d_item;
332
333 d_item.Add( wxString::Format( _( "Layer '%s' sublayer %d/%d" ),
334 item->FormatDielectricLayerName(),
335 ii+1,
336 item->GetSublayersCount() ) );
337
338 d_list.emplace_back( d_item );
339 rows.push_back( row++ );
340 }
341 }
342
343 EDA_LIST_DIALOG dlg( PAGED_DIALOG::GetDialog( this ), _( "Remove Dielectric Layer" ),
344 headers, d_list, wxEmptyString,
345 false /* do not sort the list: it is **expected** in stackup order */ );
346 dlg.SetListLabel( _( "Select layer to remove:" ) );
347 dlg.HideFilter();
348
349 if( dlg.ShowModal() == wxID_OK && dlg.GetSelection() >= 0 )
350 {
351 row = rows[ dlg.GetSelection() ];
352 BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[ row ].m_Item;
353 int sublayer = m_rowUiItemsList[ row ].m_SubItem;
354
355 // Remove the selected sub item for the selected dielectric layer
356 brd_stackup_item->RemoveDielectricPrms( sublayer );
357
360 }
361}
362
363
364void PANEL_SETUP_BOARD_STACKUP::onRemoveDielUI( wxUpdateUIEvent& event )
365{
366 // The m_buttonRemoveDielectricLayer wxButton is enabled only if a dielectric
367 // layer can be removed, i.e. if dielectric layers have sublayers
368 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
369 {
370 if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC )
371 continue;
372
373 if( item->GetSublayersCount() > 1 )
374 {
375 event.Enable( true );
376 return;
377 }
378 }
379
380 event.Enable( false );
381}
382
383
385{
387 return;
388
389 // Build a ASCII representation of stackup and copy it in the clipboard
390 wxString report = BuildStackupReport( m_stackup, m_frame->GetUserUnits() );
391
392 wxLogNull doNotLog; // disable logging of failed clipboard actions
393
394 if( wxTheClipboard->Open() )
395 {
396 // This data objects are held by the clipboard,
397 // so do not delete them in the app.
398 wxTheClipboard->SetData( new wxTextDataObject( report ) );
399 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
400 wxTheClipboard->Close();
401 }
402}
403
404
406{
408 const BOARD_STACKUP_ITEM* item = row.m_Item;
409 const wxBitmapComboBox* choice = dynamic_cast<wxBitmapComboBox*>( row.m_ColorCtrl );
410 int idx = choice ? choice->GetSelection() : 0;
411
412 if( IsCustomColorIdx( item->GetType(), idx ) )
413 return m_rowUiItemsList[aRow].m_UserColor.ToColour();
414 else
415 return GetStandardColor( item->GetType(), idx ).ToColour();
416}
417
418
420{
421 // This function tries to set the PCB thickness to the parameter and uses a fixed prepreg thickness
422 // of 0.1 mm. The core thickness is calculated accordingly as long as it also stays above 0.1mm.
423 // If the core thickness would be smaller than the default pregreg thickness given here,
424 // both are reduced towards zero to arrive at the correct PCB width
425 const int prePregDefaultThickness = pcbIUScale.mmToIU( 0.1 );
426
427 int copperLayerCount = GetCopperLayerCount();
428
429 // This code is for a symmetrical PCB stackup with even copper layer count
430 // If asymmetric stackups were to be implemented, the following layer count calculations
431 // for dielectric/core layers might need adjustments.
432 wxASSERT( copperLayerCount % 2 == 0 );
433
434 int dielectricLayerCount = copperLayerCount - 1;
435 int coreLayerCount = copperLayerCount / 2 - 1;
436
437 wxASSERT( dielectricLayerCount > 0 );
438
439 bool currentLayerIsCore = false;
440
441 // start with prepreg layer on the outside, except when creating two-layer-board
442 if( copperLayerCount == 2 )
443 {
444 coreLayerCount = 1;
445 currentLayerIsCore = true;
446 }
447
448 wxASSERT( coreLayerCount > 0 );
449
450 int prePregLayerCount = dielectricLayerCount - coreLayerCount;
451
452 int totalWidthOfFixedItems = 0;
453
455 {
456 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
457
458 if( !item->IsThicknessEditable() || !ui_item.m_isEnabled )
459 continue;
460
461 wxCheckBox* cbLock = dynamic_cast<wxCheckBox*>( ui_item.m_ThicknessLockCtrl );
462 wxChoice* layerType = dynamic_cast<wxChoice*>( ui_item.m_LayerTypeCtrl );
463
464 if( ( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && !layerType )
465 || item->GetType() == BS_ITEM_TYPE_SOLDERMASK
466 || item->GetType() == BS_ITEM_TYPE_COPPER
467 || ( cbLock && cbLock->GetValue() ) )
468 {
469 // secondary dielectric layers, mask and copper layers and locked layers will be
470 // counted as fixed width
471 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
472 int item_thickness = m_frame->ValueFromString( textCtrl->GetValue() );
473
474 totalWidthOfFixedItems += item_thickness;
475 }
476 }
477
478 // Width that hasn't been allocated by fixed items
479 int remainingWidth = targetThickness
480 - totalWidthOfFixedItems
481 - ( prePregDefaultThickness * prePregLayerCount );
482
483 int prePregThickness = prePregDefaultThickness;
484 int coreThickness = remainingWidth / coreLayerCount;
485
486 if( coreThickness < prePregThickness )
487 {
488 // There's not enough room for prepreg and core layers of at least 0.1 mm, so adjust both down
489 remainingWidth = targetThickness - totalWidthOfFixedItems;
490 prePregThickness = coreThickness = std::max( 0, remainingWidth / dielectricLayerCount );
491 }
492
494 {
495 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
496
497 if( item->GetType() != BS_ITEM_TYPE_DIELECTRIC || !ui_item.m_isEnabled )
498 continue;
499
500 wxChoice* layerType = dynamic_cast<wxChoice*>( ui_item.m_LayerTypeCtrl );
501
502 if( !layerType )
503 {
504 // ignore secondary dielectric layers
505 continue;
506 }
507
508 wxCheckBox* cbLock = dynamic_cast<wxCheckBox*>( ui_item.m_ThicknessLockCtrl );
509
510 if( cbLock && cbLock->GetValue() )
511 {
512 currentLayerIsCore = !currentLayerIsCore;
513
514 // Don't override width of locked layer
515 continue;
516 }
517
518 int layerThickness = currentLayerIsCore ? coreThickness : prePregThickness;
519
520 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
521 layerType->SetSelection( currentLayerIsCore ? 0 : 1 );
522 textCtrl->SetValue( m_frame->StringFromValue( layerThickness ) );
523
524 currentLayerIsCore = !currentLayerIsCore;
525 }
526}
527
528
530{
531 int thickness = 0;
532
534 {
535 BOARD_STACKUP_ITEM* item = ui_item.m_Item;
536
537 if( !item->IsThicknessEditable() || !ui_item.m_isEnabled )
538 continue;
539
540 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
541 int item_thickness = m_frame->ValueFromString( textCtrl->GetValue() );
542
543 thickness += item_thickness;
544 }
545
546 wxString thicknessStr = m_frame->StringFromValue( thickness, true );
547
548 // The text in the event will translate to the value for the text control
549 // and is only updated if it changed
550 m_tcCTValue->ChangeValue( thicknessStr );
551
552 return thickness;
553}
554
555
557{
558 return ( m_choiceCopperLayers->GetSelection() + 1 ) * 2;
559}
560
561
563{
564 int copperCount = GetCopperLayerCount();
565
566 wxASSERT( copperCount >= 2 );
567
569 m_enabledLayers &= ~LSET::InternalCuMask();
570
571 for( int i = 1; i < copperCount - 1; i++ )
572 m_enabledLayers.set( F_Cu + i );
573}
574
575
577{
578 const BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor();
579
580 if( aFullSync )
581 {
582 m_choiceCopperLayers->SetSelection( ( m_board->GetCopperLayerCount() / 2 ) - 1 );
583 m_impedanceControlled->SetValue( brd_stackup.m_HasDielectricConstrains );
584 }
585
586 for( BOARD_STACKUP_ROW_UI_ITEM& ui_row_item : m_rowUiItemsList )
587 {
588 BOARD_STACKUP_ITEM* item = ui_row_item.m_Item;
589 int sub_item = ui_row_item.m_SubItem;
590
591 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
592 {
593 wxChoice* choice = dynamic_cast<wxChoice*>( ui_row_item.m_LayerTypeCtrl );
594
595 if( choice )
596 choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 );
597 }
598
599 if( item->IsMaterialEditable() )
600 {
601 wxTextCtrl* matName = dynamic_cast<wxTextCtrl*>( ui_row_item.m_MaterialCtrl );
602
603 if( matName )
604 {
605 if( IsPrmSpecified( item->GetMaterial( sub_item ) ) )
606 matName->ChangeValue( item->GetMaterial( sub_item ) );
607 else
608 matName->ChangeValue( wxGetTranslation( NotSpecifiedPrm() ) );
609 }
610 }
611
612 if( item->IsThicknessEditable() )
613 {
614 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_ThicknessCtrl );
615
616 if( textCtrl )
617 textCtrl->ChangeValue( m_frame->StringFromValue( item->GetThickness( sub_item ), true ) );
618
619 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
620 {
621 wxCheckBox* cb_box = dynamic_cast<wxCheckBox*> ( ui_row_item.m_ThicknessLockCtrl );
622
623 if( cb_box )
624 cb_box->SetValue( item->IsThicknessLocked( sub_item ) );
625 }
626 }
627
628 if( item->IsColorEditable() )
629 {
630 auto bm_combo = dynamic_cast<wxBitmapComboBox*>( ui_row_item.m_ColorCtrl );
631 int selected = 0; // The "not specified" item
632
633 if( item->GetColor( sub_item ).StartsWith( wxT( "#" ) ) ) // User defined color
634 {
635 COLOR4D custom_color( item->GetColor( sub_item ) );
636
637 ui_row_item.m_UserColor = custom_color;
638
639 selected = GetColorUserDefinedListIdx( item->GetType() );
640
641 if( bm_combo ) // Update user color shown in the wxBitmapComboBox
642 {
643 bm_combo->SetString( selected, item->GetColor( sub_item ) );
644 wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
645 LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D(), custom_color );
646 bm_combo->SetItemBitmap( selected, layerbmp );
647 }
648 }
649 else
650 {
651 if( bm_combo )
652 {
653 // Note: don't use bm_combo->FindString() because the combo strings are
654 // translated.
655 for( size_t ii = 0; ii < GetStandardColors( item->GetType() ).size(); ii++ )
656 {
657 if( GetStandardColorName( item->GetType(), ii ) == item->GetColor( sub_item ) )
658 {
659 selected = ii;
660 break;
661 }
662 }
663 }
664 }
665
666 if( bm_combo )
667 bm_combo->SetSelection( selected );
668 }
669
670 if( item->HasEpsilonRValue() )
671 {
672 wxString txt = UIDouble2Str( item->GetEpsilonR( sub_item ) );
673 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_EpsilonCtrl );
674
675 if( textCtrl )
676 textCtrl->ChangeValue( txt );
677 }
678
679 if( item->HasLossTangentValue() )
680 {
681 wxString txt = UIDouble2Str( item->GetLossTangent( sub_item ) );
682 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_LossTgCtrl );
683
684 if( textCtrl )
685 textCtrl->ChangeValue( txt );
686 }
687 }
688
689 // Now enable/disable stackup items, according to the m_enabledLayers config
691
693}
694
695
697{
698 // Now enable/disable stackup items, according to the m_enabledLayers config
699 // Calculate copper layer count from m_enabledLayers, and *do not use* brd_stackup
700 // for that, because it is not necessary up to date
701 // (for instance after modifying the layer count from the panel layers in dialog)
703 int copperLayersCount = copperMask.count();
704
706 {
707 bool show_item;
708 BOARD_STACKUP_ITEM* item = ui_row_item.m_Item;
709
710 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
711 // the m_DielectricLayerId is not a copper layer id, it is a dielectric idx from 1
712 show_item = item->GetDielectricLayerId() < copperLayersCount;
713 else
714 show_item = m_enabledLayers[item->GetBrdLayerId()];
715
716 item->SetEnabled( show_item );
717
718 ui_row_item.m_isEnabled = show_item;
719
720 if( show_item && !ui_row_item.m_Icon )
721 lazyBuildRowUI( ui_row_item );
722
723 if( ui_row_item.m_Icon )
724 {
725 // Show or not items of this row:
726 ui_row_item.m_Icon->Show( show_item );
727 ui_row_item.m_LayerName->Show( show_item );
728 ui_row_item.m_LayerTypeCtrl->Show( show_item );
729 ui_row_item.m_MaterialCtrl->Show( show_item );
730
731 if( ui_row_item.m_MaterialButt )
732 ui_row_item.m_MaterialButt->Show( show_item );
733
734 ui_row_item.m_ThicknessCtrl->Show( show_item );
735 ui_row_item.m_ThicknessLockCtrl->Show( show_item );
736 ui_row_item.m_ColorCtrl->Show( show_item );
737 ui_row_item.m_EpsilonCtrl->Show( show_item );
738 ui_row_item.m_LossTgCtrl->Show( show_item );
739 }
740 }
741}
742
743
744void PANEL_SETUP_BOARD_STACKUP::addMaterialChooser( wxWindowID aId, const wxString* aMaterialName,
745 BOARD_STACKUP_ROW_UI_ITEM& aUiRowItem )
746{
747 wxBoxSizer* bSizerMat = new wxBoxSizer( wxHORIZONTAL );
748 m_fgGridSizer->Add( bSizerMat, 1, wxRIGHT|wxEXPAND, 4 );
749 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY );
750
751 if( aMaterialName )
752 {
753 if( IsPrmSpecified( *aMaterialName ) )
754 textCtrl->ChangeValue( *aMaterialName );
755 else
756 textCtrl->ChangeValue( wxGetTranslation( NotSpecifiedPrm() ) );
757 }
758
759 textCtrl->SetMinSize( m_numericTextCtrlSize );
760 bSizerMat->Add( textCtrl, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
761
762 wxButton* m_buttonMat = new wxButton( m_scGridWin, aId, _( "..." ), wxDefaultPosition,
763 wxDefaultSize, wxBU_EXACTFIT );
764 bSizerMat->Add( m_buttonMat, 0, wxALIGN_CENTER_VERTICAL, 2 );
765
766 m_buttonMat->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
767 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onMaterialChange ),
768 nullptr, this );
769 m_controlItemsList.push_back( m_buttonMat );
770
771 aUiRowItem.m_MaterialCtrl = textCtrl;
772 aUiRowItem.m_MaterialButt = m_buttonMat;
773}
774
775
777{
778 wxStaticText* emptyText = new wxStaticText( m_scGridWin, wxID_ANY, wxEmptyString );
779 m_fgGridSizer->Add( emptyText, 0, wxALIGN_CENTER_VERTICAL );
780 return emptyText;
781}
782
783
785{
786 BOARD_STACKUP_ITEM* item = ui_row_item.m_Item;
787 int sublayerIdx = ui_row_item.m_SubItem;
788 int row = ui_row_item.m_Row;
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 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
797 {
798 wxString lname = item->FormatDielectricLayerName();
799
800 if( item->GetSublayersCount() > 1 )
801 {
802 lname << wxT( " (" ) << sublayerIdx +1 << wxT( "/" )
803 << item->GetSublayersCount() << wxT( ")" );
804 }
805
806 wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname );
807 m_fgGridSizer->Add( st_text, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
808 ui_row_item.m_LayerName = st_text;
809
810 // For a dielectric layer, the layer type choice is not for each sublayer,
811 // only for the first (sublayerIdx = 0), and is common to all sublayers
812 if( sublayerIdx == 0 )
813 {
814 wxChoice* choice = new wxChoice( m_scGridWin, wxID_ANY, wxDefaultPosition,
815 wxDefaultSize, m_core_prepreg_choice );
816 choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 );
817 m_fgGridSizer->Add( choice, 1, wxEXPAND|wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
818
819 ui_row_item.m_LayerTypeCtrl = choice;
820 }
821 else
822 {
823 ui_row_item.m_LayerTypeCtrl = addSpacer();
824 }
825 }
826 else
827 {
828 item->SetLayerName( m_board->GetLayerName( item->GetBrdLayerId() ) );
829 wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, item->GetLayerName() );
830 m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 1 );
831 st_text->Show( true );
832 ui_row_item.m_LayerName = st_text;
833
834 wxString lname;
835
836 if( item->GetTypeName() == KEY_COPPER )
837 lname = _( "Copper" );
838 else
839 lname = wxGetTranslation( item->GetTypeName() );
840
841 st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname );
842 m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
843 ui_row_item.m_LayerTypeCtrl = st_text;
844 }
845
846 if( item->IsMaterialEditable() )
847 {
848 wxString matName = item->GetMaterial( sublayerIdx );
849 addMaterialChooser( ID_ITEM_MATERIAL+row, &matName, ui_row_item );
850 }
851 else
852 {
853 ui_row_item.m_MaterialCtrl = addSpacer();
854 }
855
856 if( item->IsThicknessEditable() )
857 {
858 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, ID_ITEM_THICKNESS+row );
859 textCtrl->SetMinSize( m_numericTextCtrlSize );
860 textCtrl->ChangeValue( m_frame->StringFromValue( item->GetThickness( sublayerIdx ), true ) );
861 m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
862 m_controlItemsList.push_back( textCtrl );
863 textCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED,
864 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ),
865 nullptr, this );
866 ui_row_item.m_ThicknessCtrl = textCtrl;
867
868 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
869 {
870 wxCheckBox* cb_box = new wxCheckBox( m_scGridWin, ID_ITEM_THICKNESS_LOCKED+row,
871 wxEmptyString );
872 cb_box->SetValue( item->IsThicknessLocked( sublayerIdx ) );
873 m_fgGridSizer->Add( cb_box, 0, wxALIGN_CENTER_VERTICAL, 2 );
874 ui_row_item.m_ThicknessLockCtrl = cb_box;
875 }
876 else
877 {
878 ui_row_item.m_ThicknessLockCtrl = addSpacer();
879 }
880 }
881 else
882 {
883 ui_row_item.m_ThicknessCtrl = addSpacer();
884 ui_row_item.m_ThicknessLockCtrl = addSpacer();
885 }
886
887 if( item->IsColorEditable() )
888 {
889 if( item->GetColor( sublayerIdx ).StartsWith( wxT( "#" ) ) ) // User defined color
890 {
891 ui_row_item.m_UserColor = COLOR4D( item->GetColor( sublayerIdx ) ).ToColour();
892 }
893 else
894 ui_row_item.m_UserColor = GetDefaultUserColor( item->GetType() );
895
896 wxBitmapComboBox* bm_combo = createColorBox( item, row );
897 int selected = 0; // The "not specified" item
898
899 m_fgGridSizer->Add( bm_combo, 1, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 2 );
900
901 if( item->GetColor( sublayerIdx ).StartsWith( wxT( "#" ) ) )
902 {
903 selected = GetColorUserDefinedListIdx( item->GetType() );
904 bm_combo->SetString( selected, item->GetColor( sublayerIdx ) );
905 }
906 else
907 {
908 // Note: don't use bm_combo->FindString() because the combo strings are translated.
909 for( size_t ii = 0; ii < GetStandardColors( item->GetType() ).size(); ii++ )
910 {
911 if( GetStandardColorName( item->GetType(), ii ) == item->GetColor( sublayerIdx ) )
912 {
913 selected = ii;
914 break;
915 }
916 }
917 }
918
919 bm_combo->SetSelection( selected );
920 ui_row_item.m_ColorCtrl = bm_combo;
921 }
922 else
923 {
924 ui_row_item.m_ColorCtrl = addSpacer();
925 }
926
927 if( item->HasEpsilonRValue() )
928 {
929 wxString txt = UIDouble2Str( item->GetEpsilonR( sublayerIdx ) );
930 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString,
931 wxDefaultPosition, m_numericFieldsSize );
932 textCtrl->ChangeValue( txt );
933 m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
934 ui_row_item.m_EpsilonCtrl = textCtrl;
935 }
936 else
937 {
938 ui_row_item.m_EpsilonCtrl = addSpacer();
939 }
940
941 if( item->HasLossTangentValue() )
942 {
943 wxString txt = UIDouble2Str( item->GetLossTangent( sublayerIdx ) );;
944 wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString,
945 wxDefaultPosition, m_numericFieldsSize );
946 textCtrl->ChangeValue( txt );
947 m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
948 ui_row_item.m_LossTgCtrl = textCtrl;
949 }
950 else
951 {
952 ui_row_item.m_LossTgCtrl = addSpacer();
953 }
954}
955
956
958{
959 wxWindowUpdateLocker locker( m_scGridWin );
960 m_scGridWin->Hide();
961
962 // Rebuild the stackup for the dialog, after dielectric parameters list is modified
963 // (added/removed):
964
965 // First, delete all ui objects, because wxID values will be no longer valid for many widgets
967 m_controlItemsList.clear();
968
969 // Delete widgets (handled by the wxPanel parent)
971 {
972 // This remove and delete the current ui_item.m_MaterialCtrl sizer
973 if( ui_item.m_MaterialCtrl )
974 ui_item.m_MaterialCtrl->SetSizer( nullptr );
975
976 // Delete other widgets
977 delete ui_item.m_Icon; // Color icon in first column (column 1)
978 delete ui_item.m_LayerName; // string shown in column 2
979 delete ui_item.m_LayerTypeCtrl; // control shown in column 3
980 delete ui_item.m_MaterialCtrl; // control shown in column 4, with m_MaterialButt
981 delete ui_item.m_MaterialButt; // control shown in column 4, with m_MaterialCtrl
982 delete ui_item.m_ThicknessCtrl; // control shown in column 5
983 delete ui_item.m_ThicknessLockCtrl;// control shown in column 6
984 delete ui_item.m_ColorCtrl; // control shown in column 7
985 delete ui_item.m_EpsilonCtrl; // control shown in column 8
986 delete ui_item.m_LossTgCtrl; // control shown in column 9
987 }
988
989 m_rowUiItemsList.clear();
990
991 // In order to recreate a clean grid layer list, we have to delete and
992 // recreate the sizer m_fgGridSizer (just deleting items in this size is not enough)
993 // therefore we also have to add the "old" title items to the newly recreated m_fgGridSizer:
994 m_scGridWin->SetSizer( nullptr ); // This remove and delete the current m_fgGridSizer
995
996 m_fgGridSizer = new wxFlexGridSizer( 0, 9, 0, 2 );
997 m_fgGridSizer->SetFlexibleDirection( wxHORIZONTAL );
998 m_fgGridSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
999 m_fgGridSizer->SetHGap( 6 );
1000 m_scGridWin->SetSizer( m_fgGridSizer );
1001
1002 // Re-add "old" title items:
1003 const int sizer_flags = wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_CENTER_HORIZONTAL;
1004 m_fgGridSizer->Add( m_staticTextLayer, 0, sizer_flags, 2 );
1005 m_fgGridSizer->Add( m_staticTextType, 0, sizer_flags, 2 );
1006 m_fgGridSizer->Add( m_staticTextLayerId, 0, sizer_flags, 5 );
1007 m_fgGridSizer->Add( m_staticTextMaterial, 0, sizer_flags, 2 );
1008 m_fgGridSizer->Add( m_staticTextThickness, 0, sizer_flags, 2 );
1009 m_fgGridSizer->Add( m_bitmapLockThickness, 0, sizer_flags, 1 );
1010 m_fgGridSizer->Add( m_staticTextColor, 0, sizer_flags, 2 );
1011 m_fgGridSizer->Add( m_staticTextEpsilonR, 0, sizer_flags, 2 );
1012 m_fgGridSizer->Add( m_staticTextLossTg, 0, sizer_flags, 2 );
1013
1014
1015 // Now, rebuild the widget list from the new m_stackup items:
1016 buildLayerStackPanel( false, aRelinkItems );
1017
1018 // Now enable/disable stackup items, according to the m_enabledLayers config
1020
1022
1023 m_scGridWin->Layout();
1024 m_scGridWin->Show();
1025}
1026
1027
1029 bool aRelinkStackup )
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( aCreateInitialStackup || aRelinkStackup )
1034 {
1035 if( aCreateInitialStackup )
1036 {
1037 // Creates a full BOARD_STACKUP with 32 copper layers.
1038 // extra layers will be hidden later.
1039 // but if the number of layer is changed in the dialog, the corresponding
1040 // widgets will be available with their previous values.
1042 }
1043
1044 const BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor();
1045
1046 // Now initialize all stackup items to the board values, when exist
1047 for( BOARD_STACKUP_ITEM* item: m_stackup.GetList() )
1048 {
1049 // Search for board settings:
1050 for( BOARD_STACKUP_ITEM* board_item: brd_stackup.GetList() )
1051 {
1052 if( item->GetBrdLayerId() != UNDEFINED_LAYER )
1053 {
1054 if( item->GetBrdLayerId() == board_item->GetBrdLayerId() )
1055 {
1056 *item = *board_item;
1057 break;
1058 }
1059 }
1060 else // dielectric layer: see m_DielectricLayerId for identification
1061 {
1062 // Compare dielectric layer with dielectric layer
1063 if( board_item->GetBrdLayerId() != UNDEFINED_LAYER )
1064 continue;
1065
1066 if( item->GetDielectricLayerId() == board_item->GetDielectricLayerId() )
1067 {
1068 *item = *board_item;
1069 break;
1070 }
1071 }
1072 }
1073 }
1074 }
1075
1076 int row = 0;
1077
1078 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1079 {
1080 for( int sub_idx = 0; sub_idx < item->GetSublayersCount(); sub_idx++ )
1081 {
1082 m_rowUiItemsList.emplace_back( item, sub_idx, row );
1083 row++;
1084 }
1085 }
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( 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(), sub_item );
1216 else
1217 item->SetColor( GetStandardColorName( item->GetType(), idx ), sub_item );
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 STRING_FORMATTER old_stackup;
1247
1248 // FormatBoardStackup() (using FormatInternalUnits()) expects a "C" locale
1249 // to execute some tests. So switch to the suitable locale
1251 brd_stackup.FormatBoardStackup( &old_stackup, m_board, 0 );
1252
1253 // copy enabled items to the new board stackup
1254 brd_stackup.RemoveAll();
1255
1256 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1257 {
1258 if( item->IsEnabled() )
1259 brd_stackup.Add( new BOARD_STACKUP_ITEM( *item ) );
1260 }
1261
1262 STRING_FORMATTER new_stackup;
1263 brd_stackup.FormatBoardStackup( &new_stackup, m_board, 0 );
1264
1265 bool modified = old_stackup.GetString() != new_stackup.GetString();
1266 int thickness = brd_stackup.BuildBoardThicknessFromStackup();
1267
1268 if( m_brdSettings->GetBoardThickness() != thickness )
1269 {
1270 m_brdSettings->SetBoardThickness( thickness );
1271 modified = true;
1272 }
1273
1274 if( brd_stackup.m_HasDielectricConstrains != m_impedanceControlled->GetValue() )
1275 {
1276 brd_stackup.m_HasDielectricConstrains = m_impedanceControlled->GetValue();
1277 modified = true;
1278 }
1279
1281 {
1283 modified = true;
1284 }
1285
1286 if( modified )
1287 m_frame->OnModify();
1288
1289 return true;
1290}
1291
1292
1294{
1295 BOARD* savedBrd = m_board;
1296 m_board = aBoard;
1297
1298 BOARD_DESIGN_SETTINGS* savedSettings = m_brdSettings;
1299 m_brdSettings = &aBoard->GetDesignSettings();
1300
1302
1303 rebuildLayerStackPanel( true );
1304 synchronizeWithBoard( true );
1306
1307 m_brdSettings = savedSettings;
1308 m_board = savedBrd;
1309}
1310
1311
1313{
1314 // Can be called spuriously from events before the layers page is even created
1316 return;
1317
1318 // First, verify the list of layers currently in stackup:
1319 // if it does not mach the list of layers set in PANEL_SETUP_LAYERS
1320 // rebuild the panel
1321
1322 // the current enabled layers in PANEL_SETUP_LAYERS
1323 // Note: the number of layer can change, but not the layers properties
1325
1326 if( m_enabledLayers != layersList )
1327 {
1328 m_enabledLayers = layersList;
1329
1330 synchronizeWithBoard( false );
1331
1332 Layout();
1333 Refresh();
1334 }
1335}
1336
1337
1339{
1340 int idx = event.GetSelection();
1341 int item_id = event.GetId();
1342 int row = item_id - ID_ITEM_COLOR;
1343 BOARD_STACKUP_ITEM* item = m_rowUiItemsList[row].m_Item;
1344
1345 if( IsCustomColorIdx( item->GetType(), idx ) ) // user color is the last option in list
1346 {
1347 DIALOG_COLOR_PICKER dlg( this, m_rowUiItemsList[row].m_UserColor, true, nullptr,
1348 GetDefaultUserColor( m_rowUiItemsList[row].m_Item->GetType() ) );
1349
1350#ifdef __WXGTK__
1351 // Give a time-slice to close the menu before opening the dialog.
1352 // (Only matters on some versions of GTK.)
1353 wxSafeYield();
1354#endif
1355
1356 if( dlg.ShowModal() == wxID_OK )
1357 {
1358 wxBitmapComboBox* combo = static_cast<wxBitmapComboBox*>( FindWindowById( item_id ) );
1359 COLOR4D color = dlg.GetColor();
1360
1361 m_rowUiItemsList[row].m_UserColor = color;
1362
1363 combo->SetString( idx, color.ToHexString() );
1364
1365 wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
1366 LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), color );
1367 combo->SetItemBitmap( combo->GetCount() - 1, layerbmp );
1368
1369 combo->SetSelection( idx );
1370 }
1371 }
1372
1373 updateIconColor( row );
1374}
1375
1376
1378{
1379 // Ensure m_materialList contains all materials already in use in stackup list
1380 // and add it is missing
1382 return;
1383
1384 for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1385 {
1386 DIELECTRIC_SUBSTRATE_LIST* mat_list = nullptr;
1387
1388 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1389 mat_list = &m_delectricMatList;
1390 else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK )
1391 mat_list = &m_solderMaskMatList;
1392 else if( item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
1393 mat_list = &m_silkscreenMatList;
1394
1395 else
1396 continue;
1397
1398 for( int ii = 0; ii < item->GetSublayersCount(); ii++ )
1399 {
1400 int idx = mat_list->FindSubstrate( item->GetMaterial( ii ),
1401 item->GetEpsilonR( ii ),
1402 item->GetLossTangent( ii ) );
1403
1404 if( idx < 0 && !item->GetMaterial().IsEmpty() )
1405 {
1406 // This material is not in list: add it
1407 DIELECTRIC_SUBSTRATE new_mat;
1408 new_mat.m_Name = item->GetMaterial( ii );
1409 new_mat.m_EpsilonR = item->GetEpsilonR( ii );
1410 new_mat.m_LossTangent = item->GetLossTangent( ii );
1411 mat_list->AppendSubstrate( new_mat );
1412 }
1413 }
1414 }
1415
1416 int row = event.GetId() - ID_ITEM_MATERIAL;
1417 BOARD_STACKUP_ITEM* item = m_rowUiItemsList[row].m_Item;
1418 int sub_item = m_rowUiItemsList[row].m_SubItem;
1419 DIELECTRIC_SUBSTRATE_LIST* item_mat_list = nullptr;
1420
1421 switch( item->GetType() )
1422 {
1423 case BS_ITEM_TYPE_DIELECTRIC: item_mat_list = &m_delectricMatList; break;
1424 case BS_ITEM_TYPE_SOLDERMASK: item_mat_list = &m_solderMaskMatList; break;
1425 case BS_ITEM_TYPE_SILKSCREEN: item_mat_list = &m_silkscreenMatList; break;
1426 default: item_mat_list = nullptr; break;
1427 }
1428
1429 wxCHECK( item_mat_list, /* void */ );
1430
1431 DIALOG_DIELECTRIC_MATERIAL dlg( this, *item_mat_list );
1432
1433 if( dlg.ShowModal() != wxID_OK )
1434 return;
1435
1437
1438 if( substrate.m_Name.IsEmpty() ) // No substrate specified
1439 return;
1440
1441 // Update Name, Epsilon R and Loss tg
1442 item->SetMaterial( substrate.m_Name, sub_item );
1443 item->SetEpsilonR( substrate.m_EpsilonR, sub_item );
1444 item->SetLossTangent( substrate.m_LossTangent, sub_item );
1445
1446 wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_MaterialCtrl );
1447 textCtrl->ChangeValue( item->GetMaterial( sub_item ) );
1448
1449 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC
1450 && !item->GetColor( sub_item ).StartsWith( "#" ) /* User defined color */ )
1451 {
1452 if( substrate.m_Name.IsSameAs( "PTFE" )
1453 || substrate.m_Name.IsSameAs( "Teflon" ) )
1454 {
1455 item->SetColor( "PTFE natural", sub_item );
1456 }
1457 else if( substrate.m_Name.IsSameAs( "Polyimide" )
1458 || substrate.m_Name.IsSameAs( "Kapton" ) )
1459 {
1460 item->SetColor( "Polyimide", sub_item );
1461 }
1462 else if( substrate.m_Name.IsSameAs( "Al" ) )
1463 {
1464 item->SetColor( "Aluminum", sub_item );
1465 }
1466 else
1467 {
1468 item->SetColor( "FR4 natural", sub_item );
1469 }
1470 }
1471
1472 wxBitmapComboBox* picker = static_cast<wxBitmapComboBox*>( m_rowUiItemsList[row].m_ColorCtrl );
1473
1474 for( size_t ii = 0; ii < GetStandardColors( item->GetType() ).size(); ii++ )
1475 {
1476 if( GetStandardColorName( item->GetType(), ii ) == item->GetColor( sub_item ) )
1477 {
1478 picker->SetSelection( ii );
1479 break;
1480 }
1481 }
1482
1483 // some layers have a material choice but not EpsilonR ctrl
1484 if( item->HasEpsilonRValue() )
1485 {
1486 textCtrl = dynamic_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_EpsilonCtrl );
1487
1488 if( textCtrl )
1489 textCtrl->ChangeValue( item->FormatEpsilonR( sub_item ) );
1490 }
1491
1492 // some layers have a material choice but not loss tg ctrl
1493 if( item->HasLossTangentValue() )
1494 {
1495 textCtrl = dynamic_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_LossTgCtrl );
1496
1497 if( textCtrl )
1498 textCtrl->ChangeValue( item->FormatLossTangent( sub_item ) );
1499 }
1500}
1501
1502
1504{
1505 int row = event.GetId() - ID_ITEM_THICKNESS;
1506 wxString value = event.GetString();
1507
1508 BOARD_STACKUP_ITEM* item = GetStackupItem( row );
1509 int idx = GetSublayerId( row );
1510
1511 item->SetThickness( m_frame->ValueFromString( value ), idx );
1512
1514}
1515
1516
1518{
1519 return m_rowUiItemsList[aRow].m_Item;
1520}
1521
1522
1524{
1525 return m_rowUiItemsList[aRow].m_SubItem;
1526}
1527
1528
1530{
1531 BOARD_STACKUP_ITEM* st_item = dynamic_cast<BOARD_STACKUP_ITEM*>( GetStackupItem( aRow ) );
1532
1533 wxASSERT( st_item );
1534 wxColor color;
1535
1536 if( ! st_item )
1537 return color;
1538
1539 switch( st_item->GetType() )
1540 {
1541 case BS_ITEM_TYPE_COPPER: color = copperColor; break;
1543 case BS_ITEM_TYPE_SOLDERMASK: color = GetSelectedColor( aRow ); break;
1544 case BS_ITEM_TYPE_SILKSCREEN: color = GetSelectedColor( aRow ); break;
1546
1547 default:
1549 wxFAIL_MSG( wxT( "PANEL_SETUP_BOARD_STACKUP::getColorIconItem: unrecognized item type" ) );
1550 break;
1551 }
1552
1553 wxASSERT_MSG( color.IsOk(), wxT( "Invalid color in PCB stackup" ) );
1554
1555 return color;
1556}
1557
1558
1560{
1561 // explicit depth important under MSW. We use R,V,B 24 bits/pixel bitmap
1562 const int bitmap_depth = 24;
1563
1564 if( aRow >= 0 )
1565 {
1566 wxStaticBitmap* st_bitmap = m_rowUiItemsList[aRow].m_Icon;
1567
1568 wxBitmap bmp( m_colorIconsSize.x, m_colorIconsSize.y / 2, bitmap_depth );
1569 drawBitmap( bmp, getColorIconItem( aRow ) );
1570 st_bitmap->SetBitmap( bmp );
1571 return;
1572 }
1573
1574 for( unsigned row = 0; row < m_rowUiItemsList.size(); row++ )
1575 {
1576 if( m_rowUiItemsList[row].m_Icon )
1577 {
1578 wxBitmap bmp( m_colorIconsSize.x, m_colorIconsSize.y / 2, bitmap_depth );
1579 drawBitmap( bmp, getColorIconItem( row ) );
1580 m_rowUiItemsList[row].m_Icon->SetBitmap( bmp );
1581 }
1582 }
1583}
1584
1585
1587 int aRow )
1588{
1589 wxBitmapComboBox* combo = new wxBitmapComboBox( m_scGridWin, ID_ITEM_COLOR + aRow,
1590 wxEmptyString, wxDefaultPosition,
1591 wxDefaultSize, 0, nullptr, wxCB_READONLY );
1592
1593 // Fills the combo box with choice list + bitmaps
1594 BOARD_STACKUP_ITEM_TYPE itemType = aStackupItem ? aStackupItem->GetType()
1596
1597 for( size_t ii = 0; ii < GetStandardColors( itemType ).size(); ii++ )
1598 {
1599 wxString label;
1600 COLOR4D curr_color;
1601
1602 // Defined colors have a name, the user color uses HTML notation ( i.e. #FF000080)
1603 if( IsCustomColorIdx( itemType, ii )
1604 && aStackupItem && aStackupItem->GetColor().StartsWith( wxT( "#" ) ) )
1605 {
1606 label = aStackupItem->GetColor();
1607 curr_color = COLOR4D( label );
1608 }
1609 else
1610 {
1611 label = wxGetTranslation( GetStandardColorName( itemType, ii ) );
1612 curr_color = GetStandardColor( itemType, ii );
1613 }
1614
1615 wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
1616 LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), curr_color );
1617
1618 combo->Append( label, layerbmp );
1619 }
1620
1621 // Ensure the size of the widget is enough to show the text and the icon
1622 // We have to have a selected item when doing this, because otherwise GTK
1623 // will just choose a random size that might not fit the actual data
1624 // (such as in cases where the font size is very large). So we select
1625 // the longest item (which should be the last item), and size it that way.
1626 int sel = combo->GetSelection();
1627 combo->SetSelection( combo->GetCount() - 1 );
1628
1629 combo->SetMinSize( wxSize( -1, -1 ) );
1630 wxSize bestSize = combo->GetBestSize();
1631
1632 bestSize.x = bestSize.x + m_colorSwatchesSize.x;
1633 combo->SetMinSize( bestSize );
1634 combo->SetSelection( sel );
1635
1636 // add the wxBitmapComboBox to wxControl list, to be able to disconnect the event
1637 // on exit
1638 m_controlItemsList.push_back( combo );
1639
1640 combo->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED,
1641 wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onColorSelected ),
1642 nullptr, this );
1643
1644 combo->Bind( wxEVT_COMBOBOX_DROPDOWN,
1645 [combo]( wxCommandEvent& aEvent )
1646 {
1647 combo->SetString( combo->GetCount() - 1, _( "Custom..." ) );
1648 } );
1649
1650 return combo;
1651}
1652
1653
1654void drawBitmap( wxBitmap& aBitmap, wxColor aColor )
1655{
1656 wxNativePixelData data( aBitmap );
1657 wxNativePixelData::Iterator p( data );
1658
1659 for( int yy = 0; yy < data.GetHeight(); yy++ )
1660 {
1661 wxNativePixelData::Iterator rowStart = p;
1662
1663 for( int xx = 0; xx < data.GetWidth(); xx++ )
1664 {
1665 p.Red() = aColor.Red();
1666 p.Green() = aColor.Green();
1667 p.Blue() = aColor.Blue();
1668 ++p;
1669 }
1670
1671 p = rowStart;
1672 p.OffsetY( data, 1 );
1673 }
1674}
1675
1676
int color
Definition: DXF_plotter.cpp:58
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:157
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:91
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
wxString GetColor(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
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 SetColor(const wxString &aColorName, int aDielectricSubLayer=0)
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
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.
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.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:271
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:614
int GetCopperLayerCount() const
Definition: board.cpp:590
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:501
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:731
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
wxColour ToColour() const
Definition: color4d.cpp:220
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:49
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:552
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
static PAGED_DIALOG * GetDialog(wxWindow *aWindow)
Class PANEL_SETUP_BOARD_STACKUP_BASE.
void onAddDielectricLayer(wxCommandEvent &event) override
void lazyBuildRowUI(BOARD_STACKUP_ROW_UI_ITEM &ui_row_item)
Creates the controls in a BOARD_STACKUP_ROW_UI_ITEM relative to the aStackupItem.
DIELECTRIC_SUBSTRATE_LIST m_silkscreenMatList
void onExportToClipboard(wxCommandEvent &event) override
bool transferDataFromUIToStackup()
Transfer current UI settings to m_stackup but not to the board.
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.
PANEL_SETUP_BOARD_STACKUP(wxWindow *aParentWindow, PCB_EDIT_FRAME *aFrame, PANEL_SETUP_LAYERS *aPanelLayers)
void synchronizeWithBoard(bool aFullSync)
Synchronize the full stackup shown in m_fgGridSizer according to the stackup of the current board and...
void buildLayerStackPanel(bool aCreateInitialStackup=false, bool aRelinkStackup=false)
Populate m_fgGridSizer with items to handle stackup parameters This is a full list: all copper layers...
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
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 onMaterialChange(wxCommandEvent &event)
void rebuildLayerStackPanel(bool aRelinkItems=false)
Populate m_fgGridSizer with items to handle stackup parameters If previous items are in list,...
bool IsInitialized() const
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:427
const std::string & GetString()
Definition: richio.h:450
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
void SetBorders(bool aLeft, bool aRight, bool aTop, bool aBottom)
Definition: wx_panel.h:39
#define _(s)
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:65
This file contains miscellaneous commonly used macros and functions.
KICOMMON_API 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:101
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...
std::vector< FAB_LAYER_COLOR > dummy
const std::vector< FAB_LAYER_COLOR > & GetStandardColors(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