KiCad PCB EDA Suite
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-2020 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 <convert_to_biu.h>
26 #include <macros.h> // arrayDim definition
27 #include <pcb_edit_frame.h>
28 #include <board.h>
30 #include <widgets/paged_dialog.h>
32 #include <wx/rawbmp.h>
33 #include <math/util.h> // for KiROUND
34 
35 #include "panel_board_stackup.h"
36 #include <panel_setup_layers.h>
37 #include "board_stackup_reporter.h"
38 #include <bitmaps.h>
39 #include <wx/clipbrd.h>
40 #include <wx/dataobj.h>
42 #include <wx/wupdlock.h>
43 #include <wx/richmsgdlg.h>
44 #include <wx/choicdlg.h>
45 #include <wx/dcclient.h>
46 
47 // Some wx widget ID to know what widget has fired a event:
48 #define ID_INCREMENT 256 // space between 2 ID type. Bigger than the layer count max
49 
50 // The actual widget IDs are the base id + the row index.
51 // they are used in events to know the row index of the control that fired the event
53 {
54  ID_ITEM_MATERIAL = 10000, // Be sure it is higher than other IDs
55  // used in the board setup dialog
59 };
60 
61 // Default colors to draw icons:
62 static wxColor copperColor( 220, 180, 30 );
63 static wxColor dielectricColor( 75, 120, 75 );
64 static wxColor pasteColor( 200, 200, 200 );
65 
66 static void drawBitmap( wxBitmap& aBitmap, wxColor aColor );
67 
68 
70  PANEL_SETUP_LAYERS* aPanelLayers ):
71  PANEL_SETUP_BOARD_STACKUP_BASE( aParent->GetTreebook() ),
72  m_delectricMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_DIELECTRIC ),
73  m_solderMaskMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_SOLDERMASK ),
74  m_silkscreenMatList( DIELECTRIC_SUBSTRATE_LIST::DL_MATERIAL_SILKSCREEN )
75 {
76  m_parentDialog = aParent;
77  m_frame = aFrame;
78  m_panelLayers = aPanelLayers;
81  m_units = aFrame->GetUserUnits();
82 
84 
86 
87  // Calculates a good size for color swatches (icons) in this dialog
88  wxClientDC dc( this );
89  m_colorSwatchesSize = dc.GetTextExtent( "XX" );
90  m_colorComboSize = dc.GetTextExtent( wxString::Format( "XXX %s XXX",
91  wxGetTranslation( NotSpecifiedPrm() ) ) );
92  m_colorIconsSize = dc.GetTextExtent( "XXXX" );
93 
94  // Calculates a good size for wxTextCtrl to enter Epsilon R and Loss tan
95  // ("0.000000" + margins)
96  m_numericFieldsSize = dc.GetTextExtent( "X.XXXXXXX" );
97  m_numericFieldsSize.y = -1; // Use default for the vertical size
98 
99  // Calculates a minimal size for wxTextCtrl to enter a dim with units
100  // ("000.0000000 mils" + margins)
101  m_numericTextCtrlSize = dc.GetTextExtent( "XXX.XXXXXXX mils" );
102  m_numericTextCtrlSize.y = -1; // Use default for the vertical size
103 
104  // The grid column containing the lock checkbox is kept to a minimal
105  // size. So we use a wxStaticBitmap: set the bitmap itself
106  m_bitmapLockThickness->SetBitmap( KiScaledBitmap( locked_xpm, aFrame ) );
107 
108  // Gives a minimal size of wxTextCtrl showing dimensions+units
109  m_tcCTValue->SetMinSize( m_numericTextCtrlSize );
110 
111  // Prepare dielectric layer type: layer type keyword is "core" or "prepreg"
112  m_core_prepreg_choice.Add( _( "Core" ) );
113  m_core_prepreg_choice.Add( _( "PrePreg" ) );
114 
115  buildLayerStackPanel( true );
116  synchronizeWithBoard( true );
117 
118  m_choiceCopperLayers->Bind( wxEVT_CHOICE,
119  [&]( wxCommandEvent& )
120  {
123  Layout();
124  } );
125 }
126 
127 
129 {
131 }
132 
133 
135 {
136  // Disconnect Events connected to items in m_controlItemsList
137  for( wxControl* item: m_controlItemsList )
138  {
139  wxBitmapComboBox* cb = dynamic_cast<wxBitmapComboBox*>( item );
140 
141  if( cb )
142  cb->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED,
143  wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onColorSelected ),
144  NULL, this );
145 
146  wxButton* matButt = dynamic_cast<wxButton*>( item );
147 
148  if( matButt )
149  matButt->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED,
150  wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onMaterialChange ),
151  NULL, this );
152 
153  wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( item );
154 
155  if( textCtrl )
156  textCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED,
157  wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ),
158  NULL, this );
159  }
160 }
161 
162 
164 {
165  // Build Dielectric layers list:
166  wxArrayString d_list;
167  std::vector<int> rows; // indexes of row values for each selectable item
168  int row = -1;
169 
171  {
172  row++;
173 
174  if( !item.m_isEnabled )
175  continue;
176 
177  BOARD_STACKUP_ITEM* brd_stackup_item = item.m_Item;
178 
179  if( brd_stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
180  {
181  if( brd_stackup_item->GetSublayersCount() > 1 )
182  {
183  d_list.Add( wxString::Format( _( "Layer \"%s\" (sublayer %d/%d)" ),
184  brd_stackup_item->FormatDielectricLayerName(),
185  item.m_SubItem+1, brd_stackup_item->GetSublayersCount() ) );
186  }
187  else
188  d_list.Add( brd_stackup_item->FormatDielectricLayerName() );
189 
190  rows.push_back( row );
191  }
192  }
193 
194  // Show list
195  int index = wxGetSingleChoiceIndex( _( "Select dielectric layer to add to board stack up." ),
196  _("Dielectric Layers List"),
197  d_list);
198 
199  if( index < 0 )
200  return;
201 
202  row = rows[index];
203 
204  BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[row].m_Item;
205  int new_sublayer = m_rowUiItemsList[row].m_SubItem;
206 
207  // Insert a new item after the selected item
208  brd_stackup_item->AddDielectricPrms( new_sublayer+1 );
209 
211 }
212 
213 
215 {
216  // Build deletable Dielectric layers list.
217  // A layer can be deleted if there are 2 (or more) dielectric sub-layers
218  // between 2 copper layers
219  wxArrayString d_list;
220  std::vector<int> rows; // indexes of row values for each selectable item
221 
222  int ui_row = 0; // The row index in m_rowUiItemsList of items in choice list
223 
224  // Build the list of dielectric layers:
225  for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
226  {
227  if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC ||
228  item->GetSublayersCount() <= 1 )
229  {
230  ui_row++;
231  continue;
232  }
233 
234  for( int ii = 0; ii < item->GetSublayersCount(); ii++ )
235  {
236  d_list.Add( wxString::Format( "Layer \"%s\" sublayer %d/%d",
237  item->FormatDielectricLayerName(), ii+1,
238  item->GetSublayersCount() ) );
239 
240  rows.push_back( ui_row++ );
241  }
242  }
243 
244  // Show choice list
245  int index = wxGetSingleChoiceIndex( _( "Select dielectric layer to remove from board stack up." ),
246  _( "Dielectric Layers" ),
247  d_list );
248 
249  if( index < 0 )
250  return;
251 
252  ui_row = rows[index];
253 
254  BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[ui_row].m_Item;
255  int sublayer = m_rowUiItemsList[ui_row].m_SubItem;
256 
257  // Remove the selected sub item for the selected dielectric layer
258  brd_stackup_item->RemoveDielectricPrms( sublayer );
259 
261 }
262 
263 
264 void PANEL_SETUP_BOARD_STACKUP::onRemoveDielUI( wxUpdateUIEvent& event )
265 {
266  // The m_buttonRemoveDielectricLayer wxButton is enabled only if a dielectric
267  // layer can be removed, i.e. if dielectric layers have sublayers
268  for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
269  {
270  if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC )
271  continue;
272 
273  if( item->GetSublayersCount() > 1 )
274  {
275  m_buttonRemoveDielectricLayer->Enable( true );
276  return;
277  }
278  }
279 
280  m_buttonRemoveDielectricLayer->Enable( false );
281 }
282 
283 
285 {
287  return;
288 
289  // Build a ascii representation of stackup and copy it in the clipboard
290  wxString report = BuildStackupReport( m_stackup, m_units );
291 
292  wxLogNull doNotLog; // disable logging of failed clipboard actions
293 
294  if( wxTheClipboard->Open() )
295  {
296  // This data objects are held by the clipboard,
297  // so do not delete them in the app.
298  wxTheClipboard->SetData( new wxTextDataObject( report ) );
299  wxTheClipboard->Close();
300  }
301 }
302 
303 
305 {
306  wxBitmapComboBox* choice = dynamic_cast<wxBitmapComboBox*>( m_rowUiItemsList[aRow].m_ColorCtrl );
307  wxASSERT( choice );
308 
309  int idx = choice ? choice->GetSelection() : 0;
310 
311  if( idx != GetColorUserDefinedListIdx() ) // a standard color is selected
312  return GetColorStandardList()[idx].m_Color;
313  else if( m_UserColors.count( aRow ) )
314  return m_UserColors.at( aRow );
315  else
316  return wxNullColour;
317 }
318 
319 
321 {
322  int thickness = 0;
323 
325  {
326  BOARD_STACKUP_ITEM* item = ui_item.m_Item;
327 
328  if( !item->IsThicknessEditable() || !ui_item.m_isEnabled )
329  continue;
330 
331  wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
332  wxString txt = textCtrl->GetValue();
333 
334  int item_thickness = ValueFromString( m_frame->GetUserUnits(), txt );
335  thickness += item_thickness;
336  }
337 
338  wxString thicknessStr = StringFromValue( m_units, thickness, true );
339 
340  if( m_tcCTValue->GetValue() != thicknessStr )
341  m_tcCTValue->SetValue( thicknessStr );
342 }
343 
344 
346 {
347  return ( m_choiceCopperLayers->GetSelection() + 1 ) * 2;
348 }
349 
350 
352 {
353  int copperCount = GetCopperLayerCount();
354 
355  wxASSERT( copperCount >= 2 );
356 
358  m_enabledLayers &= ~LSET::InternalCuMask();
359 
360  for( int i = 1; i < copperCount - 1; i++ )
361  m_enabledLayers.set( F_Cu + i );
362 }
363 
364 
366 {
367  const BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor();
368 
369  if( aFullSync )
370  {
371  m_choiceCopperLayers->SetSelection( ( m_board->GetCopperLayerCount() / 2 ) - 1 );
372  m_impedanceControlled->SetValue( brd_stackup.m_HasDielectricConstrains );
373  }
374 
375  int row = 0;
376 
377  for( BOARD_STACKUP_ROW_UI_ITEM& ui_row_item : m_rowUiItemsList )
378  {
379  BOARD_STACKUP_ITEM* item = ui_row_item.m_Item;
380  int sub_item = ui_row_item.m_SubItem;
381 
382  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
383  {
384  wxChoice* choice = dynamic_cast<wxChoice*>( ui_row_item.m_LayerTypeCtrl );
385 
386  if( choice )
387  choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 );
388  }
389 
390  if( item->IsMaterialEditable() )
391  {
392  wxTextCtrl* matName = dynamic_cast<wxTextCtrl*>( ui_row_item.m_MaterialCtrl );
393 
394  if( matName )
395  {
396  if( IsPrmSpecified( item->GetMaterial( sub_item ) ) )
397  matName->SetValue( item->GetMaterial( sub_item ) );
398  else
399  matName->SetValue( wxGetTranslation( NotSpecifiedPrm() ) );
400  }
401  }
402 
403  if( item->IsThicknessEditable() )
404  {
405  wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_ThicknessCtrl );
406 
407  if( textCtrl )
408  textCtrl->SetValue( StringFromValue( m_units,
409  item->GetThickness( sub_item ), true ) );
410 
411  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
412  {
413  wxCheckBox* cb_box = dynamic_cast<wxCheckBox*> ( ui_row_item.m_ThicknessLockCtrl );
414 
415  if( cb_box )
416  cb_box->SetValue( item->IsThicknessLocked( sub_item ) );
417  }
418  }
419 
420  if( item->IsColorEditable() )
421  {
422  auto bm_combo = dynamic_cast<wxBitmapComboBox*>( ui_row_item.m_ColorCtrl );
423  int color_idx = 0;
424 
425  if( item->GetColor().StartsWith( "#" ) ) // User defined color
426  {
427  wxColour color( item->GetColor() );
428  m_UserColors[row] = color;
429  color_idx = GetColorUserDefinedListIdx();
430 
431  if( bm_combo ) // Update user color shown in the wxBitmapComboBox
432  {
433  bm_combo->SetString( color_idx, color.GetAsString( wxC2S_HTML_SYNTAX ) );
434  wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
436  bm_combo->SetItemBitmap( color_idx, layerbmp );
437  }
438  }
439  else
440  {
441  const FAB_LAYER_COLOR* color_list = GetColorStandardList();
442 
443  for( int ii = 0; ii < GetColorStandardListCount(); ii++ )
444  {
445  if( color_list[ii].m_ColorName == item->GetColor() )
446  {
447  color_idx = ii;
448  break;
449  }
450  }
451  }
452 
453  if( bm_combo )
454  bm_combo->SetSelection( color_idx );
455  }
456 
457  if( item->HasEpsilonRValue() )
458  {
459  wxString txt;
460  txt.Printf( "%.2f", item->GetEpsilonR( sub_item ) );
461  wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_EpsilonCtrl );
462 
463  if( textCtrl )
464  textCtrl->SetValue( txt );
465  }
466 
467  if( item->HasLossTangentValue() )
468  {
469  wxString txt;
470  txt.Printf( "%g", item->GetLossTangent( sub_item ) );
471  wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( ui_row_item.m_LossTgCtrl );
472 
473  if( textCtrl )
474  textCtrl->SetValue( txt );
475  }
476  }
477 
478  // Now enable/disable stackup items, according to the m_enabledLayers config
480 
481  updateIconColor();
482 }
483 
484 
486 {
487 
488  // Now enable/disable stackup items, according to the m_enabledLayers config
489  // Calculate copper layer count from m_enabledLayers, and *do not use* brd_stackup
490  // for that, because it is not necessary up to date
491  // (for instance after modifying the layer count from the panel layers in dialog)
493  int copperLayersCount = copperMask.count();
494 
495  for( BOARD_STACKUP_ROW_UI_ITEM& ui_row_item: m_rowUiItemsList )
496  {
497  bool show_item;
498  BOARD_STACKUP_ITEM* item = ui_row_item.m_Item;
499 
500  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
501  // the m_DielectricLayerId is not a copper layer id, it is a dielectric idx from 1
502  show_item = item->GetDielectricLayerId() < copperLayersCount;
503  else
504  show_item = m_enabledLayers[item->GetBrdLayerId()];
505 
506  item->SetEnabled( show_item );
507 
508  ui_row_item.m_isEnabled = show_item;
509 
510  // Show or not items of this row:
511  ui_row_item.m_Icon->Show( show_item );
512  ui_row_item.m_LayerName->Show( show_item );
513  ui_row_item.m_LayerTypeCtrl->Show( show_item );
514  ui_row_item.m_MaterialCtrl->Show( show_item );
515 
516  if( ui_row_item.m_MaterialButt )
517  ui_row_item.m_MaterialButt->Show( show_item );
518 
519  ui_row_item.m_ThicknessCtrl->Show( show_item );
520  ui_row_item.m_ThicknessLockCtrl->Show( show_item );
521  ui_row_item.m_ColorCtrl->Show( show_item );
522  ui_row_item.m_EpsilonCtrl->Show( show_item );
523  ui_row_item.m_LossTgCtrl->Show( show_item );
524  }
525 }
526 
527 
529  const wxString * aMaterialName,
530  BOARD_STACKUP_ROW_UI_ITEM& aUiRowItem )
531 {
532  wxBoxSizer* bSizerMat = new wxBoxSizer( wxHORIZONTAL );
533  m_fgGridSizer->Add( bSizerMat, 1, wxRIGHT|wxEXPAND, 4 );
534  wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY );
535 
536  if( aMaterialName )
537  {
538  if( IsPrmSpecified( *aMaterialName ) )
539  textCtrl->SetValue( *aMaterialName );
540  else
541  textCtrl->SetValue( wxGetTranslation( NotSpecifiedPrm() ) );
542  }
543 
544  textCtrl->SetMinSize( m_numericTextCtrlSize );
545  bSizerMat->Add( textCtrl, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
546 
547  wxButton* m_buttonMat = new wxButton( m_scGridWin, aId, _( "..." ), wxDefaultPosition,
548  wxDefaultSize, wxBU_EXACTFIT );
549  bSizerMat->Add( m_buttonMat, 0, wxALIGN_CENTER_VERTICAL, 2 );
550 
551  m_buttonMat->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
552  wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onMaterialChange ),
553  NULL, this );
554  m_controlItemsList.push_back( m_buttonMat );
555 
556  aUiRowItem.m_MaterialCtrl = textCtrl;
557  aUiRowItem.m_MaterialButt = m_buttonMat;
558 }
559 
560 
562 {
563  wxStaticText* emptyText = new wxStaticText( m_scGridWin, wxID_ANY, wxEmptyString );
564  m_fgGridSizer->Add( emptyText, 0, wxALIGN_CENTER_VERTICAL );
565  return emptyText;
566 }
567 
568 
570  BOARD_STACKUP_ITEM* aStackupItem,
571  int aSublayerIdx )
572 {
573  wxASSERT( aStackupItem );
574  wxASSERT( aSublayerIdx >= 0 && aSublayerIdx < aStackupItem->GetSublayersCount() );
575 
576  BOARD_STACKUP_ROW_UI_ITEM ui_row_item( aStackupItem, aSublayerIdx );
577  BOARD_STACKUP_ITEM* item = aStackupItem;
578  int row = aRow;
579 
580  const FAB_LAYER_COLOR* color_list = GetColorStandardList();
581 
582  // Add color swatch icon. The color will be updated later,
583  // when all widgets are initialized
584  wxStaticBitmap* bitmap = new wxStaticBitmap( m_scGridWin, wxID_ANY, wxNullBitmap );
585  m_fgGridSizer->Add( bitmap, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 4 );
586  ui_row_item.m_Icon = bitmap;
587 
588  ui_row_item.m_isEnabled = true;
589 
590  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
591  {
592  wxString lname = item->FormatDielectricLayerName();
593 
594  if( item->GetSublayersCount() > 1 )
595  {
596  lname << " (" << aSublayerIdx+1 << "/" << item->GetSublayersCount() << ")";
597  }
598 
599  wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname );
600  m_fgGridSizer->Add( st_text, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
601  ui_row_item.m_LayerName = st_text;
602 
603  // For a dielectric layer, the layer type choice is not for each sublayer,
604  // only for the first (aSublayerIdx = 0), and is common to all sublayers
605  if( aSublayerIdx == 0 )
606  {
607  wxChoice* choice = new wxChoice( m_scGridWin, wxID_ANY, wxDefaultPosition,
608  wxDefaultSize, m_core_prepreg_choice );
609  choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 );
610  m_fgGridSizer->Add( choice, 1, wxEXPAND|wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
611 
612  ui_row_item.m_LayerTypeCtrl = choice;
613  }
614  else
615  {
616  ui_row_item.m_LayerTypeCtrl = addSpacer();
617  }
618  }
619  else
620  {
621  item->SetLayerName( m_board->GetLayerName( item->GetBrdLayerId() ) );
622  wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, item->GetLayerName() );
623  m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 1 );
624  st_text->Show( true );
625  ui_row_item.m_LayerName = st_text;
626 
627  wxString lname;
628 
629  if( item->GetTypeName() == KEY_COPPER )
630  lname = _( "Copper" );
631  else
632  lname = wxGetTranslation( item->GetTypeName() );
633 
634  st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname );
635  m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
636  ui_row_item.m_LayerTypeCtrl = st_text;
637  }
638 
639  if( item->IsMaterialEditable() )
640  {
641  wxString matName = item->GetMaterial( aSublayerIdx );
642  addMaterialChooser( ID_ITEM_MATERIAL+row, &matName, ui_row_item );
643  }
644  else
645  {
646  ui_row_item.m_MaterialCtrl = addSpacer();
647  }
648 
649  if( item->IsThicknessEditable() )
650  {
651  wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, ID_ITEM_THICKNESS+row );
652  textCtrl->SetMinSize( m_numericTextCtrlSize );
653  textCtrl->SetValue( StringFromValue( m_units, item->GetThickness( aSublayerIdx ), true ) );
654  m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
655  m_controlItemsList.push_back( textCtrl );
656  textCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED,
657  wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ),
658  NULL, this );
659  ui_row_item.m_ThicknessCtrl = textCtrl;
660 
661  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
662  {
663  wxCheckBox* cb_box = new wxCheckBox( m_scGridWin, ID_ITEM_THICKNESS_LOCKED+row,
664  wxEmptyString );
665  cb_box->SetValue( item->IsThicknessLocked( aSublayerIdx ) );
666  m_fgGridSizer->Add( cb_box, 0, wxALIGN_CENTER_VERTICAL, 2 );
667  ui_row_item.m_ThicknessLockCtrl = cb_box;
668  }
669  else
670  {
671  ui_row_item.m_ThicknessLockCtrl = addSpacer();
672  }
673  }
674  else
675  {
676  ui_row_item.m_ThicknessCtrl = addSpacer();
677  ui_row_item.m_ThicknessLockCtrl = addSpacer();
678  }
679 
680  if( item->IsColorEditable() )
681  {
682  int color_idx = 0;
683 
684  if( item->GetColor().StartsWith( "#" ) ) // User defined color
685  {
686  wxColour color( item->GetColor() );
687  m_UserColors[row] = color;
688  color_idx = GetColorUserDefinedListIdx();
689  }
690  else
691  {
692  for( int ii = 0; ii < GetColorStandardListCount(); ii++ )
693  {
694  if( color_list[ii].m_ColorName == item->GetColor() )
695  {
696  color_idx = ii;
697  break;
698  }
699  }
700  }
701 
702  wxBitmapComboBox* bm_combo = createBmComboBox( item, row );
703  m_colorComboSize.y = bm_combo->GetSize().y;
704  bm_combo->SetMinSize( m_colorComboSize );
705  m_fgGridSizer->Add( bm_combo, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
706  bm_combo->SetSelection( color_idx );
707  ui_row_item.m_ColorCtrl = bm_combo;
708  }
709  else
710  {
711  ui_row_item.m_ColorCtrl = addSpacer();
712  }
713 
714  if( item->HasEpsilonRValue() )
715  {
716  wxString txt;
717  txt.Printf( "%.2f", item->GetEpsilonR( aSublayerIdx ) );
718  wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString,
719  wxDefaultPosition, m_numericFieldsSize );
720  textCtrl->SetValue( txt );
721  m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
722  ui_row_item.m_EpsilonCtrl = textCtrl;
723  }
724  else
725  {
726  ui_row_item.m_EpsilonCtrl = addSpacer();
727  }
728 
729  if( item->HasLossTangentValue() )
730  {
731  wxString txt;
732  txt.Printf( "%g", item->GetLossTangent( aSublayerIdx ) );
733  wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString,
734  wxDefaultPosition, m_numericFieldsSize );
735  textCtrl->SetValue( txt );
736  m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 );
737  ui_row_item.m_LossTgCtrl = textCtrl;
738  }
739  else
740  {
741  ui_row_item.m_LossTgCtrl = addSpacer();
742  }
743 
744  return ui_row_item;
745 }
746 
747 
749 {
750  // Rebuild the stackup for the dialog, after dielectric parameters list is modified
751  // (added/removed):
752 
753  // First, delete all ui objects, because wxID values will be no longer valid for many widgets
755  m_controlItemsList.clear();
756 
757  // Delete widgets (handled by the wxPanel parent)
759  {
760  // This remove and delete the current ui_item.m_MaterialCtrl sizer
761  ui_item.m_MaterialCtrl->SetSizer( nullptr );
762 
763  // Delete other widgets
764  delete ui_item.m_Icon; // Color icon in first column (column 1)
765  delete ui_item.m_LayerName; // string shown in column 2
766  delete ui_item.m_LayerTypeCtrl; // control shown in column 3
767  delete ui_item.m_MaterialCtrl; // control shown in column 4, with m_MaterialButt
768  delete ui_item.m_MaterialButt; // control shown in column 4, with m_MaterialCtrl
769  delete ui_item.m_ThicknessCtrl; // control shown in column 5
770  delete ui_item.m_ThicknessLockCtrl;// control shown in column 6
771  delete ui_item.m_ColorCtrl; // control shown in column 7
772  delete ui_item.m_EpsilonCtrl; // control shown in column 8
773  delete ui_item.m_LossTgCtrl; // control shown in column 9
774  }
775 
776  m_rowUiItemsList.clear();
777  m_UserColors.clear();
778 
779  // In order to recreate a clean grid layer list, we have to delete and
780  // recreate the sizer m_fgGridSizer (just deleting items in this size is not enough)
781  // therefore we also have to add the "old" title items to the newly recreated m_fgGridSizer:
782  m_scGridWin->SetSizer( nullptr ); // This remove and delete the current m_fgGridSizer
783 
784  m_fgGridSizer = new wxFlexGridSizer( 0, 9, 0, 2 );
785  m_fgGridSizer->SetFlexibleDirection( wxHORIZONTAL );
786  m_fgGridSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
787  m_fgGridSizer->SetHGap( 6 );
788  m_scGridWin->SetSizer( m_fgGridSizer );
789 
790  // Re-add "old" title items:
791  const int sizer_flags = wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_CENTER_HORIZONTAL;
792  m_fgGridSizer->Add( m_staticTextLayer, 0, sizer_flags, 2 );
793  m_fgGridSizer->Add( m_staticTextType, 0, sizer_flags, 2 );
794  m_fgGridSizer->Add( m_staticTextLayerId, 0, sizer_flags, 5 );
795  m_fgGridSizer->Add( m_staticTextMaterial, 0, sizer_flags, 2 );
796  m_fgGridSizer->Add( m_staticTextThickness, 0, sizer_flags, 2 );
797  m_fgGridSizer->Add( m_bitmapLockThickness, 0, sizer_flags, 1 );
798  m_fgGridSizer->Add( m_staticTextColor, 0, sizer_flags, 2 );
799  m_fgGridSizer->Add( m_staticTextEpsilonR, 0, sizer_flags, 2 );
800  m_fgGridSizer->Add( m_staticTextLossTg, 0, sizer_flags, 2 );
801 
802 
803  // Now, rebuild the widget list from the new m_stackup items:
804  buildLayerStackPanel( false );
805 
806  // Now enable/disable stackup items, according to the m_enabledLayers config
808 
809  m_scGridWin->Layout();
810 }
811 
812 
813 void PANEL_SETUP_BOARD_STACKUP::buildLayerStackPanel( bool aCreatedInitialStackup )
814 {
815  wxWindowUpdateLocker locker( m_scGridWin );
816 
817  // Build a full stackup for the dialog, with a active copper layer count
818  // = current board layer count to calculate a reasonable default stackup:
819  if( aCreatedInitialStackup )
820  {
821  // Creates a full BOARD_STACKUP with 32 copper layers.
822  // extra layers will be hidden later.
823  // but if the number of layer is changed in the dialog, the corresponding
824  // widgets will be available with their previous values.
826  const BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor();
827 
828  // Now initialize all stackup items to the board values, when exist
829  for( BOARD_STACKUP_ITEM* item: m_stackup.GetList() )
830  {
831  // Search for board settings:
832  for( BOARD_STACKUP_ITEM* board_item: brd_stackup.GetList() )
833  {
834  if( item->GetBrdLayerId() != UNDEFINED_LAYER )
835  {
836  if( item->GetBrdLayerId() == board_item->GetBrdLayerId() )
837  {
838  *item = *board_item;
839  break;
840  }
841  }
842  else // dielectric layer: see m_DielectricLayerId for identification
843  {
844  // Compare dielectric layer with dielectric layer
845  if( board_item->GetBrdLayerId() != UNDEFINED_LAYER )
846  continue;
847 
848  if( item->GetDielectricLayerId() == board_item->GetDielectricLayerId() )
849  {
850  *item = *board_item;
851  break;
852  }
853  }
854  }
855  }
856  }
857 
858  int row = 0;
859 
860  for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
861  {
862  for( int sub_idx = 0; sub_idx < item->GetSublayersCount(); sub_idx++ )
863  {
864  BOARD_STACKUP_ROW_UI_ITEM ui_row_item = createRowData( row, item, sub_idx );
865  m_rowUiItemsList.emplace_back( ui_row_item );
866 
867  row++;
868  }
869  }
870 
871  updateIconColor();
872  m_scGridWin->Layout();
873 }
874 
875 
876 // Transfer current UI settings to m_stackup but not to the board
878 {
879  // First, verify the list of layers currently in stackup: if it doesn't match the list
880  // of layers set in PANEL_SETUP_LAYERS prompt the user to update the stackup
882 
883  if( m_enabledLayers != layersList )
885 
886  // The board thickness and the thickness from stackup settings should be compatible
887  // so verify that compatibility
888  int stackup_thickness = 0;
889 
890  wxString txt;
891  wxString error_msg;
892  bool success = true;
893  double value;
894  int row = 0;
895 
897  {
898  // Skip stackup items useless for the current board
899  if( !ui_item.m_isEnabled )
900  {
901  row++;
902  continue;
903  }
904 
905  BOARD_STACKUP_ITEM* item = ui_item.m_Item;
906  int sub_item = ui_item.m_SubItem;
907 
908  // Add sub layer if there is a new sub layer:
909  while( item->GetSublayersCount() <= sub_item )
910  item->AddDielectricPrms( item->GetSublayersCount() );
911 
912  if( sub_item == 0 ) // Name only main layer
913  item->SetLayerName( ui_item.m_LayerName->GetLabel() );
914 
915  if( item->HasEpsilonRValue() )
916  {
917  wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_EpsilonCtrl );
918  txt = textCtrl->GetValue();
919 
920  if( txt.ToDouble( &value ) && value >= 0.0 )
921  item->SetEpsilonR( value, sub_item );
922  else if( txt.ToCDouble( &value ) && value >= 0.0 )
923  item->SetEpsilonR( value, sub_item );
924  else
925  {
926  success = false;
927  error_msg << _( "Incorrect value for Epsilon R (Epsilon R must be positive or null if not used)" );
928  }
929  }
930 
931  if( item->HasLossTangentValue() )
932  {
933  wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_LossTgCtrl );
934  txt = textCtrl->GetValue();
935 
936  if( txt.ToDouble( &value ) && value >= 0.0 )
937  item->SetLossTangent( value, sub_item );
938  else if( txt.ToCDouble( &value ) && value >= 0.0 )
939  item->SetLossTangent( value, sub_item );
940  else
941  {
942  success = false;
943 
944  if( !error_msg.IsEmpty() )
945  error_msg << "\n";
946 
947  error_msg << _( "Incorrect value for Loss tg (Loss tg must be positive or null if not used)" );
948  }
949  }
950 
951  if( item->IsMaterialEditable() )
952  {
953  wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_MaterialCtrl );
954  item->SetMaterial( textCtrl->GetValue(), sub_item );
955 
956  // Ensure the not specified mat name is the keyword, not its translation
957  // to avoid any issue is the language setting changes
958  if( !IsPrmSpecified( item->GetMaterial( sub_item ) ) )
959  item->SetMaterial( NotSpecifiedPrm(), sub_item );
960  }
961 
962  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
963  {
964  // Choice is Core or Prepreg. Sublayers have no choice:
965  wxChoice* choice = dynamic_cast<wxChoice*>( ui_item.m_LayerTypeCtrl );
966 
967  if( choice )
968  {
969  int idx = choice->GetSelection();
970 
971  if( idx == 0 )
972  item->SetTypeName( KEY_CORE );
973  else
974  item->SetTypeName( KEY_PREPREG );
975  }
976  }
977 
978  if( item->IsThicknessEditable() )
979  {
980  wxTextCtrl* textCtrl = static_cast<wxTextCtrl*>( ui_item.m_ThicknessCtrl );
981  txt = textCtrl->GetValue();
982 
983  int new_thickness = ValueFromString( m_frame->GetUserUnits(), txt );
984  item->SetThickness( new_thickness, sub_item );
985  stackup_thickness += new_thickness;
986 
987  if( new_thickness < 0 )
988  {
989  success = false;
990 
991  if( !error_msg.IsEmpty() )
992  error_msg << "\n";
993 
994  error_msg << _( "A layer thickness is < 0. Fix it" );
995  }
996 
997  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
998  {
999  // Dielectric thickness layer can have a locked thickness:
1000  wxCheckBox* cb_box = static_cast<wxCheckBox*>
1001  ( ui_item.m_ThicknessLockCtrl );
1002  item->SetThicknessLocked( cb_box && cb_box->GetValue(), sub_item );
1003  }
1004  }
1005 
1006  if( sub_item == 0 && item->IsColorEditable() )
1007  {
1008  const FAB_LAYER_COLOR* color_list = GetColorStandardList();
1009 
1010  wxBitmapComboBox* choice = dynamic_cast<wxBitmapComboBox*>( ui_item.m_ColorCtrl );
1011 
1012  if( choice )
1013  {
1014  int idx = choice->GetSelection();
1015 
1016  if( idx == GetColorUserDefinedListIdx() )
1017  {
1018  wxASSERT( m_UserColors.count( row ) );
1019  wxColour color = m_UserColors[row];
1020  item->SetColor( color.GetAsString( wxC2S_HTML_SYNTAX ) );
1021  }
1022  else
1023  {
1024  item->SetColor( color_list[idx].m_ColorName );
1025  }
1026  }
1027  }
1028 
1029  row++;
1030  }
1031 
1032  if( !success )
1033  {
1034  wxMessageBox( error_msg, _( "Errors" ) );
1035  return false;
1036  }
1037 
1039 
1040  return true;
1041 }
1042 
1043 
1045 {
1047  return false;
1048 
1049  // NOTE: Copper layer count is transferred via PANEL_SETUP_LAYERS even though it is configured
1050  // on this page, because the logic for confirming deletion of board items on deleted layers is
1051  // on that panel and it doesn't make sense to split it up.
1052 
1054 
1055  STRING_FORMATTER old_stackup;
1056  brd_stackup.FormatBoardStackup( &old_stackup, m_board, 0 );
1057 
1058  brd_stackup.m_FinishType = m_stackup.m_FinishType;
1062  brd_stackup.m_EdgePlating = m_stackup.m_EdgePlating;
1063 
1064  // copy enabled items to the new board stackup
1065  brd_stackup.RemoveAll();
1066 
1067  for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1068  {
1069  if( item->IsEnabled() )
1070  brd_stackup.Add( new BOARD_STACKUP_ITEM( *item ) );
1071  }
1072 
1073  STRING_FORMATTER new_stackup;
1074  brd_stackup.FormatBoardStackup( &new_stackup, m_board, 0 );
1075 
1076  bool modified = old_stackup.GetString() != new_stackup.GetString();
1077  int thickness = brd_stackup.BuildBoardThicknessFromStackup();
1078 
1079  if( m_brdSettings->GetBoardThickness() != thickness )
1080  {
1081  m_brdSettings->SetBoardThickness( thickness );
1082  modified = true;
1083  }
1084 
1085  if( !m_brdSettings->m_HasStackup )
1086  {
1087  m_brdSettings->m_HasStackup = true;
1088  modified = true;
1089  }
1090 
1091  if( modified )
1092  m_frame->OnModify();
1093 
1094  return true;
1095 }
1096 
1097 
1099 {
1100  BOARD* savedBrd = m_board;
1101  BOARD_DESIGN_SETTINGS* savedSettings = m_brdSettings;
1102  m_brdSettings = &aBoard->GetDesignSettings();
1103 
1105  synchronizeWithBoard( true );
1106 
1107  m_brdSettings = savedSettings;
1108  m_board = savedBrd;
1109 
1111 }
1112 
1113 
1115 {
1116  // First, verify the list of layers currently in stackup:
1117  // if it does not mach the list of layers set in PANEL_SETUP_LAYERS
1118  // rebuild the panel
1119 
1120  // the current enabled layers in PANEL_SETUP_LAYERS
1121  // Note: the number of layer can change, but not the layers properties
1123 
1124  if( m_enabledLayers != layersList )
1125  {
1126  m_enabledLayers = layersList;
1127 
1128  synchronizeWithBoard( false );
1129 
1130  Layout();
1131  Refresh();
1132  }
1133 }
1134 
1135 
1136 void PANEL_SETUP_BOARD_STACKUP::onColorSelected( wxCommandEvent& event )
1137 {
1138  int idx = event.GetSelection();
1139  int item_id = event.GetId();
1140 
1141  int row = item_id - ID_ITEM_COLOR;
1142 
1143  if( GetColorStandardListCount()-1 == idx ) // Set user color is the last option in list
1144  {
1145  COLOR4D defaultColor( GetColorStandardList()[GetColorUserDefinedListIdx()].m_Color );
1146  COLOR4D currentColor(
1147  m_UserColors.count( row ) ? m_UserColors[row] : COLOR4D( 0.5, 0.5, 0.5, 1.0 ) );
1148 
1149  DIALOG_COLOR_PICKER dlg( this, currentColor, false, nullptr, defaultColor );
1150 
1151  if( dlg.ShowModal() == wxID_OK )
1152  {
1153  wxBitmapComboBox* combo = static_cast<wxBitmapComboBox*>( FindWindowById( item_id ) );
1154 
1155  wxColour color = dlg.GetColor().ToColour();
1156  m_UserColors[row] = color;
1157 
1158  combo->SetString( idx, color.GetAsString( wxC2S_HTML_SYNTAX ) );
1159 
1160  wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
1161  LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ),
1162  COLOR4D( color ) );
1163  combo->SetItemBitmap( combo->GetCount()-1, layerbmp );
1164  }
1165  }
1166 
1167  updateIconColor( row );
1168 }
1169 
1170 
1171 void PANEL_SETUP_BOARD_STACKUP::onMaterialChange( wxCommandEvent& event )
1172 {
1173  // Ensure m_materialList contains all materials already in use in stackup list
1174  // and add it is missing
1176  return;
1177 
1178  for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
1179  {
1180  DIELECTRIC_SUBSTRATE_LIST* mat_list = nullptr;
1181 
1182  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1183  mat_list = &m_delectricMatList;
1184  else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK )
1185  mat_list = &m_solderMaskMatList;
1186  else if( item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
1187  mat_list = &m_silkscreenMatList;
1188 
1189  else
1190  continue;
1191 
1192  for( int ii = 0; ii < item->GetSublayersCount(); ii++ )
1193  {
1194  int idx = mat_list->FindSubstrate( item->GetMaterial( ii ),
1195  item->GetEpsilonR( ii ),
1196  item->GetLossTangent( ii ) );
1197 
1198  if( idx < 0 && !item->GetMaterial().IsEmpty() )
1199  {
1200  // This material is not in list: add it
1201  DIELECTRIC_SUBSTRATE new_mat;
1202  new_mat.m_Name = item->GetMaterial( ii );
1203  new_mat.m_EpsilonR = item->GetEpsilonR( ii );
1204  new_mat.m_LossTangent = item->GetLossTangent( ii );
1205  mat_list->AppendSubstrate( new_mat );
1206  }
1207  }
1208  }
1209 
1210  int row = event.GetId() - ID_ITEM_MATERIAL;
1211  BOARD_STACKUP_ITEM* item = m_rowUiItemsList[row].m_Item;
1212  int sub_item = m_rowUiItemsList[row].m_SubItem;
1213  DIELECTRIC_SUBSTRATE_LIST* item_mat_list = nullptr;
1214 
1215  switch( item->GetType() )
1216  {
1217  case BS_ITEM_TYPE_DIELECTRIC: item_mat_list = &m_delectricMatList; break;
1218  case BS_ITEM_TYPE_SOLDERMASK: item_mat_list = &m_solderMaskMatList; break;
1219  case BS_ITEM_TYPE_SILKSCREEN: item_mat_list = &m_silkscreenMatList; break;
1220  default: item_mat_list = nullptr; break;
1221  }
1222 
1223  wxCHECK( item_mat_list, /* void */ );
1224 
1225  DIALOG_DIELECTRIC_MATERIAL dlg( this, *item_mat_list );
1226 
1227  if( dlg.ShowModal() != wxID_OK )
1228  return;
1229 
1230  DIELECTRIC_SUBSTRATE substrate = dlg.GetSelectedSubstrate();
1231 
1232  if( substrate.m_Name.IsEmpty() ) // No substrate specified
1233  return;
1234 
1235  // Update Name, Epsilon R and Loss tg
1236  item->SetMaterial( substrate.m_Name, sub_item );
1237  item->SetEpsilonR( substrate.m_EpsilonR, sub_item );
1238  item->SetLossTangent( substrate.m_LossTangent, sub_item );
1239 
1240  wxTextCtrl* textCtrl;
1241  textCtrl = static_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_MaterialCtrl );
1242  textCtrl->SetValue( item->GetMaterial( sub_item ) );
1243 
1244  // some layers have a material choice but not EpsilonR ctrl
1245  if( item->HasEpsilonRValue() )
1246  {
1247  textCtrl = dynamic_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_EpsilonCtrl );
1248 
1249  if( textCtrl )
1250  textCtrl->SetValue( item->FormatEpsilonR( sub_item ) );
1251  }
1252 
1253  // some layers have a material choice but not loss tg ctrl
1254  if( item->HasLossTangentValue() )
1255  {
1256  textCtrl = dynamic_cast<wxTextCtrl*>( m_rowUiItemsList[row].m_LossTgCtrl );
1257 
1258  if( textCtrl )
1259  textCtrl->SetValue( item->FormatLossTangent( sub_item ) );
1260  }
1261 }
1262 
1263 
1265 {
1266  int row = event.GetId() - ID_ITEM_THICKNESS;
1267  wxString value = event.GetString();
1268 
1269  BOARD_STACKUP_ITEM* item = GetStackupItem( row );
1270  int idx = GetSublayerId( row );
1271 
1272  item->SetThickness( ValueFromString( m_frame->GetUserUnits(), value ), idx );
1273 }
1274 
1275 
1277 {
1278  return m_rowUiItemsList[aRow].m_Item;
1279 }
1280 
1281 
1283 {
1284  return m_rowUiItemsList[aRow].m_SubItem;
1285 }
1286 
1287 
1289 {
1290  BOARD_STACKUP_ITEM* st_item = dynamic_cast<BOARD_STACKUP_ITEM*>( GetStackupItem( aRow ) );
1291 
1292  wxASSERT( st_item );
1293  wxColor color;
1294 
1295  if( ! st_item )
1296  return color;
1297 
1298  switch( st_item->GetType() )
1299  {
1300  case BS_ITEM_TYPE_COPPER: color = copperColor; break;
1302  case BS_ITEM_TYPE_SOLDERMASK: color = GetSelectedColor( aRow ); break;
1303  case BS_ITEM_TYPE_SILKSCREEN: color = GetSelectedColor( aRow ); break;
1304  case BS_ITEM_TYPE_SOLDERPASTE: color = pasteColor; break;
1305 
1306  default:
1308  wxFAIL_MSG( "PANEL_SETUP_BOARD_STACKUP::getColorIconItem: unrecognized item type" );
1309  break;
1310  }
1311 
1312  return color;
1313 }
1314 
1315 
1317 {
1318  if( aRow >= 0 )
1319  {
1320  wxStaticBitmap* st_bitmap = m_rowUiItemsList[aRow].m_Icon;
1321  // explicit depth important under MSW
1322  wxBitmap bmp( m_colorIconsSize.x, m_colorIconsSize.y / 2, 28 );
1323  drawBitmap( bmp, getColorIconItem( aRow ) );
1324  st_bitmap->SetBitmap( bmp );
1325  return;
1326  }
1327 
1328  for( unsigned row = 0; row < m_rowUiItemsList.size(); row++ )
1329  {
1330  // explicit depth important under MSW
1331  wxBitmap bmp( m_colorIconsSize.x, m_colorIconsSize.y / 2, 28 );
1332  drawBitmap( bmp, getColorIconItem( row ) );
1333  m_rowUiItemsList[row].m_Icon->SetBitmap( bmp );
1334  }
1335 }
1336 
1337 
1339  int aRow )
1340 {
1341  wxBitmapComboBox* combo = new wxBitmapComboBox( m_scGridWin, ID_ITEM_COLOR + aRow,
1342  wxEmptyString, wxDefaultPosition,
1343  wxDefaultSize, 0, nullptr, wxCB_READONLY );
1344  // Fills the combo box with choice list + bitmaps
1345  const FAB_LAYER_COLOR* color_list = GetColorStandardList();
1346 
1347  for( int ii = 0; ii < GetColorStandardListCount(); ii++ )
1348  {
1349  const FAB_LAYER_COLOR& item = color_list[ii];
1350 
1351  wxColor curr_color = item.m_Color;
1352  wxString label;
1353 
1354  // Defined colors have a name, the user color uses the HTML notation ( i.e. #FF0000)
1355  if( GetColorStandardListCount()-1 > (int)combo->GetCount() )
1356  {
1357  label = wxGetTranslation( item.m_ColorName );
1358  }
1359  else // Append the user color, if specified, else add a default user color
1360  {
1361  if( aStackupItem && aStackupItem->GetColor().StartsWith( "#" ) )
1362  {
1363  curr_color = wxColour( aStackupItem->GetColor() );
1364  label = aStackupItem->GetColor();
1365  }
1366  else
1367  {
1368  label = curr_color.GetAsString( wxC2S_HTML_SYNTAX );
1369  }
1370  }
1371 
1372  wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
1373  LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), COLOR4D( curr_color ) );
1374 
1375  combo->Append( label, layerbmp );
1376  }
1377 
1378 #ifdef __WXGTK__
1379  // Adjust the minimal width. On GTK, the size calculated by wxWidgets is not good
1380  // bitmaps are ignored
1381  combo->Fit();
1382  int min_width = combo->GetSize().x;
1383  min_width += m_colorSwatchesSize.x;
1384  combo->SetMinSize( wxSize( min_width, -1 ) );
1385 #endif
1386 
1387  // add the wxBitmapComboBox to wxControl list, to be able to disconnect the event
1388  // on exit
1389  m_controlItemsList.push_back( combo );
1390 
1391  combo->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED,
1392  wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onColorSelected ),
1393  NULL, this );
1394 
1395  return combo;
1396 }
1397 
1398 
1399 void drawBitmap( wxBitmap& aBitmap, wxColor aColor )
1400 {
1401  wxNativePixelData data( aBitmap );
1402  wxNativePixelData::Iterator p( data );
1403 
1404  for( int yy = 0; yy < data.GetHeight(); yy++ )
1405  {
1406  wxNativePixelData::Iterator rowStart = p;
1407 
1408  for( int xx = 0; xx < data.GetWidth(); xx++ )
1409  {
1410  p.Red() = aColor.Red();
1411  p.Green() = aColor.Green();
1412  p.Blue() = aColor.Blue();
1413  ++p;
1414  }
1415 
1416  p = rowStart;
1417  p.OffsetY(data, 1);
1418  }
1419 }
1420 
1421 
BOARD_STACKUP_ITEM_TYPE GetType() const
void onExportToClipboard(wxCommandEvent &event) override
static wxColor pasteColor(200, 200, 200)
std::vector< wxControl * > m_controlItemsList
void OnModify() override
Must be called after a board change to set the modified flag.
void SetTypeName(const wxString &aName)
a Dialog to select/change/add a dielectric material from a material list
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:342
this class manage the layers needed to make a physical board they are solder mask,...
int AppendSubstrate(DIELECTRIC_SUBSTRATE &aItem)
Append a item in list similar to aItem.
PANEL_SETUP_LAYERS * m_panelLayers
wxString m_FinishType
The name of external copper finish.
bool m_EdgePlating
True if the edge board is plated.
void updateCopperLayerCount()
Updates the enabled copper layers when the dropdown is changed.
bool IsPrmSpecified(const wxString &aPrmValue)
int color
Definition: DXF_plotter.cpp:60
bool IsThicknessLocked(int aDielectricSubLayer=0) const
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
void onAddDielectricLayer(wxCommandEvent &event) override
wxBitmapComboBox * createBmComboBox(BOARD_STACKUP_ITEM *aStackupItem, int aRow)
creates a bitmap combobox to select a layer color
bool m_CastellatedPads
True if castellated pads exist.
#define KEY_COPPER
wxString GetColor() const
int GetColorStandardListCount()
void onUpdateThicknessValue(wxUpdateUIEvent &event) override
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:591
DIELECTRIC_SUBSTRATE_LIST m_silkscreenMatList
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings Returns a bit-mask of all t...
Definition: board.cpp:447
static wxColor dielectricColor(75, 120, 75)
int GetColorUserDefinedListIdx()
void onRemoveDielUI(wxUpdateUIEvent &event) override
void SetBoardThickness(int aThickness)
wxBitmap KiScaledBitmap(BITMAP_DEF aBitmap, wxWindow *aWindow)
Construct a wxBitmap from a memory record, scaling it if device DPI demands it.
Definition: bitmap.cpp:118
double GetLossTangent(int aDielectricSubLayer=0) const
int BuildBoardThicknessFromStackup() const
void buildLayerStackPanel(bool aCreatedInitialStackup)
Populate m_fgGridSizer with items to handle stackup parameters This is a full list: all copper layers...
void disconnectEvents()
disconnect event handlers connected to wxControl items found in list m_controlItemsList
This file contains miscellaneous commonly used macros and functions.
BOARD_STACKUP_ITEM * GetStackupItem(int aRow)
void OnLayersOptionsChanged(LSET aNewLayerSet)
Must be called if the copper layers count has changed or solder mask, solder paste or silkscreen laye...
DIELECTRIC_SUBSTRATE_LIST m_solderMaskMatList
#define ID_INCREMENT
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
void SetLayerName(const wxString &aName)
void addMaterialChooser(wxWindowID aId, const wxString *aMaterialName, BOARD_STACKUP_ROW_UI_ITEM &aUiRowItem)
add a control (a wxTextCtrl + a button) in m_fgGridSizer to select a material
BOARD_STACKUP & GetStackupDescriptor()
Class PANEL_SETUP_BOARD_STACKUP_BASE.
BOARD_STACKUP_ROW_UI_ITEM createRowData(int aRow, BOARD_STACKUP_ITEM *aStackupItem, int aSublayerIdx)
Creates a BOARD_STACKUP_ROW_UI_ITEM relative to the aStackupItem.
bool IsThicknessEditable() const
void onThicknessChange(wxCommandEvent &event)
LSET is a set of PCB_LAYER_IDs.
std::map< int, wxColor > m_UserColors
void synchronizeWithBoard(bool aFullSync)
Synchronize the full stackup shown in m_fgGridSizer according to the stackup of the current board and...
#define NULL
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
static LSET StackupAllowedBrdLayers()
long long int ValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: base_units.cpp:451
wxString NotSpecifiedPrm()
int GetThickness(int aDielectricSubLayer=0) const
bool HasLossTangentValue() const
BOARD_DESIGN_SETTINGS * m_brdSettings
void ImportSettingsFrom(BOARD *aBoard)
double GetEpsilonR(int aDielectricSubLayer=0) const
wxString FormatEpsilonR(int aDielectricSubLayer=0) const
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:710
static LSET ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition: lset.cpp:780
wxString GetTypeName() const
wxString FormatLossTangent(int aDielectricSubLayer=0) const
wxString BuildStackupReport(BOARD_STACKUP &aStackup, EDA_UNITS aUnits)
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
const std::string & GetString()
Definition: richio.h:435
#define KEY_PREPREG
this class manage one layer needed to make a physical board it can be a solder mask,...
void rebuildLayerStackPanel()
Populate m_fgGridSizer with items to handle stackup parameters If previous items are in list,...
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
PCB_LAYER_ID GetBrdLayerId() const
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, const BOARD *aBoard, int aNestLevel) const
Writes the stackup info on board file.
void AddDielectricPrms(int aDielectricPrmsIdx)
true if this stackup item must be taken in account, false to ignore it.
void SetThickness(int aThickness, int aDielectricSubLayer=0)
static void DrawColorSwatch(wxBitmap &aLayerbmp, COLOR4D aBackground, COLOR4D aColor)
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
wxString GetLayerName() const
wxControl * addSpacer()
add a Spacer in m_fgGridSizer when a empty cell is needed
void SetThicknessLocked(bool aLocked, int aDielectricSubLayer=0)
PANEL_SETUP_BOARD_STACKUP(PAGED_DIALOG *aParent, PCB_EDIT_FRAME *aFrame, PANEL_SETUP_LAYERS *aPanelLayers)
static void drawBitmap(wxBitmap &aBitmap, wxColor aColor)
void RemoveAll()
Delete all items in list and clear the list.
int FindSubstrate(DIELECTRIC_SUBSTRATE *aItem)
Find a item in list similar to aItem.
const BITMAP_OPAQUE locked_xpm[1]
Definition: locked.cpp:30
int GetDielectricLayerId() const
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
int GetCopperLayerCount() const
Definition: board.cpp:435
The main frame for Pcbnew.
const FAB_LAYER_COLOR * GetColorStandardList()
void updateIconColor(int aRow=-1)
Update the icons color (swatches in first grid column)
void onColorSelected(wxCommandEvent &event)
static wxColor copperColor(220, 180, 30)
wxColor GetSelectedColor(int aRow) const
Return the color currently selected for the row aRow.
bool transferDataFromUIToStackup()
Transfer current UI settings to m_stackup but not to the board.
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:225
void showOnlyActiveLayers()
Show or do not show items in m_fgGridSizer according to the stackup of the current board.
DIELECTRIC_SUBSTRATE_LIST m_delectricMatList
void SetColor(const wxString &aColorName)
BOARD * GetBoard() const
#define KEY_CORE
void Add(BOARD_STACKUP_ITEM *aItem)
Add a new item in stackup layer.
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
void SetPhysicalStackupPanel(PANEL_SETUP_BOARD_STACKUP *aPanel)
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:411
void onRemoveDielectricLayer(wxCommandEvent &event) override
void SetLossTangent(double aTg, int aDielectricSubLayer=0)
wxString GetMaterial(int aDielectricSubLayer=0) const
void onMaterialChange(wxCommandEvent &event)
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
wxString FormatDielectricLayerName() const
Container for design settings for a BOARD object.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:98
void SetEnabled(bool aEnable)
void RemoveDielectricPrms(int aDielectricPrmsIdx)
Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList.
std::vector< BOARD_STACKUP_ROW_UI_ITEM > m_rowUiItemsList