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