KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_export_idf.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) 2013-2015 Cirilo Bernardo
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <pcb_edit_frame.h>
22#include <board.h>
24#include <dialog_export_idf.h>
25
26#include <set>
27#include <3d_cache/3d_cache.h>
29#include <export_idf.h>
30#include <footprint.h>
32#include <pcb_field.h>
33#include <string_utils.h>
35#include <project_pcb.h>
36#include <project/project_file.h> // LAST_PATH_TYPE
37#include <kidialog.h>
38#include <reporter.h>
39
40
42 DIALOG_EXPORT_IDF3_BASE( aEditFrame ),
43 m_xPos( aEditFrame, m_xLabel, m_IDF_Xref, m_xUnits ),
44 m_yPos( aEditFrame, m_yLabel, m_IDF_Yref, m_yUnits ),
45 m_parent( aEditFrame )
46{
48}
49
50
52 DIALOG_EXPORT_IDF3_BASE( aParent ),
53 m_xPos( aEditFrame, m_xLabel, m_IDF_Xref, m_xUnits ),
54 m_yPos( aEditFrame, m_yLabel, m_IDF_Yref, m_yUnits ),
55 m_parent( aEditFrame ),
56 m_job( aJob )
57{
59}
60
61
63{
64 m_hash_key = TO_UTF8( GetTitle() );
65
66 m_rbOriginUser->Bind( wxEVT_RADIOBUTTON, &DIALOG_EXPORT_IDF3::onRadioButtonsChanged, this );
67 m_rbOriginDrill->Bind( wxEVT_RADIOBUTTON, &DIALOG_EXPORT_IDF3::onRadioButtonsChanged, this );
68 m_rbOriginGrid->Bind( wxEVT_RADIOBUTTON, &DIALOG_EXPORT_IDF3::onRadioButtonsChanged, this );
70
72
73 if( m_job )
75 else
76 SetupStandardButtons( { { wxID_OK, _( "Export" ) }, { wxID_CANCEL, _( "Close" ) } } );
77
78 // Now all widgets have the size fixed, call FinishDialogSettings
80}
81
82
83void DIALOG_EXPORT_IDF3::onRadioButtonsChanged( wxCommandEvent& event )
84{
85 m_xPos.Enable( m_rbOriginUser->GetValue() );
86 m_yPos.Enable( m_rbOriginUser->GetValue() );
87
88 event.Skip();
89}
90
91
92void DIALOG_EXPORT_IDF3::OnOKButton( wxCommandEvent& event )
93{
94 if( m_job )
95 {
97 EndModal( wxID_OK );
98 }
99
100 doExport();
101}
102
103
105{
106 m_outputUnitsChoice->SetSelection( aSettings.units == IDF_SETTINGS::UNITS::MILS ? 1 : 0 );
107
108 switch( aSettings.originMode )
109 {
110 default:
111 case IDF_SETTINGS::COORD_ORIGIN::CENTER: m_rbOriginBoardCenter->SetValue( true ); break;
112 case IDF_SETTINGS::COORD_ORIGIN::GRID: m_rbOriginGrid->SetValue( true ); break;
113 case IDF_SETTINGS::COORD_ORIGIN::DRILL: m_rbOriginDrill->SetValue( true ); break;
114 case IDF_SETTINGS::COORD_ORIGIN::USER: m_rbOriginUser->SetValue( true ); break;
115 }
116
117 m_xPos.SetValue( pcbIUScale.mmToIU( aSettings.userOriginX ) );
118 m_yPos.SetValue( pcbIUScale.mmToIU( aSettings.userOriginY ) );
119
120 m_cbRemoveDNP->SetValue( !aSettings.includeDNP );
121 m_cbRemoveUnspecified->SetValue( !aSettings.includeUnspecified );
122 m_cbHeightFromModels->SetValue( aSettings.calculateHeightFromModels );
123
124 if( !m_choicePartNumberField->SetStringSelection( aSettings.partNumberField ) )
125 m_choicePartNumberField->SetStringSelection( wxS( "Value" ) );
126}
127
128
130{
131 IDF_EXPORT_SETTINGS settings;
132
134
135 if( m_rbOriginBoardCenter->GetValue() )
137 else if( m_rbOriginGrid->GetValue() )
139 else if( m_rbOriginDrill->GetValue() )
141 else if( m_rbOriginUser->GetValue() )
143
144 settings.userOriginX = m_xPos.GetValue() / pcbIUScale.IU_PER_MM;
145 settings.userOriginY = m_yPos.GetValue() / pcbIUScale.IU_PER_MM;
146
147 settings.includeDNP = !m_cbRemoveDNP->GetValue();
148 settings.includeUnspecified = !m_cbRemoveUnspecified->GetValue();
149 settings.calculateHeightFromModels = m_cbHeightFromModels->GetValue();
150 settings.partNumberField = m_choicePartNumberField->GetStringSelection();
151
152 return settings;
153}
154
155
157{
158 SetTitle( aSettings.GetSettingsDialogTitle() );
159
160 IDF_EXPORT_SETTINGS settings;
161
162 switch( aSettings.m_units )
163 {
164 default:
167 }
168
169 switch( aSettings.m_originMode )
170 {
171 default:
176 }
177
178 // The job stores the origin in m_units; the project stores it in mm
179 double iuPerUnit = aSettings.m_units == IDF_SETTINGS::UNITS::MM ? pcbIUScale.IU_PER_MM : pcbIUScale.IU_PER_MILS;
180
181 settings.userOriginX = aSettings.m_userOrigin.x * iuPerUnit * pcbIUScale.MM_PER_IU;
182 settings.userOriginY = aSettings.m_userOrigin.y * iuPerUnit * pcbIUScale.MM_PER_IU;
183 settings.includeDNP = aSettings.m_includeDNP;
184 settings.includeUnspecified = aSettings.m_includeUnspecified;
186 settings.partNumberField = aSettings.m_partNumberField;
187
188 ApplySettings( settings );
189}
190
191
193{
194 IDF_EXPORT_SETTINGS settings = GetSettings();
195
196 aSettingsOut.m_units = settings.units;
197
198 switch( settings.originMode )
199 {
200 default:
203 break;
206 break;
209 break;
212 break;
213 }
214
215 // The project stores the origin in mm; the job stores it in m_units
216 double iuPerUnit = aSettingsOut.m_units == IDF_SETTINGS::UNITS::MM ? pcbIUScale.IU_PER_MM : pcbIUScale.IU_PER_MILS;
217
218 aSettingsOut.m_userOrigin.x = settings.userOriginX * iuPerUnit;
219 aSettingsOut.m_userOrigin.y = settings.userOriginY * iuPerUnit;
220 aSettingsOut.m_includeDNP = settings.includeDNP;
221 aSettingsOut.m_includeUnspecified = settings.includeUnspecified;
223 aSettingsOut.m_partNumberField = settings.partNumberField;
224}
225
226
228{
229 m_tcLog->Clear();
230 std::set<wxString> fieldNames;
231
232 for( FOOTPRINT* fp : m_parent->GetBoard()->Footprints() )
233 {
234 for( PCB_FIELD* field : fp->GetFields() )
235 {
236 wxCHECK2( field, continue );
237
238 if( field->IsReference() )
239 continue;
240
241 fieldNames.insert( field->GetUntranslatedName() );
242 }
243 }
244
245 m_choicePartNumberField->Append( std::vector<wxString>( fieldNames.begin(), fieldNames.end() ) );
246
247 if( m_job )
249 else
250 ApplySettings( m_parent->Prj().GetProjectFile().m_IdfExportSettings );
251
252 wxString path = m_parent->GetLastPath( LAST_PATH_IDF );
253
254 if( m_job )
255 path = m_job->GetConfiguredOutputPath();
256
257 if( path.IsEmpty() )
258 {
259 wxFileName brdFile = m_parent->GetBoard()->GetFileName();
260 brdFile.SetExt( FILEEXT::IdfV3BoardFileExtension );
261 path = brdFile.GetFullPath();
262 }
263
264 m_filePickerIDF->SetPath( path );
265
266 wxCommandEvent dummy;
268
269 return true;
270}
271
272
274{
275 if( m_job )
277
278 return true;
279}
280
281
283{
284 wxFileName brdFile( m_filePickerIDF->GetPath() );
285 brdFile.SetExt( wxT( "emn" ) );
286
287 wxFileName libFile( m_filePickerIDF->GetPath() );
288 libFile.SetExt( wxT( "emp" ) );
289
290 wxArrayString existing;
291
292 if( brdFile.FileExists() )
293 existing.push_back( brdFile.GetFullPath() );
294
295 if( libFile.FileExists() )
296 existing.push_back( libFile.GetFullPath() );
297
298 if( !existing.empty() )
299 {
300 wxString msg;
301
302 if( existing.size() == 1 )
303 msg = wxString::Format( _( "File %s already exists." ), existing[0] );
304 else
305 msg = wxString::Format( _( "Files %s and %s already exist." ), existing[0], existing[1] );
306
307 KIDIALOG dlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
308 dlg.SetOKLabel( _( "Overwrite" ) );
309 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
310
311 if( dlg.ShowModal() != wxID_OK )
312 return false;
313 }
314
315 for( const wxFileName& fn : { brdFile, libFile } )
316 {
317 if( fn.FileExists() && !fn.IsFileWritable() )
318 {
319 wxMessageBox( wxString::Format( _( "Insufficient permissions to write file '%s'." ),
320 fn.GetFullPath() ),
321 _( "IDF Export" ), wxOK | wxICON_ERROR );
322
323 return false;
324 }
325 }
326
327 return true;
328}
329
330
332{
333 if( !checkFilenames() )
334 return;
335
336 m_tcLog->Clear();
338
340
341 bool resetJob = false;
343
344 if( !m_job )
345 {
346 GetJobSettings( job );
347 m_job = &job;
348 resetJob = true;
349 }
350
351 IDF_EXPORTER exporter( m_parent->GetBoard(), resolver, &job, &reporter );
352
353 wxBusyCursor dummy;
354
355 if( !exporter.Export( m_filePickerIDF->GetPath() ) )
356 {
357 wxMessageBox( wxString::Format( _( "Failed to create file '%s'." ), m_filePickerIDF->GetPath() ) );
358 return;
359 }
360
361 GetJobSettings( job );
362
363 IDF_EXPORT_SETTINGS& projectSettings = m_parent->Prj().GetProjectFile().m_IdfExportSettings;
364
365 projectSettings.units = job.m_units;
366
367 switch( job.m_originMode )
368 {
369 default:
374 }
375
376 // The job stores the origin in m_units; the project stores it in mm
377 double iuPerUnit = job.m_units == IDF_SETTINGS::UNITS::MM ? pcbIUScale.IU_PER_MM : pcbIUScale.IU_PER_MILS;
378
379 projectSettings.userOriginX = job.m_userOrigin.x * iuPerUnit * pcbIUScale.MM_PER_IU;
380 projectSettings.userOriginY = job.m_userOrigin.y * iuPerUnit * pcbIUScale.MM_PER_IU;
381 projectSettings.includeDNP = job.m_includeDNP;
382 projectSettings.includeUnspecified = job.m_includeUnspecified;
384 projectSettings.partNumberField = job.m_partNumberField;
385
386 m_parent->SetLastPath( LAST_PATH_IDF, m_filePickerIDF->GetPath() );
387
388 if( resetJob )
389 m_job = nullptr;
390}
391
392
394{
396 dlg.ShowModal();
397
398 return 0;
399}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
int ExportIDF(const TOOL_EVENT &aEvent)
wxFilePickerCtrl * m_filePickerIDF
DIALOG_EXPORT_IDF3_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Export IDFv3"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
bool TransferDataToWindow() override
bool TransferDataFromWindow() override
JOB_EXPORT_PCB_IDF * m_job
void GetJobSettings(JOB_EXPORT_PCB_IDF &aSettingsOut) const
IDF_EXPORT_SETTINGS GetSettings() const
void ApplySettings(const IDF_EXPORT_SETTINGS &aSettings)
void onRadioButtonsChanged(wxCommandEvent &event)
PCB_EDIT_FRAME * m_parent
DIALOG_EXPORT_IDF3(PCB_EDIT_FRAME *aEditFrame)
void OnOKButton(wxCommandEvent &event) override
void ApplyJobSettings(const JOB_EXPORT_PCB_IDF &aSettings)
void OptOut(wxWindow *aWindow)
Opt out of control state saving.
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int ShowModal() override
Provide an extensible class to resolve 3D model paths.
bool Export(const wxString &aFullFileName) const
Generate IDFv3 compliant board (*.emn) and library (*.emp) files for aPcb.
IDF_SETTINGS::COORD_ORIGIN m_originMode
VECTOR2D m_userOrigin
In whatever units the user specified in m_units.
IDF_SETTINGS::UNITS m_units
wxString GetSettingsDialogTitle() const override
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:38
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:51
int ShowModal() override
Definition kidialog.cpp:89
The main frame for Pcbnew.
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
FILENAME_RESOLVER * GetResolver() noexcept
Definition 3d_cache.cpp:548
Generic, UI-independent tool event.
Definition tool_event.h:167
A wrapper for reporting to a wxTextCtrl object.
Definition reporter.h:218
#define _(s)
static const std::string IdfV3BoardFileExtension
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
@ LAST_PATH_IDF
std::vector< FAB_LAYER_COLOR > dummy
Persisted settings for the IDF export dialog.
IDF_SETTINGS::COORD_ORIGIN originMode
IDF_SETTINGS::UNITS units
std::string path
IbisParser parser & reporter