KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_bom.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 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, 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
31#include <bitmaps.h>
33#include <bom_plugins.h>
34#include <confirm.h>
35#include <dialog_bom.h>
37#include <eeschema_settings.h>
38#include <gestfich.h>
39#include <i18n_utility.h> // for _HKI definition used in dialog_bom_help_md.h
40#include <invoke_sch_dialog.h>
41#include <kiface_base.h>
42#include <netlist.h>
44#include <paths.h>
45#include <pgm_base.h>
46#include <reporter.h>
47#include <sch_edit_frame.h>
48#include <string_utils.h>
49
50#include <wx/filedlg.h>
51#include <wx/log.h>
52#include <wx/msgdlg.h>
53#include <wx/textdlg.h>
54
55wxString s_bomHelpInfo =
56#include <dialog_bom_help_md.h>
57;
58
59// Create and show DIALOG_BOM.
61{
62 DIALOG_BOM dlg( aCaller );
63
64 // QuasiModal so syntax help works
65 return dlg.ShowQuasiModal();
66}
67
68
70 DIALOG_BOM_BASE( parent ),
71 m_parent( parent ),
72 m_initialized( false ),
73 m_helpWindow( nullptr )
74{
75 m_buttonAddGenerator->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
76 m_buttonDelGenerator->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
77 m_buttonEdit->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
78
80
81#ifndef __WINDOWS__
82 m_checkBoxShowConsole->Show( false );
83#endif
84
85 SetupStandardButtons( { { wxID_OK, _( "Generate" ) },
86 { wxID_CANCEL, _( "Close" ) } } );
87
89
90 // Now all widgets have the size fixed, call FinishDialogSettings
92
93 m_buttonReset->Bind( wxEVT_BUTTON,
94 [&]( wxCommandEvent& )
95 {
97
98 cfg->m_BomPanel.selected_plugin = wxEmptyString;
100
102 } );
103}
104
105
107{
108 if( m_helpWindow )
109 m_helpWindow->Destroy();
110
112
113 cfg->m_BomPanel.plugins.clear();
114
115 for( const std::unique_ptr<BOM_GENERATOR_HANDLER>& plugin : m_generators )
116 {
117 wxString name = plugin->GetName();
118 wxFileName path( plugin->GetStoredPath() );
119
120 // handle empty nickname by stripping path
121 if( name.IsEmpty() )
122 name = path.GetName();
123
124 EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS setting( name, path.GetFullPath() );
125 setting.command = plugin->GetCommand();
126
127 cfg->m_BomPanel.plugins.emplace_back( setting );
128 }
129
130 cfg->m_BomPanel.selected_plugin = m_lbGenerators->GetStringSelection().ToStdString();
131}
132
133
134// Read the initialized plugins in config and fill the list of names
136{
138
139 wxString active_plugin_name = cfg->m_BomPanel.selected_plugin;
140
141 m_generators.clear();
142
144 {
145 auto plugin = std::make_unique<BOM_GENERATOR_HANDLER>( setting.path );
146
147 plugin->SetName( setting.name );
148
149 if( !setting.command.IsEmpty() )
150 plugin->SetCommand( setting.command );
151
152 m_generators.emplace_back( std::move( plugin ) );
153 }
154
155 m_lbGenerators->Clear();
156
157 if( !m_generators.empty() )
158 {
159 for( unsigned ii = 0; ii < m_generators.size(); ii++ )
160 {
161 wxString name = m_generators[ii]->GetName();
162
163 if( !m_generators[ii]->FindFilePath().Exists( wxFILE_EXISTS_REGULAR ) )
164 {
165 wxLogTrace( BOM_TRACE, wxS( "BOM plugin %s not found" ),
166 m_generators[ii]->FindFilePath().GetFullName() );
167 name.Append( wxT( " " ) + _( "(file missing)" ) );
168
169 if( active_plugin_name == name )
170 active_plugin_name.Clear();
171 }
172
173 m_lbGenerators->Append( name );
174
175 if( active_plugin_name == name )
176 m_lbGenerators->SetSelection( ii );
177 }
178 }
179
180 pluginInit();
181}
182
183
184BOM_GENERATOR_HANDLER* DIALOG_BOM::addGenerator( const wxString& aPath, const wxString& aName )
185{
186 BOM_GENERATOR_HANDLER* ret = nullptr;
187 auto plugin = std::make_unique<BOM_GENERATOR_HANDLER>( aPath );
188
189 if( !plugin->IsOk() )
190 return nullptr;
191
192 if( !aName.IsEmpty() )
193 {
194 plugin->SetName( aName );
195 m_lbGenerators->Append( aName );
196 }
197 else
198 {
199 m_lbGenerators->Append( plugin->GetName() );
200 }
201
202 ret = plugin.get();
203 m_generators.push_back( std::move( plugin ) );
204 return ret;
205}
206
207
208bool DIALOG_BOM::pluginExists( const wxString& aName )
209{
210 for( unsigned ii = 0; ii < m_generators.size(); ii++ )
211 {
212 if( aName == m_generators[ii]->GetName() )
213 return true;
214 }
215
216 return false;
217}
218
219
220void DIALOG_BOM::OnGeneratorSelected( wxCommandEvent& event )
221{
222 pluginInit();
223}
224
225
227{
229
230 if( !plugin )
231 {
232 m_textCtrlName->SetValue( wxEmptyString );
233 m_textCtrlCommand->SetValue( wxEmptyString );
234 m_Messages->SetValue( wxEmptyString );
235 return;
236 }
237
238 if( !plugin->FindFilePath().Exists( wxFILE_EXISTS_REGULAR ) )
239 {
240 m_textCtrlName->SetValue( wxEmptyString );
241 m_textCtrlCommand->SetValue( wxEmptyString );
242
243 wxString msg =
244 wxString::Format( _( "The selected BOM generator script %s could not be found." ),
245 plugin->GetFile().GetFullPath() );
246
247 if( !plugin->GetFile().IsAbsolute() )
248 {
249 msg.Append( wxString::Format( _( "\n\nSearched:\n\t%s\n\t%s" ),
252 }
253
254 m_Messages->SetValue( msg );
255 return;
256 }
257
258 m_textCtrlName->SetValue( plugin->GetName() );
259 m_textCtrlCommand->SetValue( plugin->GetCommand() );
260 m_Messages->SetValue( plugin->GetInfo() );
261 m_Messages->SetSelection( 0, 0 );
262
263#ifdef __WINDOWS__
264 if( plugin->Options().Index( wxT( "show_console" ) ) == wxNOT_FOUND )
265 m_checkBoxShowConsole->SetValue( false );
266 else
267 m_checkBoxShowConsole->SetValue( true );
268#endif
269
270 // A plugin can be not working, so do not left the OK button enabled if
271 // the plugin is not ready to use
272 m_sdbSizerOK->Enable( plugin->IsOk() );
273}
274
275
276void DIALOG_BOM::OnRunGenerator( wxCommandEvent& event )
277{
278 // Calculate the xml netlist filename
279 wxFileName fn = m_parent->Schematic().GetFileName();
280
281 fn.ClearExt();
282
283 wxString fullfilename = fn.GetFullPath();
285
286 WX_STRING_REPORTER reporter;
288
289#ifdef __WINDOWS__
290 if( m_checkBoxShowConsole->IsChecked() )
291 m_parent->SetExecFlags( wxEXEC_SHOW_CONSOLE );
292#endif
293
294 bool status = false;
295
296 if( m_parent->ReadyToNetlist( _( "Generating BOM requires a fully annotated schematic." ) ) )
297 status = m_parent->WriteNetListFile( NET_TYPE_BOM, fullfilename,
298 GNL_OPT_BOM | GNL_ALL, &reporter );
299
300 if( !status )
301 DisplayErrorMessage( this, _( "Failed to create file." ) );
302
303 m_Messages->SetValue( reporter.GetMessages() );
304
305 // Force focus back on the dialog
306 SetFocus();
307}
308
309
310void DIALOG_BOM::OnRemoveGenerator( wxCommandEvent& event )
311{
312 int ii = m_lbGenerators->GetSelection();
313
314 if( ii < 0 )
315 return;
316
317 m_lbGenerators->Delete( ii );
318 m_generators.erase( m_generators.begin() + ii );
319
320 // Select the next item, if exists
321 if( m_lbGenerators->GetCount() )
322 m_lbGenerators->SetSelection( std::min( ii, (int) m_lbGenerators->GetCount() - 1 ) );
323
324 pluginInit();
325}
326
327
328void DIALOG_BOM::OnAddGenerator( wxCommandEvent& event )
329{
330 wxString filename = chooseGenerator();
331
332 if( filename.IsEmpty() )
333 return;
334
335 // Creates a new plugin entry
336 wxFileName fn( filename );
337 wxString name = wxGetTextFromUser( _( "Generator nickname:" ), _( "Add Generator" ),
338 fn.GetName(), this );
339
340 if( name.IsEmpty() )
341 return;
342
343 // Verify if it does not exists
344 if( pluginExists( name ) )
345 {
346 wxMessageBox( wxString::Format( _( "Nickname '%s' already in use." ), name ) );
347 return;
348 }
349
350 try
351 {
352 auto plugin = addGenerator( fn.GetFullPath(), name );
353
354 if( plugin )
355 {
356 m_lbGenerators->SetSelection( m_lbGenerators->GetCount() - 1 );
357 m_textCtrlCommand->SetValue( plugin->GetCommand() );
358 pluginInit();
359 }
360 }
361 catch( const std::runtime_error& e )
362 {
363 DisplayErrorMessage( this, e.what() );
364 }
365}
366
367
369{
370 static wxString lastPath;
371
372 if( lastPath.IsEmpty() )
373 lastPath = PATHS::GetUserPluginsPath();
374
375 wxString fullFileName = wxFileSelector( _( "Generator File" ), lastPath, wxEmptyString,
376 wxEmptyString, wxFileSelectorDefaultWildcardStr,
377 wxFD_OPEN, this );
378
379 return fullFileName;
380}
381
382
383void DIALOG_BOM::OnEditGenerator( wxCommandEvent& event )
384{
385 auto plugin = selectedGenerator();
386
387 if( !plugin )
388 return;
389
390 wxString pluginFile = plugin->GetFile().GetFullPath();
391
392 if( pluginFile.Length() <= 2 ) // if name != ""
393 {
394 wxMessageBox( _( "Generator file name not found." ) );
395 return;
396 }
397
398 wxString editorname = Pgm().GetTextEditor();
399
400 if( !editorname.IsEmpty() )
401 ExecuteFile( editorname, pluginFile );
402 else
403 wxMessageBox( _( "No text editor selected in KiCad. Please choose one." ) );
404}
405
406
407void DIALOG_BOM::OnHelp( wxCommandEvent& event )
408{
409 if( m_helpWindow )
410 {
412 return;
413 }
414
415 m_helpWindow = new HTML_MESSAGE_BOX( nullptr, _( "Bill of Materials Generation Help" ) );
416 m_helpWindow->SetDialogSizeInDU( 500, 350 );
417
418 wxString html_txt;
419 ConvertMarkdown2Html( wxGetTranslation( s_bomHelpInfo ), html_txt );
420
421 m_helpWindow->AddHTML_Text( html_txt );
423}
424
425
426void DIALOG_BOM::OnCommandLineEdited( wxCommandEvent& event )
427{
428 auto generator = selectedGenerator();
429
430 if( generator )
431 generator->SetCommand( m_textCtrlCommand->GetValue() );
432}
433
434
435void DIALOG_BOM::OnNameEdited( wxCommandEvent& event )
436{
437 if( m_textCtrlName->GetValue().IsEmpty() )
438 return;
439
440 int ii = m_lbGenerators->GetSelection();
441
442 if( ii < 0 )
443 return;
444
445 m_generators[ii]->SetName( m_textCtrlName->GetValue() );
446 m_lbGenerators->SetString( ii, m_generators[ii]->GetName() );
447}
448
449
450void DIALOG_BOM::OnShowConsoleChanged( wxCommandEvent& event )
451{
452#ifdef __WINDOWS__
453 static constexpr wxChar OPT_SHOW_CONSOLE[] = wxT( "show_console" );
454
455 auto plugin = selectedGenerator();
456
457 if( !plugin )
458 return;
459
460 if( m_checkBoxShowConsole->IsChecked() )
461 {
462 if( plugin->Options().Index( OPT_SHOW_CONSOLE ) == wxNOT_FOUND )
463 plugin->Options().Add( OPT_SHOW_CONSOLE );
464 }
465 else
466 {
467 plugin->Options().Remove( OPT_SHOW_CONSOLE );
468 }
469#endif
470}
471
472
473void DIALOG_BOM::OnIdle( wxIdleEvent& event )
474{
475 // On some platforms we initialize wxTextCtrls to all-selected, but we don't want that
476 // for the messages text box.
477 if( !m_initialized )
478 {
479 m_Messages->SetSelection( 0, 0 );
480 m_initialized = true;
481 }
482}
483
484
486{
487 int idx = m_lbGenerators->GetSelection();
488
489 if( idx < 0 || idx >= (int) m_generators.size() )
490 return nullptr;
491
492 return m_generators[idx].get();
493}
const char * name
Definition: DXF_plotter.cpp:59
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
const wxChar BOM_TRACE[]
Definition: bom_plugins.cpp:33
Bill of material output generator.
Definition: bom_plugins.h:46
const wxString & GetInfo() const
Return plugin description stored in the plugin header file (if available).
Definition: bom_plugins.h:68
const wxFileName & GetFile() const
Return the file name of the plugin.
Definition: bom_plugins.h:76
bool IsOk()
Return true if the plugin is ready to work, i.e.
Definition: bom_plugins.h:56
wxArrayString & Options()
Accessor to array of options.
Definition: bom_plugins.h:130
const wxString & GetName() const
Return the customisable plugin name.
Definition: bom_plugins.h:96
wxFileName FindFilePath() const
Returns the calculated path to the plugin: if the path is already absolute and exists,...
const wxString & GetCommand() const
Return the command to execute the plugin.
Definition: bom_plugins.h:114
Class DIALOG_BOM_BASE.
wxButton * m_buttonReset
STD_BITMAP_BUTTON * m_buttonEdit
wxTextCtrl * m_textCtrlName
STD_BITMAP_BUTTON * m_buttonAddGenerator
STD_BITMAP_BUTTON * m_buttonDelGenerator
wxTextCtrl * m_Messages
wxCheckBox * m_checkBoxShowConsole
wxTextCtrl * m_textCtrlCommand
wxListBox * m_lbGenerators
wxButton * m_sdbSizerOK
void OnEditGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:383
SCH_EDIT_FRAME * m_parent
Definition: dialog_bom.h:42
void OnRemoveGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:310
void installGeneratorsList()
Definition: dialog_bom.cpp:135
void pluginInit()
Definition: dialog_bom.cpp:226
bool m_initialized
Definition: dialog_bom.h:44
BOM_GENERATOR_HANDLER * selectedGenerator()
Definition: dialog_bom.cpp:485
bool pluginExists(const wxString &aName)
Definition: dialog_bom.cpp:208
BOM_GENERATOR_HANDLER * addGenerator(const wxString &aPath, const wxString &aName=wxEmptyString)
Definition: dialog_bom.cpp:184
void OnIdle(wxIdleEvent &event) override
Definition: dialog_bom.cpp:473
void OnHelp(wxCommandEvent &event) override
Definition: dialog_bom.cpp:407
void OnCommandLineEdited(wxCommandEvent &event) override
Definition: dialog_bom.cpp:426
wxString chooseGenerator()
Definition: dialog_bom.cpp:368
void OnGeneratorSelected(wxCommandEvent &event) override
Definition: dialog_bom.cpp:220
void OnNameEdited(wxCommandEvent &event) override
Definition: dialog_bom.cpp:435
void OnRunGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:276
HTML_MESSAGE_BOX * m_helpWindow
Definition: dialog_bom.h:46
void OnShowConsoleChanged(wxCommandEvent &event) override
Definition: dialog_bom.cpp:450
BOM_GENERATOR_ARRAY m_generators
Definition: dialog_bom.h:43
DIALOG_BOM(SCH_EDIT_FRAME *parent)
Definition: dialog_bom.cpp:69
void OnAddGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:328
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:102
void SetupStandardButtons(std::map< int, wxString > aLabels={})
int ShowQuasiModal()
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
virtual void ClearMsgPanel()
Clear all messages from the message panel.
static std::vector< BOM_PLUGIN_SETTINGS > DefaultBomPlugins()
void SetDialogSizeInDU(int aWidth, int aHeight)
Set the dialog size, using a "logical" value.
void AddHTML_Text(const wxString &message)
Add HTML text (without any change) to message list.
void ShowModeless()
Show a modeless version of the dialog (without an OK button).
static wxString GetUserPluginsPath()
Gets the user path for plugins.
Definition: paths.cpp:55
static wxString GetStockPluginsPath()
Gets the stock (install) plugins path.
Definition: paths.cpp:332
virtual const wxString & GetTextEditor(bool aCanShowFileChooser=true)
Return the path to the preferred text editor application.
Definition: pgm_base.cpp:196
wxString GetFileName() const override
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:306
EESCHEMA_SETTINGS * eeconfig() const
Schematic editor (Eeschema) main window.
void SetExecFlags(const int aFlags)
Set (adds) specified flags for next execution of external generator of the netlist or bom.
bool ReadyToNetlist(const wxString &aAnnotateMessage)
Check if we are ready to write a netlist file for the current schematic.
bool WriteNetListFile(int aFormat, const wxString &aFullFileName, unsigned aNetlistOptions, REPORTER *aReporter=nullptr)
Create a netlist file.
SCHEMATIC & Schematic() const
void SetNetListerCommand(const wxString &aCommand)
void SetBitmap(const wxBitmapBundle &aBmp)
A wrapper for reporting to a wxString object.
Definition: reporter.h:171
const wxString & GetMessages() const
Definition: reporter.cpp:84
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
This file is part of the common library.
int InvokeDialogCreateBOM(SCH_EDIT_FRAME *aCaller)
Create and show DIALOG_BOM and return whatever DIALOG_BOM::ShowModal() returns.
Definition: dialog_bom.cpp:60
wxString s_bomHelpInfo
Definition: dialog_bom.cpp:55
#define _(s)
int ExecuteFile(const wxString &aEditorName, const wxString &aFileName, wxProcess *aCallback, bool aFileForKicad)
Call the executable file aEditorName with the parameter aFileName.
Definition: gestfich.cpp:143
Some functions to handle hotkeys in KiCad.
@ NET_TYPE_BOM
Definition: netlist.h:37
#define GNL_ALL
@ GNL_OPT_BOM
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
void ConvertMarkdown2Html(const wxString &aMarkdownInput, wxString &aHtmlOutput)
std::vector< BOM_PLUGIN_SETTINGS > plugins