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.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, GNL_OPT_BOM|GNL_ALL, &reporter );
298
299 if( !status )
300 DisplayError( this, _( "Failed to create file." ) );
301
302 m_Messages->SetValue( reporter.GetMessages() );
303
304 // Force focus back on the dialog
305 SetFocus();
306}
307
308
309void DIALOG_BOM::OnRemoveGenerator( wxCommandEvent& event )
310{
311 int ii = m_lbGenerators->GetSelection();
312
313 if( ii < 0 )
314 return;
315
316 m_lbGenerators->Delete( ii );
317 m_generators.erase( m_generators.begin() + ii );
318
319 // Select the next item, if exists
320 if( m_lbGenerators->GetCount() )
321 m_lbGenerators->SetSelection( std::min( ii, (int) m_lbGenerators->GetCount() - 1 ) );
322
323 pluginInit();
324}
325
326
327void DIALOG_BOM::OnAddGenerator( wxCommandEvent& event )
328{
329 wxString filename = chooseGenerator();
330
331 if( filename.IsEmpty() )
332 return;
333
334 // Creates a new plugin entry
335 wxFileName fn( filename );
336 wxString name = wxGetTextFromUser( _( "Generator nickname:" ), _( "Add Generator" ),
337 fn.GetName(), this );
338
339 if( name.IsEmpty() )
340 return;
341
342 // Verify if it does not exists
343 if( pluginExists( name ) )
344 {
345 wxMessageBox( wxString::Format( _( "Nickname '%s' already in use." ), name ) );
346 return;
347 }
348
349 try
350 {
351 auto plugin = addGenerator( fn.GetFullPath(), name );
352
353 if( plugin )
354 {
355 m_lbGenerators->SetSelection( m_lbGenerators->GetCount() - 1 );
356 m_textCtrlCommand->SetValue( plugin->GetCommand() );
357 pluginInit();
358 }
359 }
360 catch( const std::runtime_error& e )
361 {
362 DisplayError( this, e.what() );
363 }
364}
365
366
368{
369 static wxString lastPath;
370
371 if( lastPath.IsEmpty() )
372 lastPath = PATHS::GetUserPluginsPath();
373
374 wxString fullFileName = wxFileSelector( _( "Generator File" ), lastPath, wxEmptyString,
375 wxEmptyString, wxFileSelectorDefaultWildcardStr,
376 wxFD_OPEN, this );
377
378 return fullFileName;
379}
380
381
382void DIALOG_BOM::OnEditGenerator( wxCommandEvent& event )
383{
384 auto plugin = selectedGenerator();
385
386 if( !plugin )
387 return;
388
389 wxString pluginFile = plugin->GetFile().GetFullPath();
390
391 if( pluginFile.Length() <= 2 ) // if name != ""
392 {
393 wxMessageBox( _( "Generator file name not found." ) );
394 return;
395 }
396
397 wxString editorname = Pgm().GetTextEditor();
398
399 if( !editorname.IsEmpty() )
400 ExecuteFile( editorname, pluginFile );
401 else
402 wxMessageBox( _( "No text editor selected in KiCad. Please choose one." ) );
403}
404
405
406void DIALOG_BOM::OnHelp( wxCommandEvent& event )
407{
408 if( m_helpWindow )
409 {
411 return;
412 }
413
414 m_helpWindow = new HTML_MESSAGE_BOX( nullptr, _( "Bill of Materials Generation Help" ) );
415 m_helpWindow->SetDialogSizeInDU( 500, 350 );
416
417 wxString html_txt;
418 ConvertMarkdown2Html( wxGetTranslation( s_bomHelpInfo ), html_txt );
419
420 m_helpWindow->AddHTML_Text( html_txt );
422}
423
424
425void DIALOG_BOM::OnCommandLineEdited( wxCommandEvent& event )
426{
427 auto generator = selectedGenerator();
428
429 if( generator )
430 generator->SetCommand( m_textCtrlCommand->GetValue() );
431}
432
433
434void DIALOG_BOM::OnNameEdited( wxCommandEvent& event )
435{
436 if( m_textCtrlName->GetValue().IsEmpty() )
437 return;
438
439 int ii = m_lbGenerators->GetSelection();
440
441 if( ii < 0 )
442 return;
443
444 m_generators[ii]->SetName( m_textCtrlName->GetValue() );
445 m_lbGenerators->SetString( ii, m_generators[ii]->GetName() );
446}
447
448
449void DIALOG_BOM::OnShowConsoleChanged( wxCommandEvent& event )
450{
451#ifdef __WINDOWS__
452 static constexpr wxChar OPT_SHOW_CONSOLE[] = wxT( "show_console" );
453
454 auto plugin = selectedGenerator();
455
456 if( !plugin )
457 return;
458
459 if( m_checkBoxShowConsole->IsChecked() )
460 {
461 if( plugin->Options().Index( OPT_SHOW_CONSOLE ) == wxNOT_FOUND )
462 plugin->Options().Add( OPT_SHOW_CONSOLE );
463 }
464 else
465 {
466 plugin->Options().Remove( OPT_SHOW_CONSOLE );
467 }
468#endif
469}
470
471
472void DIALOG_BOM::OnIdle( wxIdleEvent& event )
473{
474 // On some platforms we initialize wxTextCtrls to all-selected, but we don't want that
475 // for the messages text box.
476 if( !m_initialized )
477 {
478 m_Messages->SetSelection( 0, 0 );
479 m_initialized = true;
480 }
481}
482
483
485{
486 int idx = m_lbGenerators->GetSelection();
487
488 if( idx < 0 || idx >= (int) m_generators.size() )
489 return nullptr;
490
491 return m_generators[idx].get();
492}
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:382
SCH_EDIT_FRAME * m_parent
Definition: dialog_bom.h:42
void OnRemoveGenerator(wxCommandEvent &event) override
Definition: dialog_bom.cpp:309
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:484
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:472
void OnHelp(wxCommandEvent &event) override
Definition: dialog_bom.cpp:406
void OnCommandLineEdited(wxCommandEvent &event) override
Definition: dialog_bom.cpp:425
wxString chooseGenerator()
Definition: dialog_bom.cpp:367
void OnGeneratorSelected(wxCommandEvent &event) override
Definition: dialog_bom.cpp:220
void OnNameEdited(wxCommandEvent &event) override
Definition: dialog_bom.cpp:434
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:449
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:327
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:54
static wxString GetStockPluginsPath()
Gets the stock (install) plugins path.
Definition: paths.cpp:288
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:308
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 DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:170
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:1060
see class PGM_BASE
void ConvertMarkdown2Html(const wxString &aMarkdownInput, wxString &aHtmlOutput)
std::vector< BOM_PLUGIN_SETTINGS > plugins