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