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