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