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