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