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