KiCad PCB EDA Suite
dialog_gendrill.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) 1992-2019 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5 * Copyright (C) 1992-2022 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 <confirm.h>
26#include <core/arraydim.h>
27#include <pcb_edit_frame.h>
28#include <pcbnew_settings.h>
29#include <pcbplot.h>
32#include <bitmaps.h>
34#include <board.h>
36#include <footprint.h>
37#include <pad.h>
38#include <pcb_track.h>
39#include <paths.h>
40#include <project.h>
41#include <dialog_gendrill.h>
43#include <reporter.h>
44#include <wx/msgdlg.h>
45#include <wx/dirdlg.h>
46#include <wx/filedlg.h>
47
48// list of allowed precision for EXCELLON files, for integer format:
49// Due to difference between inches and mm,
50// there are 2 precision values, one for inches and one for metric
51// Note: for decimla format, the precision is not used
54
55
56/* This function displays the dialog frame for drill tools
57 */
59{
60 PCB_EDIT_FRAME* editFrame = getEditFrame<PCB_EDIT_FRAME>();
61 DIALOG_GENDRILL dlg( editFrame, editFrame );
62
63 dlg.ShowModal();
64 return 0;
65}
66
67
68DIALOG_GENDRILL::DIALOG_GENDRILL( PCB_EDIT_FRAME* aPcbEditFrame, wxWindow* aParent ) :
69 DIALOG_GENDRILL_BASE( aParent )
70{
71 m_pcbEditFrame = aPcbEditFrame;
74
75 SetupStandardButtons( { { wxID_OK, _( "Generate Drill File" ) },
76 { wxID_APPLY, _( "Generate Map File" ) },
77 { wxID_CANCEL, _( "Close" ) } } );
78
79 m_buttonsSizer->Layout();
80
81 SetReturnCode( 1 );
82 initDialog();
83 GetSizer()->SetSizeHints( this );
84}
85
86
87// Static members of DIALOG_GENDRILL
88int DIALOG_GENDRILL::m_UnitDrillIsInch = true; // Only for Excellon format
90bool DIALOG_GENDRILL::m_MinimalHeader = false; // Only for Excellon format
91bool DIALOG_GENDRILL::m_Mirror = false; // Only for Excellon format
92bool DIALOG_GENDRILL::m_Merge_PTH_NPTH = false; // Only for Excellon format
93int DIALOG_GENDRILL::m_mapFileType = 4; // The last choice in m_Choice_Drill_Map
95bool DIALOG_GENDRILL::m_UseRouteModeForOvalHoles = true; // Use G00 route mode to "drill" oval holes
96
98{
99}
100
101
103{
104 auto cfg = m_pcbEditFrame->GetPcbNewSettings();
105
106 m_Merge_PTH_NPTH = cfg->m_GenDrill.merge_pth_npth;
107 m_MinimalHeader = cfg->m_GenDrill.minimal_header;
108 m_Mirror = cfg->m_GenDrill.mirror;
109 m_UnitDrillIsInch = cfg->m_GenDrill.unit_drill_is_inch;
110 m_UseRouteModeForOvalHoles = cfg->m_GenDrill.use_route_for_oval_holes;
111 m_drillFileType = cfg->m_GenDrill.drill_file_type;
112 m_mapFileType = cfg->m_GenDrill.map_file_type;
113 m_ZerosFormat = cfg->m_GenDrill.zeros_format;
114
116
117 // Ensure validity of m_mapFileType
118 if( m_mapFileType < 0 || m_mapFileType >= (int)m_Choice_Drill_Map->GetCount() )
119 m_mapFileType = m_Choice_Drill_Map->GetCount() - 1; // last item in list = default = PDF
120
122}
123
124
126{
128
129 m_rbExcellon->SetValue( m_drillFileType == 0 );
130 m_rbGerberX2->SetValue( m_drillFileType == 1 );
131 m_Choice_Unit->SetSelection( m_UnitDrillIsInch ? 1 : 0 );
132 m_Choice_Zeros_Format->SetSelection( m_ZerosFormat );
134 m_Check_Minimal->SetValue( m_MinimalHeader );
135
136 m_Choice_Drill_Offset->SetSelection( m_drillOriginIsAuxAxis ? 1 : 0 );
137
138 m_Check_Mirror->SetValue( m_Mirror );
140 m_Choice_Drill_Map->SetSelection( m_mapFileType );
141 m_radioBoxOvalHoleMode->SetSelection( m_UseRouteModeForOvalHoles ? 0 : 1 );
142
148
149 for( FOOTPRINT* footprint : m_board->Footprints() )
150 {
151 for( PAD* pad : footprint->Pads() )
152 {
153 if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
154 {
155 if( pad->GetDrillSize().x != 0 )
156 {
157 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
159 else
161 }
162 }
163 else
164 {
165 if( pad->GetDrillSize().x != 0 && pad->GetDrillSize().y != 0 )
166 {
167 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
169 else
171 }
172 }
173 }
174 }
175
176 for( PCB_TRACK* track : m_board->Tracks() )
177 {
178 const PCB_VIA *via = dynamic_cast<const PCB_VIA*>( track );
179
180 if( via )
181 {
182 switch( via->GetViaType() )
183 {
185 case VIATYPE::MICROVIA: m_microViasCount++; break;
187 default: break;
188 }
189 }
190 }
191
192 // Display hole counts:
193 m_PlatedPadsCountInfoMsg->SetLabel( wxString() << m_platedPadsHoleCount );
194 m_NotPlatedPadsCountInfoMsg->SetLabel( wxString() << m_notplatedPadsHoleCount );
195 m_ThroughViasInfoMsg->SetLabel( wxString() << m_throughViasCount );
196 m_MicroViasInfoMsg->SetLabel( wxString() << m_microViasCount );
197 m_BuriedViasInfoMsg->SetLabel( wxString() << m_blindOrBuriedViasCount );
198
199 // Output directory
201
202 wxCommandEvent dummy;
204}
205
206
207void DIALOG_GENDRILL::onFileFormatSelection( wxCommandEvent& event )
208{
209 bool enbl_Excellon = m_rbExcellon->GetValue();
210
211 m_drillFileType = enbl_Excellon ? 0 : 1;
212
213 m_Choice_Unit->Enable( enbl_Excellon );
214 m_Choice_Zeros_Format->Enable( enbl_Excellon );
215 m_Check_Mirror->Enable( enbl_Excellon );
216 m_Check_Minimal->Enable( enbl_Excellon );
217 m_Check_Merge_PTH_NPTH->Enable( enbl_Excellon );
218 m_radioBoxOvalHoleMode->Enable( enbl_Excellon );
219
220 if( enbl_Excellon )
221 {
223 }
224 else
225 {
226 m_staticTextPrecision->Enable( true );
227 m_staticTextPrecision->SetLabel( m_plotOpts.GetGerberPrecision() == 6 ? wxT( "4.6" )
228 : wxT( "4.5" ) );
229 }
230}
231
232
234{
236
237 auto cfg = m_pcbEditFrame->GetPcbNewSettings();
238
240 cfg->m_GenDrill.minimal_header = m_MinimalHeader;
241 cfg->m_GenDrill.mirror = m_Mirror;
242 cfg->m_GenDrill.unit_drill_is_inch = m_UnitDrillIsInch;
243 cfg->m_GenDrill.use_route_for_oval_holes = m_UseRouteModeForOvalHoles;
244 cfg->m_GenDrill.drill_file_type = m_drillFileType;
245 cfg->m_GenDrill.map_file_type = m_mapFileType;
246 cfg->m_GenDrill.zeros_format = m_ZerosFormat;
247}
248
249
250void DIALOG_GENDRILL::OnSelDrillUnitsSelected( wxCommandEvent& event )
251{
253}
254
255
256void DIALOG_GENDRILL::OnGenMapFile( wxCommandEvent& event )
257{
258 GenDrillAndMapFiles( false, true );
259}
260
261
262void DIALOG_GENDRILL::OnGenDrillFile( wxCommandEvent& event )
263{
264 GenDrillAndMapFiles( true, false );
265}
266
267
268void DIALOG_GENDRILL::OnSelZerosFmtSelected( wxCommandEvent& event )
269{
271}
272
273
275{
276 if( m_Choice_Unit->GetSelection()== 1 )
277 {
278 // Units = inches
280 }
281 else
282 {
283 // metric options
285 }
286
288 m_staticTextPrecision->Enable( false );
289 else
290 m_staticTextPrecision->Enable( true );
291}
292
293
295{
296 // Build the absolute path of current output directory to preselect it in the file browser.
297 wxString path = ExpandEnvVarSubstitutions( m_outputDirectoryName->GetValue(), &Prj() );
298 path = Prj().AbsolutePath( path );
299
300 wxDirDialog dirDialog( this, _( "Select Output Directory" ), path );
301
302 if( dirDialog.ShowModal() == wxID_CANCEL )
303 return;
304
305 wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() );
306 wxFileName fn( Prj().AbsolutePath( m_board->GetFileName() ) );
307 wxString defaultPath = fn.GetPathWithSep();
308 wxString msg;
309 msg.Printf( _( "Do you want to use a path relative to\n'%s'?" ), defaultPath );
310
311 wxMessageDialog dialog( this, msg, _( "Plot Output Directory" ),
312 wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT );
313
314 if( dialog.ShowModal() == wxID_YES )
315 {
316 if( !dirName.MakeRelativeTo( defaultPath ) )
317 {
318 wxMessageBox( _( "Cannot make path relative (target volume different from file volume)!" ),
319 _( "Plot Output Directory" ), wxOK | wxICON_ERROR );
320 }
321 }
322
323 m_outputDirectoryName->SetValue( dirName.GetFullPath() );
324}
325
326
328{
329 // Set output directory and replace backslashes with forward ones
330 wxString dirStr;
331 dirStr = m_outputDirectoryName->GetValue();
332 dirStr.Replace( wxT( "\\" ), wxT( "/" ) );
334 m_drillOriginIsAuxAxis = m_Choice_Drill_Offset->GetSelection() == 1;
336
337 m_mapFileType = m_Choice_Drill_Map->GetSelection();
338
339 m_UnitDrillIsInch = (m_Choice_Unit->GetSelection() == 0) ? false : true;
340 m_MinimalHeader = m_Check_Minimal->IsChecked();
341 m_Mirror = m_Check_Mirror->IsChecked();
343 m_ZerosFormat = m_Choice_Zeros_Format->GetSelection();
345
346 if( m_Choice_Drill_Offset->GetSelection() == 0 )
347 m_DrillFileOffset = VECTOR2I( 0, 0 );
348 else
350
353 else
355
357 {
360 }
361}
362
363
364void DIALOG_GENDRILL::GenDrillAndMapFiles( bool aGenDrill, bool aGenMap )
365{
366 UpdateConfig(); // set params and Save drill options
367
370
371 const PLOT_FORMAT filefmt[] = {
372 // Keep these format ids in the same order than m_Choice_Drill_Map choices
374 PLOT_FORMAT::GERBER, // Only X2 format because we need the .FileFunction attribute
378 };
379
380 unsigned choice = (unsigned) m_Choice_Drill_Map->GetSelection();
381
382 if( choice >= arrayDim( filefmt ) )
383 choice = arrayDim( filefmt )-1; // Last choice = PDF
384
385 // Create output directory if it does not exist (also transform it in absolute form).
386 // Bail if it fails.
387
388 std::function<bool( wxString* )> textResolver =
389 [&]( wxString* token ) -> bool
390 {
391 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
392 return m_board->ResolveTextVar( token, 0 );
393 };
394
396 path = ExpandTextVars( path, &textResolver, nullptr, nullptr );
397 path = ExpandEnvVarSubstitutions( path, nullptr );
398
399 wxFileName outputDir = wxFileName::DirName( path );
400 wxString boardFilename = m_board->GetFileName();
401
402 if( !EnsureFileDirectoryExists( &outputDir, boardFilename, &reporter ) )
403 {
404 wxString msg;
405 msg.Printf( _( "Could not write drill and/or map files to folder '%s'." ),
406 outputDir.GetPath() );
407 DisplayError( this, msg );
408 return;
409 }
410
411 if( m_drillFileType == 0 )
412 {
413 EXCELLON_WRITER excellonWriter( m_board );
418 excellonWriter.SetMapFileFormat( filefmt[choice] );
419
420 excellonWriter.CreateDrillandMapFilesSet( outputDir.GetFullPath(), aGenDrill, aGenMap,
421 &reporter );
422 }
423 else
424 {
425 GERBER_WRITER gerberWriter( m_board );
426 // Set gerber precision: only 5 or 6 digits for mantissa are allowed
427 // (SetFormat() accept 5 or 6, and any other value set the precision to 5)
428 // the integer part precision is always 4, and units always mm
429 gerberWriter.SetFormat( m_plotOpts.GetGerberPrecision() );
430 gerberWriter.SetOptions( m_DrillFileOffset );
431 gerberWriter.SetMapFileFormat( filefmt[choice] );
432
433 gerberWriter.CreateDrillandMapFilesSet( outputDir.GetFullPath(), aGenDrill, aGenMap,
434 &reporter );
435 }
436}
437
438
439void DIALOG_GENDRILL::OnGenReportFile( wxCommandEvent& event )
440{
441 UpdateConfig(); // set params and Save drill options
442
443 wxFileName fn = m_board->GetFileName();
444
445 fn.SetName( fn.GetName() + wxT( "-drl" ) );
446 fn.SetExt( ReportFileExtension );
447
448 wxString defaultPath = ExpandEnvVarSubstitutions( m_plotOpts.GetOutputDirectory(), &Prj() );
449 defaultPath = Prj().AbsolutePath( defaultPath );
450
451 if( defaultPath.IsEmpty() )
452 defaultPath = PATHS::GetDefaultUserProjectsPath();
453
454 wxFileDialog dlg( this, _( "Save Drill Report File" ), defaultPath, fn.GetFullName(),
455 ReportFileWildcard(), wxFD_SAVE );
456
457 if( dlg.ShowModal() == wxID_CANCEL )
458 return;
459
460 bool success;
461
462 // Info is slightly different between Excellon and Gerber
463 // (file ext, Merge PTH/NPTH option)
464 if( m_drillFileType == 0 )
465 {
466 EXCELLON_WRITER excellonWriter( m_board );
467 excellonWriter.SetMergeOption( m_Merge_PTH_NPTH );
468 success = excellonWriter.GenDrillReportFile( dlg.GetPath() );
469 }
470 else
471 {
472 GERBER_WRITER gerberWriter( m_board );
473 success = gerberWriter.GenDrillReportFile( dlg.GetPath() );
474 }
475
476 wxString msg;
477
478 if( ! success )
479 {
480 msg.Printf( _( "Failed to create file '%s'." ), dlg.GetPath() );
481 m_messagesBox->AppendText( msg );
482 }
483 else
484 {
485 msg.Printf( _( "Report file '%s' created." ), dlg.GetPath() );
486 m_messagesBox->AppendText( msg );
487 }
488}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
@ small_folder
const VECTOR2I & GetAuxOrigin()
int GenerateDrillFiles(const TOOL_EVENT &aEvent)
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition: board.h:630
bool ResolveTextVar(wxString *token, int aDepth) const
Definition: board.cpp:310
FOOTPRINTS & Footprints()
Definition: board.h:307
TRACKS & Tracks()
Definition: board.h:304
const wxString & GetFileName() const
Definition: board.h:302
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:629
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:643
Class DIALOG_GENDRILL_BASE.
wxRadioButton * m_rbExcellon
wxRadioBox * m_Choice_Drill_Offset
wxStaticText * m_staticTextPrecision
wxStaticText * m_MicroViasInfoMsg
wxBitmapButton * m_browseButton
wxRadioButton * m_rbGerberX2
wxStaticText * m_PlatedPadsCountInfoMsg
wxCheckBox * m_Check_Merge_PTH_NPTH
wxStaticText * m_ThroughViasInfoMsg
wxStaticText * m_BuriedViasInfoMsg
wxStaticText * m_NotPlatedPadsCountInfoMsg
wxRadioBox * m_radioBoxOvalHoleMode
wxTextCtrl * m_outputDirectoryName
wxRadioBox * m_Choice_Zeros_Format
PCB_PLOT_PARAMS m_plotOpts
VECTOR2I m_DrillFileOffset
void onFileFormatSelection(wxCommandEvent &event) override
void OnSelDrillUnitsSelected(wxCommandEvent &event) override
static bool m_MinimalHeader
static bool m_Merge_PTH_NPTH
PCB_EDIT_FRAME * m_pcbEditFrame
static bool m_Mirror
DIALOG_GENDRILL(PCB_EDIT_FRAME *aPcbEditFrame, wxWindow *aParent)
void OnGenReportFile(wxCommandEvent &event) override
void GenDrillAndMapFiles(bool aGenDrill, bool aGenMap)
Call the functions to create EXCELLON drill files and/or drill map files.
void OnSelZerosFmtSelected(wxCommandEvent &event) override
void OnGenDrillFile(wxCommandEvent &event) override
static int m_UnitDrillIsInch
void UpdateDrillParams()
Update board drill/plot parameters.
static bool m_UseRouteModeForOvalHoles
static int m_ZerosFormat
void OnGenMapFile(wxCommandEvent &event) override
void OnOutputDirectoryBrowseClicked(wxCommandEvent &event) override
static int m_mapFileType
DRILL_PRECISION m_Precision
static int m_drillFileType
void SetupStandardButtons(std::map< int, wxString > aLabels={})
Helper to handle drill precision format in excellon files.
virtual void ClearMsgPanel()
Clear all messages from the message panel.
Create Excellon drill, drill map, and drill report files.
void SetFormat(bool aMetric, ZEROS_FMT aZerosFmt=DECIMAL_FORMAT, int aLeftDigits=0, int aRightDigits=0)
Initialize internal parameters to match the given format.
void SetOptions(bool aMirror, bool aMinimalHeader, const VECTOR2I &aOffset, bool aMerge_PTH_NPTH)
Initialize internal parameters to match drill options.
void SetRouteModeForOvalHoles(bool aUseRouteModeForOvalHoles)
void CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board.
void SetMapFileFormat(PLOT_FORMAT aMapFmt)
Initialize the format for the drill map file.
bool GenDrillReportFile(const wxString &aFullFileName)
Create a plain text report file giving a list of drill values and drill count for through holes,...
void SetMergeOption(bool aMerge)
Set the option to make separate drill files for PTH and NPTH.
Used to create Gerber drill files.
void CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board filenames are computed from the board name,...
void SetOptions(const VECTOR2I &aOffset)
Initialize internal parameters to match drill options.
void SetFormat(int aRightDigits=6)
Initialize internal parameters to match the given format.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Definition: pad.h:59
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition: paths.cpp:139
DIALOG_GENERATE_DRILL m_GenDrill
virtual const PCB_PLOT_PARAMS & GetPlotSettings() const
Return the PCB_PLOT_PARAMS for the BOARD owned by this frame.
PCBNEW_SETTINGS * GetPcbNewSettings() const
BOARD * GetBoard() const
The main frame for Pcbnew.
void OnModify() override
Must be called after a board change to set the modified flag.
bool GetUseAuxOrigin() const
void SetOutputDirectory(const wxString &aDir)
wxString GetOutputDirectory() const
int GetGerberPrecision() const
bool IsSameAs(const PCB_PLOT_PARAMS &aPcbPlotParams) const
Compare current settings to aPcbPlotParams, including not saved parameters in brd file.
void SetUseAuxOrigin(bool aAux)
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition: project.cpp:305
Generic, UI-independent tool event.
Definition: tool_event.h:156
A wrapper for reporting to a wxTextCtrl object.
Definition: reporter.h:138
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:267
bool EnsureFileDirectoryExists(wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.
Definition: common.cpp:295
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:57
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
This file is part of the common library.
static DRILL_PRECISION precisionListForInches(2, 4)
static DRILL_PRECISION precisionListForMetric(3, 3)
#define _(s)
Classes used in drill files, map files and report files generation.
Classes used in drill files, map files and report files generation.
const std::string ReportFileExtension
wxString ReportFileWildcard()
@ NPTH
like PAD_PTH, but not plated
@ PAD_DRILL_SHAPE_CIRCLE
Definition: pad_shapes.h:70
@ BLIND_BURIED
PLOT_FORMAT
The set of supported output plot formats.
Definition: plotter.h:70
std::vector< FAB_LAYER_COLOR > dummy
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618
Definition of file extensions used in Kicad.