KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_fab_export_content.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
23
24#include <algorithm>
25#include <board.h>
27#include <string_utils.h>
28
29#include <wx/choicdlg.h>
30
31namespace
32{
33struct SECTION_ROW
34{
35 FAB::SECTION m_section;
36 wxString m_label;
37 wxString m_odb;
38 wxString m_ipc;
39};
40
41
42std::vector<SECTION_ROW> sectionRows()
43{
44 using SECTION = FAB::SECTION;
45
46 return {
47 { SECTION::BOM_AVL, _( "BOM" ), wxS( "components PRP, boms/" ), wxS( "Bom, Avl" ) },
48 { SECTION::PACKAGES, _( "packages" ), wxS( "eda/data PKG" ), wxS( "Package" ) },
49 { SECTION::COMPONENTS, _( "components" ), wxS( "comp_+_top, comp_+_bot" ), wxS( "Component" ) },
50 { SECTION::PADSTACKS, _( "padstacks" ), wxS( "features .geometry" ), wxS( "PadStackDef" ) },
51 { SECTION::STACKUP, _( "stackup" ), wxS( "matrix/stackup.xml" ), wxS( "Stackup, Spec" ) },
52 { SECTION::PROFILE, _( "board profile" ), wxS( "steps/pcb/profile" ), wxS( "Profile" ) },
53 { SECTION::SOLDERMASK, _( "solder mask" ), wxS( "SOLDER_MASK" ), wxS( "SOLDERMASK" ) },
54 { SECTION::SOLDERPASTE, _( "solder paste" ), wxS( "SOLDER_PASTE" ), wxS( "SOLDERPASTE" ) },
55 { SECTION::SILKSCREEN, _( "silkscreen" ), wxS( "SILK_SCREEN" ), wxS( "SILKSCREEN" ) },
56 { SECTION::DRILL_ROUT, _( "drill and router" ), wxS( "DRILL, ROUT" ), wxS( "DRILL, ROUT" ) },
57 { SECTION::DOCUMENTATION, _( "documentation" ), wxS( "DOCUMENT" ), wxS( "DOCUMENT" ) },
58 { SECTION::OUTER_COPPER, _( "outer copper" ), wxS( "SIGNAL" ), wxS( "CONDUCTOR, SIGNAL" ) },
59 { SECTION::INNER_COPPER, _( "inner copper" ), wxS( "SIGNAL, POWER_GROUND, MIXED" ),
60 wxS( "SIGNAL, PLANE, MIXED" ) },
61 { SECTION::DIELECTRIC, _( "dielectric" ), wxS( "DIELECTRIC rows" ), wxS( "DIELCORE, DIELPREG" ) },
62 { SECTION::MISC_FAB, _( "misc fab layers" ), wxS( "DOCUMENT (MISC)" ), wxS( "Layer (other functions)" ) },
63 { SECTION::LOGICAL_NET, _( "logical netlist" ), wxS( "eda/data NET" ), wxS( "LogicalNet" ) },
64 { SECTION::PHYSICAL_NET, _( "physical netlist" ), wxS( "netlists/cadnet" ), wxS( "PhyNetGroup" ) },
65 };
66}
67
68
69class DIALOG_FAB_CUSTOMIZE : public DIALOG_FAB_CUSTOMIZE_BASE
70{
71public:
72 DIALOG_FAB_CUSTOMIZE( wxWindow* aParent, FAB_CONTENT_FORMAT aFormat, FAB::MODE aMode,
73 const FAB::SECTION_SET& aCurrent, bool aBoardMetadata ) :
74 DIALOG_FAB_CUSTOMIZE_BASE( aParent ),
75 m_original( aCurrent )
76 {
77 bool odb = aFormat == FAB_CONTENT_FORMAT::ODBPP;
78 m_sections->AppendToggleColumn( wxEmptyString, wxDATAVIEW_CELL_ACTIVATABLE, 36 );
79 m_sections->AppendTextColumn( _( "Section" ), wxDATAVIEW_CELL_INERT, 170 );
80 m_sections->AppendTextColumn( odb ? _( "ODB++" ) : _( "IPC-2581" ), wxDATAVIEW_CELL_INERT, 260 );
81 FAB::SECTION_SET optional = FAB::OptionalSections( aMode );
82
83 for( const SECTION_ROW& row : sectionRows() )
84 {
85 m_rows.push_back( row.m_section );
86 // A preset allows changes only to its optional sections
87 m_editable.push_back( ( odb && aMode == FAB::MODE::USERDEF ) || optional.Contains( row.m_section ) );
88 wxVector<wxVariant> values;
89 values.push_back( wxVariant( aCurrent.Contains( row.m_section ) ) );
90 values.push_back( wxVariant( row.m_label ) );
91 values.push_back( wxVariant( odb ? row.m_odb : row.m_ipc ) );
92 m_sections->AppendItem( values );
93 }
94
95 if( odb )
96 {
97 m_intro->SetLabel(
98 _( "Choose sections to include. Intentional shorts are written when the board has net ties." ) );
99 wxVector<wxVariant> values;
100 values.push_back( wxVariant( aBoardMetadata ) );
101 values.push_back( wxVariant( _( "board metadata" ) ) );
102 values.push_back( wxVariant( wxS( "misc/metadata.xml" ) ) );
103 m_metadataRow = m_sections->GetItemCount();
104 m_sections->AppendItem( values );
105 m_editable.push_back( true );
106
107 values.clear();
108 values.push_back( wxVariant( true ) );
109 values.push_back( wxVariant( _( "intentional shorts" ) ) );
110 values.push_back( wxVariant( wxS( "eda/shortf" ) ) );
111 m_sections->AppendItem( values );
112 m_editable.push_back( false );
113 }
114
115 m_sections->Bind( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &DIALOG_FAB_CUSTOMIZE::onValueChanged, this );
116 SetupStandardButtons();
117 finishDialogSettings();
118 }
119
120 FAB::SECTION_SET Selected() const
121 {
122 FAB::SECTION_SET selected;
123
124 for( size_t index = 0; index < m_rows.size(); ++index )
125 {
126 wxVariant value;
127 m_sections->GetValue( value, static_cast<unsigned int>( index ), 0 );
128
129 if( value.GetBool() )
130 selected.Set( m_rows[index] );
131 }
132
133 return selected;
134 }
135
136 bool BoardMetadata() const
137 {
138 wxVariant value;
139 m_sections->GetValue( value, m_metadataRow, 0 );
140 return value.GetBool();
141 }
142
143private:
144 void onValueChanged( wxDataViewEvent& aEvent )
145 {
146 if( m_restoring )
147 return;
148
149 unsigned int row = m_sections->ItemToRow( aEvent.GetItem() );
150
151 if( row >= m_editable.size() || m_editable[row] )
152 return;
153
154 m_restoring = true;
155 bool included = row < m_rows.size() && m_original.Contains( m_rows[row] );
156
157 if( row >= m_rows.size() )
158 included = true;
159
160 m_sections->SetValue( wxVariant( included ), row, 0 );
161 m_restoring = false;
162 }
163
164 std::vector<FAB::SECTION> m_rows;
165 std::vector<bool> m_editable;
166 unsigned int m_metadataRow = 0;
167 FAB::SECTION_SET m_original;
168 bool m_restoring = false;
169};
170} // namespace
171
172
173PANEL_FAB_EXPORT_CONTENT::PANEL_FAB_EXPORT_CONTENT( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos,
174 const wxSize& aSize, long aStyle, const wxString& aName ) :
175 PANEL_FAB_EXPORT_CONTENT_BASE( aParent, aId, aPos, aSize, aStyle, aName )
176{
177}
178
179
181{
182 m_format = aFormat;
183 m_board = aBoard;
184 bool odb = aFormat == FAB_CONTENT_FORMAT::ODBPP;
185 m_lblRefDes->Show( !odb );
186 m_choiceRefDes->Show( !odb );
187 m_choiceDataSet->Clear();
188 m_modes.clear();
189
190 auto addMode = [&]( FAB::MODE aMode, const wxString& aLabel )
191 {
192 m_modes.push_back( aMode );
193 m_choiceDataSet->Append( aLabel );
194 };
195
196 addMode( FAB::MODE::USERDEF, _( "All data (user-defined)" ) );
197
198 if( !odb )
199 addMode( FAB::MODE::BOM, _( "Bill of materials" ) );
200
201 addMode( FAB::MODE::STACKUP, _( "Stackup" ) );
202 addMode( FAB::MODE::FABRICATION, _( "Fabrication" ) );
203 addMode( FAB::MODE::ASSEMBLY, _( "Assembly" ) );
204 addMode( FAB::MODE::TEST, _( "Test" ) );
205
206 if( !odb )
207 addMode( FAB::MODE::STENCIL, _( "Stencil" ) );
208
209 m_choiceDataSet->SetSelection( 0 );
210 m_choiceVariant->Clear();
211 wxString current = aBoard ? aBoard->GetCurrentVariant() : wxString();
212
213 if( current.IsEmpty() )
214 current = GetDefaultVariantName();
215
216 m_choiceVariant->Append( wxString::Format( _( "Current (%s)" ), current ) );
217
218 if( aBoard )
219 {
220 for( const wxString& variant : aBoard->GetVariantNames() )
221 m_choiceVariant->Append( variant );
222 }
223
224 if( odb )
225 {
226 m_choiceVariant->Append( _( "Selected variants…" ) );
227 m_choiceVariant->Append( _( "All variants" ) );
228 }
229
230 m_choiceVariant->SetSelection( 0 );
232
233 // Every entry names the same output when the board has no variants
234 bool hasVariants = aBoard && !aBoard->GetVariantNames().empty();
235 m_lblVariant->Enable( hasVariants );
236 m_choiceVariant->Enable( hasVariants );
237 m_variantOutput->Show( false );
238 m_variantHint->Show( false );
240}
241
242
244{
245 int selection = m_choiceDataSet->GetSelection();
246 return selection >= 0 && static_cast<size_t>( selection ) < m_modes.size() ? m_modes[selection]
247 : FAB::MODE::USERDEF;
248}
249
250
252{
253 auto it = std::find( m_modes.begin(), m_modes.end(), aMode );
254 m_choiceDataSet->SetSelection( it == m_modes.end() ? 0 : static_cast<int>( it - m_modes.begin() ) );
256}
257
258
264
265
270
271
277
278
283
284
285void PANEL_FAB_EXPORT_CONTENT::SetSectionKey( const std::optional<wxString>& aKey )
286{
287 m_sectionKey = aKey;
289}
290
291
293{
294 m_boardMetadata = aValue;
296}
297
298
299std::vector<wxString> PANEL_FAB_EXPORT_CONTENT::GetVariantNames() const
300{
301 int selection = m_choiceVariant->GetSelection();
302
303 if( !m_board || selection <= 0 )
304 return {};
305
306 const std::vector<wxString>& names = m_board->GetVariantNames();
307
308 if( static_cast<size_t>( selection ) <= names.size() )
309 return { names[selection - 1] };
310
312 return {};
313
314 if( static_cast<size_t>( selection ) == names.size() + 1 )
315 return m_selectedVariants;
316
317 std::vector<wxString> all = { GetDefaultVariantName() };
318 all.insert( all.end(), names.begin(), names.end() );
319 return all;
320}
321
322
323void PANEL_FAB_EXPORT_CONTENT::SetVariantNames( const std::vector<wxString>& aNames )
324{
325 m_selectedVariants = aNames;
326 int selection = 0;
327
328 if( m_board && !aNames.empty() )
329 {
330 const std::vector<wxString>& names = m_board->GetVariantNames();
331
332 if( aNames.size() == 1 )
333 {
334 auto it = std::find( names.begin(), names.end(), aNames.front() );
335
336 if( it != names.end() )
337 selection = static_cast<int>( it - names.begin() ) + 1;
338 }
339
340 if( selection == 0 && m_format == FAB_CONTENT_FORMAT::ODBPP )
341 selection = static_cast<int>( names.size() ) + 1;
342 }
343
344 m_choiceVariant->SetSelection( selection );
345 m_selectedChoice = selection;
347}
348
349
351{
352 m_variantOutput->SetSelection( aCombined ? 1 : 0 );
354}
355
356
358{
359 FAB::MODE mode = GetDataSet();
360 FAB::SECTION_SET selected;
361
363 {
365 selected.Set( FAB::SECTION::PACKAGES );
366
367 // Table 4 by itself only gives the schema sections
369 ? selected
371 }
372
374 return OdbDefaultSections( mode );
375
376 // Table 4 by itself only gives the schema sections
378 .m_included;
379}
380
381
383{
385 wxString summary = _( "Includes: " );
386 wxString line = summary;
387 int lines = 1;
388 std::vector<wxString> labels;
389
390 for( const SECTION_ROW& row : sectionRows() )
391 {
392 if( sections.Contains( row.m_section ) )
393 labels.push_back( row.m_label );
394 }
395
397 labels.push_back( _( "board metadata" ) );
398
399 for( const wxString& label : labels )
400 {
401 wxString item = label;
402 bool first = line == _( "Includes: " );
403 item = first ? item : wxS( ", " ) + item;
404
405 if( line.length() + item.length() > 70 && !first )
406 {
407 summary << wxS( "\n" );
408 line.clear();
409 ++lines;
410 item = label;
411 }
412
413 summary << item;
414 line << item;
415 }
416
417 m_lblIncludes->SetLabel( summary );
418 m_lblIncludes->SetMinSize( wxSize( -1, lines * GetCharHeight() + 5 ) );
419 m_btnBomFields->Enable(
422 Layout();
423
424 if( GetParent() )
425 GetParent()->Layout();
426}
427
428
430{
431 bool multiple = m_format == FAB_CONTENT_FORMAT::ODBPP && GetVariantNames().size() > 1;
432 m_variantOutput->Show( multiple );
433 m_variantHint->Show( multiple && !IsCombinedVariantOutput() );
434 Layout();
435
436 if( GetParent() )
437 GetParent()->Layout();
438}
439
440
446
447
448void PANEL_FAB_EXPORT_CONTENT::onDataSetChange( wxCommandEvent& aEvent )
449{
450 // A new function mode removes the section key of the previous function mode
451 m_sectionKey.reset();
453 changed();
454}
455
456
457void PANEL_FAB_EXPORT_CONTENT::onVariantChange( wxCommandEvent& aEvent )
458{
460 {
461 const std::vector<wxString>& names = m_board->GetVariantNames();
462 int selectedIndex = static_cast<int>( names.size() ) + 1;
463
464 if( m_choiceVariant->GetSelection() == selectedIndex )
465 {
466 wxArrayString choices;
467 choices.Add( GetDefaultVariantName() );
468
469 for( const wxString& name : names )
470 choices.Add( name );
471
472 wxMultiChoiceDialog dialog( this, _( "Choose the variants to export" ), _( "Selected variants" ), choices );
473 wxArrayInt checked;
474
475 for( const wxString& name : m_selectedVariants )
476 {
477 int index = choices.Index( name );
478
479 if( index != wxNOT_FOUND )
480 checked.Add( index );
481 }
482
483 dialog.SetSelections( checked );
484
485 if( dialog.ShowModal() != wxID_OK )
486 {
487 m_choiceVariant->SetSelection( m_selectedChoice );
488 return;
489 }
490
491 m_selectedVariants.clear();
492
493 for( int index : dialog.GetSelections() )
494 m_selectedVariants.push_back( choices[index] );
495
496 if( m_selectedVariants.empty() )
497 {
498 m_choiceVariant->SetSelection( m_selectedChoice );
499 return;
500 }
501 }
502 }
503
504 m_selectedChoice = m_choiceVariant->GetSelection();
506 changed();
507}
508
509
511{
513 changed();
514}
515
516
517void PANEL_FAB_EXPORT_CONTENT::onCustomizeClick( wxCommandEvent& aEvent )
518{
519 DIALOG_FAB_CUSTOMIZE dialog( this, m_format, GetDataSet(), ResolvedSections(), m_boardMetadata );
520
521 if( dialog.ShowModal() != wxID_OK )
522 return;
523
525 : FAB::SectionKeyString( dialog.Selected() );
526
527 if( m_format == FAB_CONTENT_FORMAT::ODBPP && m_sectionKey->IsEmpty() )
528 m_sectionKey.reset();
529
531 m_boardMetadata = dialog.BoardMetadata();
532
534 changed();
535}
536
537
538void PANEL_FAB_EXPORT_CONTENT::onBomFieldsClick( wxCommandEvent& aEvent )
539{
540 if( !m_board )
541 return;
542
544
545 if( dialog.ShowModal() == wxID_OK )
546 m_bomFields = dialog.GetFields();
547}
int index
const char * name
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const std::vector< wxString > & GetVariantNames() const
Definition board.h:532
wxString GetCurrentVariant() const
Definition board.h:529
const IPC2581_BOM_FIELDS & GetFields() const
Class DIALOG_FAB_CUSTOMIZE_BASE.
int ShowModal() override
Set of schema sections.
SECTION_SET & Set(SECTION aSection, bool aOn=true)
bool Contains(SECTION aSection) const
NET_NAMES
An anonymous name keeps the connections.
PANEL_FAB_EXPORT_CONTENT_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
void onBomFieldsClick(wxCommandEvent &aEvent) override
std::function< void()> m_contentChanged
void onVariantOutputChange(wxCommandEvent &aEvent) override
void SetSectionKey(const std::optional< wxString > &aKey)
void SetRefDes(JOB_EXPORT_PCB_IPC2581::REF_DES aRefDes)
void onVariantChange(wxCommandEvent &aEvent) override
PANEL_FAB_EXPORT_CONTENT(wxWindow *aParent, wxWindowID aId=wxID_ANY, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize, long aStyle=wxTAB_TRAVERSAL, const wxString &aName=wxEmptyString)
void onDataSetChange(wxCommandEvent &aEvent) override
void SetVariantNames(const std::vector< wxString > &aNames)
void onCustomizeClick(wxCommandEvent &aEvent) override
std::vector< wxString > GetVariantNames() const
void updateSummary()
Set the Includes line and BOM Fields button from the function mode.
std::optional< wxString > m_sectionKey
Set after Customize An empty IPC-2581 key is a true empty selection.
void SetNetNames(JOB_EXPORT_PCB_FAB::NET_NAMES aNetNames)
JOB_EXPORT_PCB_FAB::NET_NAMES GetNetNames() const
JOB_EXPORT_PCB_IPC2581::REF_DES GetRefDes() const
FAB::SECTION_SET ResolvedSections() const
void SetCombinedVariantOutput(bool aCombined)
void Configure(FAB_CONTENT_FORMAT aFormat, BOARD *aBoard)
std::vector< FAB::MODE > m_modes
std::vector< wxString > m_selectedVariants
#define _(s)
JOB_EXPORT_PCB_FAB::DATA_SET MODE
Columns of Table 4 The job owns the enum so that every layer shares one type.
wxString SectionKeyString(const SECTION_SET &aSet)
Give aSet as a sectionKey attribute value.
SECTION
Schema sections of the IPC-2581C function mode table, Table 4 p 80.
bool SectionSetFromKeyString(const wxString &aKey, SECTION_SET &aResult)
Read a sectionKey attribute value Return false for an unknown character.
SECTION_SET OptionalSections(MODE aMode)
Schema sections that Table 4 marks O for aMode.
SECTION_SET RecommendedOptionalSections(MODE aMode)
Optional schema sections that aMode selects by default.
RESOLVE_RESULT ResolveSections(REVISION aRevision, MODE aMode, const SECTION_SET &aRequested)
Compare aRequested with Table 4 and give the attributes to remove and the conflicts.
wxString OdbSectionKeyForSelection(FAB::MODE aMode, const FAB::SECTION_SET &aSelection)
Use an empty section key when Customize matches the data set default.
FAB::SECTION_SET OdbDefaultSections(FAB::MODE aMode)
Sections a data set exports when no section key is given.
wxString GetDefaultVariantName()