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