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