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 (C) 1992-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
31#include <bitmaps.h>
33#include <bom_plugins.h>
34#include <confirm.h>
35#include <dialog_bom_base.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/textdlg.h>
53
54wxString s_bomHelpInfo =
55#include <dialog_bom_help_md.h>
56;
57
58// BOM "plugins" are not actually plugins. They are external tools
59// (scripts or executables) called by this dialog.
60typedef std::vector< std::unique_ptr<BOM_GENERATOR_HANDLER> > BOM_GENERATOR_ARRAY;
61
62
63// The main dialog frame to run scripts to build bom
65{
66private:
70
72
73public:
74 DIALOG_BOM( SCH_EDIT_FRAME* parent );
76
77private:
78 void OnGeneratorSelected( wxCommandEvent& event ) override;
79 void OnRunGenerator( wxCommandEvent& event ) override;
80 void OnHelp( wxCommandEvent& event ) override;
81 void OnAddGenerator( wxCommandEvent& event ) override;
82 void OnRemoveGenerator( wxCommandEvent& event ) override;
83 void OnEditGenerator( wxCommandEvent& event ) override;
84 void OnCommandLineEdited( wxCommandEvent& event ) override;
85 void OnNameEdited( wxCommandEvent& event ) override;
86 void OnShowConsoleChanged( wxCommandEvent& event ) override;
87 void OnIdle( wxIdleEvent& event ) override;
88
89 void pluginInit();
91 BOM_GENERATOR_HANDLER* addGenerator( const wxString& aPath,
92 const wxString& aName = wxEmptyString );
93 bool pluginExists( const wxString& aName );
94
96 {
97 int idx = m_lbGenerators->GetSelection();
98
99 if( idx < 0 || idx >= (int)m_generators.size() )
100 return nullptr;
101
102 return m_generators[idx].get();
103 }
104
105 wxString chooseGenerator();
106};
107
108
109// Create and show DIALOG_BOM.
111{
112 DIALOG_BOM dlg( aCaller );
113
114 // QuasiModal so syntax help works
115 return dlg.ShowQuasiModal();
116}
117
118
120 DIALOG_BOM_BASE( parent ),
121 m_parent( parent ),
122 m_initialized( false ),
123 m_helpWindow( nullptr )
124{
125 m_buttonAddGenerator->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
126 m_buttonDelGenerator->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
127 m_buttonEdit->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
128
130
131#ifndef __WINDOWS__
132 m_checkBoxShowConsole->Show( false );
133#endif
134
135 SetupStandardButtons( { { wxID_OK, _( "Generate" ) },
136 { wxID_CANCEL, _( "Close" ) } } );
137
139
140 // Now all widgets have the size fixed, call FinishDialogSettings
142
143 m_buttonReset->Bind( wxEVT_BUTTON,
144 [&]( wxCommandEvent& )
145 {
147
148 cfg->m_BomPanel.selected_plugin = wxEmptyString;
150
152 } );
153}
154
155
157{
158 if( m_helpWindow )
159 m_helpWindow->Destroy();
160
162
163 cfg->m_BomPanel.plugins.clear();
164
165 for( const std::unique_ptr<BOM_GENERATOR_HANDLER>& plugin : m_generators )
166 {
167 wxString name = plugin->GetName();
168 wxFileName path( plugin->GetStoredPath() );
169
170 // handle empty nickname by stripping path
171 if( name.IsEmpty() )
172 name = path.GetName();
173
174 EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS setting( name, path.GetFullPath() );
175 setting.command = plugin->GetCommand();
176
177 cfg->m_BomPanel.plugins.emplace_back( setting );
178 }
179
180 cfg->m_BomPanel.selected_plugin = m_lbGenerators->GetStringSelection().ToStdString();
181}
182
183
184// Read the initialized plugins in config and fill the list of names
186{
188
189 wxString active_plugin_name = cfg->m_BomPanel.selected_plugin;
190
191 m_generators.clear();
192
194 {
195 auto plugin = std::make_unique<BOM_GENERATOR_HANDLER>( setting.path );
196
197 plugin->SetName( setting.name );
198
199 if( !setting.command.IsEmpty() )
200 plugin->SetCommand( setting.command );
201
202 m_generators.emplace_back( std::move( plugin ) );
203 }
204
205 m_lbGenerators->Clear();
206
207 if( !m_generators.empty() )
208 {
209 for( unsigned ii = 0; ii < m_generators.size(); ii++ )
210 {
211 wxString name = m_generators[ii]->GetName();
212
213 if( !m_generators[ii]->FindFilePath().Exists( wxFILE_EXISTS_REGULAR ) )
214 {
215 wxLogTrace( BOM_TRACE, wxS( "BOM plugin %s not found" ),
216 m_generators[ii]->FindFilePath().GetFullName() );
217 name.Append( wxT( " " ) + _( "(file missing)" ) );
218
219 if( active_plugin_name == name )
220 active_plugin_name.Clear();
221 }
222
223 m_lbGenerators->Append( name );
224
225 if( active_plugin_name == name )
226 m_lbGenerators->SetSelection( ii );
227 }
228 }
229
230 pluginInit();
231}
232
233
234BOM_GENERATOR_HANDLER* DIALOG_BOM::addGenerator( const wxString& aPath, const wxString& aName )
235{
236 BOM_GENERATOR_HANDLER* ret = nullptr;
237 auto plugin = std::make_unique<BOM_GENERATOR_HANDLER>( aPath );
238
239 if( !plugin->IsOk() )
240 return nullptr;
241
242 if( !aName.IsEmpty() )
243 {
244 plugin->SetName( aName );
245 m_lbGenerators->Append( aName );
246 }
247 else
248 {
249 m_lbGenerators->Append( plugin->GetName() );
250 }
251
252 ret = plugin.get();
253 m_generators.push_back( std::move( plugin ) );
254 return ret;
255}
256
257
258bool DIALOG_BOM::pluginExists( const wxString& aName )
259{
260 for( unsigned ii = 0; ii < m_generators.size(); ii++ )
261 {
262 if( aName == m_generators[ii]->GetName() )
263 return true;
264 }
265
266 return false;
267}
268
269
270void DIALOG_BOM::OnGeneratorSelected( wxCommandEvent& event )
271{
272 pluginInit();
273}
274
275
277{
279
280 if( !plugin )
281 {
282 m_textCtrlName->SetValue( wxEmptyString );
283 m_textCtrlCommand->SetValue( wxEmptyString );
284 m_Messages->SetValue( wxEmptyString );
285 return;
286 }
287
288 if( !plugin->FindFilePath().Exists( wxFILE_EXISTS_REGULAR ) )
289 {
290 m_textCtrlName->SetValue( wxEmptyString );
291 m_textCtrlCommand->SetValue( wxEmptyString );
292
293 wxString msg =
294 wxString::Format( _( "The selected BOM generator script %s could not be found." ),
295 plugin->GetFile().GetFullPath() );
296
297 if( !plugin->GetFile().IsAbsolute() )
298 {
299 msg.Append( wxString::Format( _( "\n\nSearched:\n\t%s\n\t%s" ),
302 }
303
304 m_Messages->SetValue( msg );
305 return;
306 }
307
308 m_textCtrlName->SetValue( plugin->GetName() );
309 m_textCtrlCommand->SetValue( plugin->GetCommand() );
310 m_Messages->SetValue( plugin->GetInfo() );
311 m_Messages->SetSelection( 0, 0 );
312
313#ifdef __WINDOWS__
314 if( plugin->Options().Index( wxT( "show_console" ) ) == wxNOT_FOUND )
315 m_checkBoxShowConsole->SetValue( false );
316 else
317 m_checkBoxShowConsole->SetValue( true );
318#endif
319
320 // A plugin can be not working, so do not left the OK button enabled if
321 // the plugin is not ready to use
322 m_sdbSizerOK->Enable( plugin->IsOk() );
323}
324
325
326void DIALOG_BOM::OnRunGenerator( wxCommandEvent& event )
327{
328 // Calculate the xml netlist filename
329 wxFileName fn = m_parent->Schematic().GetFileName();
330
331 fn.ClearExt();
332
333 wxString fullfilename = fn.GetFullPath();
335
336 wxString reportmsg;
337 WX_STRING_REPORTER reporter( &reportmsg );
339
340#ifdef __WINDOWS__
341 if( m_checkBoxShowConsole->IsChecked() )
342 m_parent->SetExecFlags( wxEXEC_SHOW_CONSOLE );
343#endif
344
345 bool status = false;
346
347 if( m_parent->ReadyToNetlist( _( "Generating BOM requires a fully annotated schematic." ) ) )
348 status = m_parent->WriteNetListFile( NET_TYPE_BOM, fullfilename, GNL_OPT_BOM|GNL_ALL, &reporter );
349
350 if( !status )
351 DisplayError( this, _( "Failed to create file." ) );
352
353 m_Messages->SetValue( reportmsg );
354
355 // Force focus back on the dialog
356 SetFocus();
357}
358
359
360void DIALOG_BOM::OnRemoveGenerator( wxCommandEvent& event )
361{
362 int ii = m_lbGenerators->GetSelection();
363
364 if( ii < 0 )
365 return;
366
367 m_lbGenerators->Delete( ii );
368 m_generators.erase( m_generators.begin() + ii );
369
370 // Select the next item, if exists
371 if( m_lbGenerators->GetCount() )
372 m_lbGenerators->SetSelection( std::min( ii, (int) m_lbGenerators->GetCount() - 1 ) );
373
374 pluginInit();
375}
376
377
378void DIALOG_BOM::OnAddGenerator( wxCommandEvent& event )
379{
380 wxString filename = chooseGenerator();
381
382 if( filename.IsEmpty() )
383 return;
384
385 // Creates a new plugin entry
386 wxFileName fn( filename );
387 wxString name = wxGetTextFromUser( _( "Generator nickname:" ), _( "Add Generator" ),
388 fn.GetName(), this );
389
390 if( name.IsEmpty() )
391 return;
392
393 // Verify if it does not exists
394 if( pluginExists( name ) )
395 {
396 wxMessageBox( wxString::Format( _( "Nickname '%s' already in use." ), name ) );
397 return;
398 }
399
400 try
401 {
402 auto plugin = addGenerator( fn.GetFullPath(), name );
403
404 if( plugin )
405 {
406 m_lbGenerators->SetSelection( m_lbGenerators->GetCount() - 1 );
407 m_textCtrlCommand->SetValue( plugin->GetCommand() );
408 pluginInit();
409 }
410 }
411 catch( const std::runtime_error& e )
412 {
413 DisplayError( this, e.what() );
414 }
415}
416
417
419{
420 static wxString lastPath;
421
422 if( lastPath.IsEmpty() )
423 lastPath = PATHS::GetUserPluginsPath();
424
425 wxString fullFileName = wxFileSelector( _( "Generator File" ), lastPath, wxEmptyString,
426 wxEmptyString, wxFileSelectorDefaultWildcardStr,
427 wxFD_OPEN, this );
428
429 return fullFileName;
430}
431
432
433void DIALOG_BOM::OnEditGenerator( wxCommandEvent& event )
434{
435 auto plugin = selectedGenerator();
436
437 if( !plugin )
438 return;
439
440 wxString pluginFile = plugin->GetFile().GetFullPath();
441
442 if( pluginFile.Length() <= 2 ) // if name != ""
443 {
444 wxMessageBox( _( "Generator file name not found." ) );
445 return;
446 }
447
448 wxString editorname = Pgm().GetTextEditor();
449
450 if( !editorname.IsEmpty() )
451 ExecuteFile( editorname, pluginFile );
452 else
453 wxMessageBox( _( "No text editor selected in KiCad. Please choose one." ) );
454}
455
456
457void DIALOG_BOM::OnHelp( wxCommandEvent& event )
458{
459 if( m_helpWindow )
460 {
462 return;
463 }
464
465 m_helpWindow = new HTML_MESSAGE_BOX( nullptr, _( "Bill of Materials Generation Help" ) );
466 m_helpWindow->SetDialogSizeInDU( 500, 350 );
467
468 wxString html_txt;
469 ConvertMarkdown2Html( wxGetTranslation( s_bomHelpInfo ), html_txt );
470
471 m_helpWindow->AddHTML_Text( html_txt );
473}
474
475
476void DIALOG_BOM::OnCommandLineEdited( wxCommandEvent& event )
477{
478 auto generator = selectedGenerator();
479
480 if( generator )
481 generator->SetCommand( m_textCtrlCommand->GetValue() );
482}
483
484
485void DIALOG_BOM::OnNameEdited( wxCommandEvent& event )
486{
487 if( m_textCtrlName->GetValue().IsEmpty() )
488 return;
489
490 int ii = m_lbGenerators->GetSelection();
491
492 if( ii < 0 )
493 return;
494
495 m_generators[ii]->SetName( m_textCtrlName->GetValue() );
496 m_lbGenerators->SetString( ii, m_generators[ii]->GetName() );
497}
498
499
500void DIALOG_BOM::OnShowConsoleChanged( wxCommandEvent& event )
501{
502#ifdef __WINDOWS__
503 static constexpr wxChar OPT_SHOW_CONSOLE[] = wxT( "show_console" );
504
505 auto plugin = selectedGenerator();
506
507 if( !plugin )
508 return;
509
510 if( m_checkBoxShowConsole->IsChecked() )
511 {
512 if( plugin->Options().Index( OPT_SHOW_CONSOLE ) == wxNOT_FOUND )
513 plugin->Options().Add( OPT_SHOW_CONSOLE );
514 }
515 else
516 {
517 plugin->Options().Remove( OPT_SHOW_CONSOLE );
518 }
519#endif
520}
521
522
523void DIALOG_BOM::OnIdle( wxIdleEvent& event )
524{
525 // On some platforms we initialize wxTextCtrls to all-selected, but we don't want that
526 // for the messages text box.
527 if( !m_initialized )
528 {
529 m_Messages->SetSelection( 0, 0 );
530 m_initialized = true;
531 }
532}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
const wxChar BOM_TRACE[]
Definition: bom_plugins.cpp:32
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:433
SCH_EDIT_FRAME * m_parent
Definition: dialog_bom.cpp:67
void OnRemoveGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:360
void installGeneratorsList()
Definition: dialog_bom.cpp:185
void pluginInit()
Definition: dialog_bom.cpp:276
bool m_initialized
Definition: dialog_bom.cpp:69
BOM_GENERATOR_HANDLER * selectedGenerator()
Definition: dialog_bom.cpp:95
bool pluginExists(const wxString &aName)
Definition: dialog_bom.cpp:258
BOM_GENERATOR_HANDLER * addGenerator(const wxString &aPath, const wxString &aName=wxEmptyString)
Definition: dialog_bom.cpp:234
void OnIdle(wxIdleEvent &event) override
Definition: dialog_bom.cpp:523
void OnHelp(wxCommandEvent &event) override
Definition: dialog_bom.cpp:457
void OnCommandLineEdited(wxCommandEvent &event) override
Definition: dialog_bom.cpp:476
wxString chooseGenerator()
Definition: dialog_bom.cpp:418
void OnGeneratorSelected(wxCommandEvent &event) override
Definition: dialog_bom.cpp:270
void OnNameEdited(wxCommandEvent &event) override
Definition: dialog_bom.cpp:485
void OnRunGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:326
HTML_MESSAGE_BOX * m_helpWindow
Definition: dialog_bom.cpp:71
void OnShowConsoleChanged(wxCommandEvent &event) override
Definition: dialog_bom.cpp:500
BOM_GENERATOR_ARRAY m_generators
Definition: dialog_bom.cpp:68
DIALOG_BOM(SCH_EDIT_FRAME *parent)
Definition: dialog_bom.cpp:119
void OnAddGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:378
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:97
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:54
static wxString GetStockPluginsPath()
Gets the stock (install) plugins path.
Definition: paths.cpp:278
wxString GetFileName() const override
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:281
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:164
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.
int InvokeDialogCreateBOM(SCH_EDIT_FRAME *aCaller)
Create and show DIALOG_BOM and return whatever DIALOG_BOM::ShowModal() returns.
Definition: dialog_bom.cpp:110
wxString s_bomHelpInfo
Definition: dialog_bom.cpp:54
std::vector< std::unique_ptr< BOM_GENERATOR_HANDLER > > BOM_GENERATOR_ARRAY
Definition: dialog_bom.cpp:60
#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:139
Some functions to handle hotkeys in KiCad.
@ NET_TYPE_BOM
Definition: netlist.h:37
#define GNL_ALL
@ GNL_OPT_BOM
see class PGM_BASE
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:119
void ConvertMarkdown2Html(const wxString &aMarkdownInput, wxString &aHtmlOutput)
std::vector< BOM_PLUGIN_SETTINGS > plugins