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