KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kicad.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) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2004-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 <wx/filename.h>
32#include <wx/log.h>
33#include <wx/app.h>
34#include <wx/stdpaths.h>
35#include <wx/msgdlg.h>
36#include <wx/cmdline.h>
37
38#include <env_vars.h>
39#include <file_history.h>
40#include <hotkeys_basic.h>
41#include <kiway.h>
42#include <macros.h>
43#include <paths.h>
44#include <richio.h>
47#include <systemdirsappend.h>
48#include <trace_helpers.h>
50#include <confirm.h>
51
52#include <git2.h>
53#include <stdexcept>
54
55#include "pgm_kicad.h"
56#include "kicad_manager_frame.h"
57
58#include <kiplatform/app.h>
60
61#ifdef KICAD_IPC_API
62#include <api/api_server.h>
63#endif
64
65// a dummy to quiet linking with EDA_BASE_FRAME::config();
66#include <kiface_base.h>
68{
69 // This function should never be called. It is only referenced from
70 // EDA_BASE_FRAME::config() and this is only provided to satisfy the linker,
71 // not to be actually called.
72 wxLogFatalError( wxT( "Unexpected call to Kiface() in kicad/kicad.cpp" ) );
73
74 throw std::logic_error( "Unexpected call to Kiface() in kicad/kicad.cpp" );
75}
76
77
79
81{
82 return program;
83}
84
85
87{
88 App().SetAppDisplayName( wxT( "KiCad" ) );
89
90#if defined(DEBUG)
91 wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
92
93 if( !wxIsAbsolutePath( absoluteArgv0 ) )
94 {
95 wxLogError( wxT( "No meaningful argv[0]" ) );
96 return false;
97 }
98#endif
99
100 // Initialize the git library before trying to initialize individual programs
101 git_libgit2_init();
102
103 static const wxCmdLineEntryDesc desc[] = {
104 { wxCMD_LINE_OPTION, "f", "frame", "Frame to load", wxCMD_LINE_VAL_STRING, 0 },
105 { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
106 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
107 { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
108 };
109
110 wxCmdLineParser parser( App().argc, App().argv );
111 parser.SetDesc( desc );
112 parser.Parse( false );
113
114 FRAME_T appType = KICAD_MAIN_FRAME_T;
115
116 const struct
117 {
118 wxString name;
119 FRAME_T type;
120 } frameTypes[] = { { wxT( "pcb" ), FRAME_PCB_EDITOR },
121 { wxT( "fpedit" ), FRAME_FOOTPRINT_EDITOR },
122 { wxT( "sch" ), FRAME_SCH },
123 { wxT( "calc" ), FRAME_CALC },
124 { wxT( "bm2cmp" ), FRAME_BM2CMP },
125 { wxT( "ds" ), FRAME_PL_EDITOR },
126 { wxT( "gerb" ), FRAME_GERBER },
127 { wxT( "" ), FRAME_T_COUNT } };
128
129 wxString frameName;
130
131 if( parser.Found( "frame", &frameName ) )
132 {
133 appType = FRAME_T_COUNT;
134
135 for( const auto& it : frameTypes )
136 {
137 if( it.name == frameName )
138 appType = it.type;
139 }
140
141 if( appType == FRAME_T_COUNT )
142 {
143 wxLogError( wxT( "Unknown frame: %s" ), frameName );
144 // Clean up
145 OnPgmExit();
146 return false;
147 }
148 }
149
150 if( appType == KICAD_MAIN_FRAME_T )
151 {
153 }
154 else
155 {
157 }
158
159 bool skipPythonInit = false;
160
161 if( appType == FRAME_BM2CMP || appType == FRAME_PL_EDITOR || appType == FRAME_GERBER
162 || appType == FRAME_CALC )
163 skipPythonInit = true;
164
165 if( !InitPgm( false, skipPythonInit ) )
166 return false;
167
171 m_bm.Init();
172
173 // Add search paths to feed the PGM_KICAD::SysSearch() function,
174 // currently limited in support to only look for project templates
175 {
176 SEARCH_STACK bases;
177
178 SystemDirsAppend( &bases );
179
180 for( unsigned i = 0; i < bases.GetCount(); ++i )
181 {
182 wxFileName fn( bases[i], wxEmptyString );
183
184 // Add KiCad template file path to search path list.
185 fn.AppendDir( wxT( "template" ) );
186
187 // Only add path if exists and can be read by the user.
188 if( fn.DirExists() && fn.IsDirReadable() )
189 m_bm.m_search.AddPaths( fn.GetPath() );
190 }
191
192 // The versioned TEMPLATE_DIR takes precedence over the search stack template path.
193 if( std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( GetLocalEnvVariables(),
194 wxT( "TEMPLATE_DIR" ) ) )
195 {
196 if( !v->IsEmpty() )
197 m_bm.m_search.Insert( *v, 0 );
198 }
199
200 // We've been adding system (installed default) search paths so far, now for user paths
201 // The default user search path is inside KIPLATFORM::ENV::GetDocumentsPath()
203
204 // ...but the user can override that default with the KICAD_USER_TEMPLATE_DIR env var
205 ENV_VAR_MAP_CITER it = GetLocalEnvVariables().find( "KICAD_USER_TEMPLATE_DIR" );
206
207 if( it != GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString )
208 m_bm.m_search.Insert( it->second.GetValue(), 0 );
209 }
210
211 wxFrame* frame = nullptr;
212 KIWAY_PLAYER* playerFrame = nullptr;
213 KICAD_MANAGER_FRAME* managerFrame = nullptr;
214
215 if( appType == KICAD_MAIN_FRAME_T )
216 {
217 managerFrame = new KICAD_MANAGER_FRAME( nullptr, wxT( "KiCad" ), wxDefaultPosition,
218 wxWindow::FromDIP( wxSize( 775, -1 ), NULL ) );
219 frame = managerFrame;
220 }
221 else
222 {
223 // Use KIWAY to create a top window, which registers its existence also.
224 // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
225 // and is one of the types in FRAME_T.
226 playerFrame = Kiway.Player( appType, true );
227 frame = playerFrame;
228
229 if( frame == nullptr )
230 {
231 return false;
232 }
233 }
234
235 App().SetTopWindow( frame );
236
237 if( playerFrame )
238 App().SetAppDisplayName( playerFrame->GetAboutTitle() );
239
240 Kiway.SetTop( frame );
241
242 KICAD_SETTINGS* settings = static_cast<KICAD_SETTINGS*>( PgmSettings() );
243
244#ifdef KICAD_IPC_API
245 m_api_server = std::make_unique<KICAD_API_SERVER>();
246#endif
247
248 wxString projToLoad;
249
250 HideSplash();
251
252 if( playerFrame && parser.GetParamCount() )
253 {
254 // Now after the frame processing, the rest of the positional args are files
255 std::vector<wxString> fileArgs;
256 /*
257 gerbview handles multiple project data files, i.e. gerber files on
258 cmd line. Others currently do not, they handle only one. For common
259 code simplicity we simply pass all the arguments in however, each
260 program module can do with them what they want, ignore, complain
261 whatever. We don't establish policy here, as this is a multi-purpose
262 launcher.
263 */
264
265 for( size_t i = 0; i < parser.GetParamCount(); i++ )
266 fileArgs.push_back( parser.GetParam( i ) );
267
268 // special attention to a single argument: argv[1] (==argSet[0])
269 if( fileArgs.size() == 1 )
270 {
271 wxFileName argv1( fileArgs[0] );
272
273#if defined( PGM_DATA_FILE_EXT )
274 // PGM_DATA_FILE_EXT, if present, may be different for each compile,
275 // it may come from CMake on the compiler command line, but often does not.
276 // This facility is mostly useful for those program footprints
277 // supporting a single argv[1].
278 if( !argv1.GetExt() )
279 argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
280#endif
281 argv1.MakeAbsolute();
282
283 fileArgs[0] = argv1.GetFullPath();
284 }
285
286 // Use the KIWAY_PLAYER::OpenProjectFiles() API function:
287 if( !playerFrame->OpenProjectFiles( fileArgs ) )
288 {
289 // OpenProjectFiles() API asks that it report failure to the UI.
290 // Nothing further to say here.
291
292 // We've already initialized things at this point, but wx won't call OnExit if
293 // we fail out. Call our own cleanup routine here to ensure the relevant resources
294 // are freed at the right time (if they aren't, segfaults will occur).
295 OnPgmExit();
296
297 // Fail the process startup if the file could not be opened,
298 // although this is an optional choice, one that can be reversed
299 // also in the KIFACE specific OpenProjectFiles() return value.
300 return false;
301 }
302 }
303 else if( managerFrame )
304 {
305 if( App().argc > 1 )
306 {
307 wxFileName tmp = App().argv[1];
308
309 if( tmp.GetExt() != FILEEXT::ProjectFileExtension
310 && tmp.GetExt() != FILEEXT::LegacyProjectFileExtension )
311 {
312 wxString msg;
313
314 msg.Printf( _( "File '%s'\ndoes not appear to be a valid KiCad project file." ),
315 tmp.GetFullPath() );
316 wxMessageDialog dlg( nullptr, msg, _( "Error" ), wxOK | wxICON_EXCLAMATION );
317 dlg.ShowModal();
318 }
319 else
320 {
321 projToLoad = tmp.GetFullPath();
322 }
323 }
324
325 // If no file was given as an argument, check that there was a file open.
326 if( projToLoad.IsEmpty() && settings->m_OpenProjects.size() )
327 {
328 wxString last_pro = settings->m_OpenProjects.front();
329 settings->m_OpenProjects.erase( settings->m_OpenProjects.begin() );
330
331 if( wxFileExists( last_pro ) )
332 {
333 // Try to open the last opened project,
334 // if a project name is not given when starting Kicad
335 projToLoad = last_pro;
336 }
337 }
338
339 // Do not attempt to load a non-existent project file.
340 if( !projToLoad.empty() )
341 {
342 wxFileName fn( projToLoad );
343
344 if( fn.Exists() )
345 {
346 fn.MakeAbsolute();
347
348 if( appType == KICAD_MAIN_FRAME_T )
349 {
350 managerFrame->LoadProject( fn );
351 }
352 }
353 }
354 }
355
356 frame->Show( true );
357 frame->Raise();
358
359#ifdef KICAD_IPC_API
360 m_api_server->SetReadyToReply();
361#endif
362
363 return true;
364}
365
366
368{
369 return 0;
370}
371
372
374{
376
377#ifdef KICAD_IPC_API
378 m_api_server.reset();
379#endif
380
382 {
384 m_settings_manager->Save();
385 }
386
387 // Destroy everything in PGM_KICAD,
388 // especially wxSingleInstanceCheckerImpl earlier than wxApp and earlier
389 // than static destruction would.
390 Destroy();
391 git_libgit2_shutdown();
392}
393
394
395void PGM_KICAD::MacOpenFile( const wxString& aFileName )
396{
397#if defined(__WXMAC__)
398
399 KICAD_MANAGER_FRAME* frame = (KICAD_MANAGER_FRAME*) App().GetTopWindow();
400
401 if( !aFileName.empty() && wxFileExists( aFileName ) )
402 frame->LoadProject( wxFileName( aFileName ) );
403
404#endif
405}
406
407
409{
410 // unlike a normal destructor, this is designed to be called more
411 // than once safely:
412
413 m_bm.End();
414
416}
417
418
420
421#ifdef NDEBUG
422// Define a custom assertion handler
423void CustomAssertHandler(const wxString& file,
424 int line,
425 const wxString& func,
426 const wxString& cond,
427 const wxString& msg)
428{
429 Pgm().HandleAssert( file, line, func, cond, msg );
430}
431#endif
432
436struct APP_KICAD : public wxApp
437{
438 APP_KICAD() : wxApp()
439 {
440 SetPgm( &program );
441
442 // Init the environment each platform wants
444 }
445
446
447 bool OnInit() override
448 {
449#ifdef NDEBUG
450 // These checks generate extra assert noise
451 wxSizerFlags::DisableConsistencyChecks();
452 wxDISABLE_DEBUG_SUPPORT();
453 wxSetAssertHandler( CustomAssertHandler );
454#endif
455
456 // Perform platform-specific init tasks
457 if( !KIPLATFORM::APP::Init() )
458 return false;
459
460#ifndef DEBUG
461 // Enable logging traces to the console in release build.
462 // This is usually disabled, but it can be useful for users to run to help
463 // debug issues and other problems.
464 if( wxGetEnv( wxS( "KICAD_ENABLE_WXTRACE" ), nullptr ) )
465 {
466 wxLog::EnableLogging( true );
467 wxLog::SetLogLevel( wxLOG_Trace );
468 }
469#endif
470
471 if( !program.OnPgmInit() )
472 {
474 return false;
475 }
476
477 return true;
478 }
479
480 int OnExit() override
481 {
483
484 // Avoid wxLog crashing when used in destructors.
485 wxLog::EnableLogging( false );
486
487 return wxApp::OnExit();
488 }
489
490 int OnRun() override
491 {
492 try
493 {
494 return wxApp::OnRun();
495 }
496 catch(...)
497 {
498 Pgm().HandleException( std::current_exception() );
499 }
500
501 return -1;
502 }
503
504 int FilterEvent( wxEvent& aEvent ) override
505 {
506 if( aEvent.GetEventType() == wxEVT_SHOW )
507 {
508 wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
509 wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
510
511 std::vector<void*>& dlgs = Pgm().m_ModalDialogs;
512
513 if( dialog )
514 {
515 if( event.IsShown() && dialog->IsModal() )
516 {
517 dlgs.push_back( dialog );
518 }
519 // Under GTK, sometimes the modal flag is cleared before hiding
520 else if( !event.IsShown() && !dlgs.empty() )
521 {
522 // If we close the expected dialog, remove it from our stack
523 if( dlgs.back() == dialog )
524 dlgs.pop_back();
525 // If an out-of-order, remove all dialogs added after the closed one
526 else if( auto it = std::find( dlgs.begin(), dlgs.end(), dialog ) ; it != dlgs.end() )
527 dlgs.erase( it, dlgs.end() );
528 }
529 }
530 }
531
532 return Event_Skip;
533 }
534
535#if defined( DEBUG )
539 bool ProcessEvent( wxEvent& aEvent ) override
540 {
541 if( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
542 {
543 wxKeyEvent* keyEvent = static_cast<wxKeyEvent*>( &aEvent );
544
545 if( keyEvent )
546 {
547 wxLogTrace( kicadTraceKeyEvent, "APP_KICAD::ProcessEvent %s", dump( *keyEvent ) );
548 }
549 }
550
551 aEvent.Skip();
552 return false;
553 }
554
562 bool OnExceptionInMainLoop() override
563 {
564 try
565 {
566 throw;
567 }
568 catch(...)
569 {
570 Pgm().HandleException( std::current_exception() );
571 }
572
573 return false; // continue on. Return false to abort program
574 }
575#endif
576
582#if defined( __WXMAC__ )
583 void MacOpenFile( const wxString& aFileName ) override
584 {
585 Pgm().MacOpenFile( aFileName );
586 }
587#endif
588};
589
590IMPLEMENT_APP( APP_KICAD )
591
592
593// The C++ project manager supports one open PROJECT, so Prj() calls within
594// this link image need this function.
596{
597 return Kiway.Prj();
598}
599
const char * name
Definition: DXF_plotter.cpp:57
const wxString & GetAboutTitle() const
The main KiCad project manager frame.
void LoadProject(const wxFileName &aProjectFileName)
std::vector< wxString > m_OpenProjects
A KIFACE implementation.
Definition: kiface_base.h:39
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:65
virtual bool OpenProjectFiles(const std::vector< wxString > &aFileList, int aCtl=0)
Open a project or set of files given by aFileList.
Definition: kiway_player.h:113
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:284
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:406
void SetCtlBits(int aCtlBits)
Overwrites previously set ctl bits, only for use in kicad.cpp to flip between standalone and manager ...
Definition: kiway.h:417
void OnKiwayEnd()
Definition: kiway.cpp:740
void SetTop(wxFrame *aTop)
Tell this KIWAY about the top most frame in the program and optionally allows it to play the role of ...
Definition: kiway.cpp:88
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition: kiway.cpp:196
static wxString GetUserTemplatesPath()
Gets the user path for custom templates.
Definition: paths.cpp:76
virtual wxApp & App()
Returns a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition: pgm_base.cpp:182
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:924
virtual void MacOpenFile(const wxString &aFileName)=0
Specific to MacOSX (not used under Linux or Windows).
std::unique_ptr< SETTINGS_MANAGER > m_settings_manager
Definition: pgm_base.h:408
void Destroy()
Definition: pgm_base.cpp:170
bool InitPgm(bool aHeadless=false, bool aSkipPyInit=false, bool aIsUnitTest=false)
Initialize this program.
Definition: pgm_base.cpp:457
std::vector< void * > m_ModalDialogs
Definition: pgm_base.h:388
void HandleException(std::exception_ptr aPtr)
A exception handler to be used at the top level if exceptions bubble up that for.
Definition: pgm_base.cpp:939
void HandleAssert(const wxString &aFile, int aLine, const wxString &aFunc, const wxString &aCond, const wxString &aMsg)
A common assert handler to be used between single_top and kicad.
Definition: pgm_base.cpp:996
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:142
void HideSplash()
Definition: pgm_base.cpp:446
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition: pgm_base.cpp:670
PGM_KICAD extends PGM_BASE to bring in FileHistory() and PdfBrowser() which were moved from EDA_APP i...
Definition: pgm_kicad.h:38
bool OnPgmInit()
Definition: kicad.cpp:86
void Destroy()
Definition: kicad.cpp:408
void MacOpenFile(const wxString &aFileName) override
Specific to MacOSX (not used under Linux or Windows).
Definition: kicad.cpp:395
void OnPgmExit()
Definition: kicad.cpp:373
APP_SETTINGS_BASE * PgmSettings()
Definition: pgm_kicad.h:55
int OnPgmRun()
Definition: kicad.cpp:367
BIN_MOD m_bm
Definition: pgm_kicad.h:68
Container for project specific data.
Definition: project.h:64
Look for files in a number of paths.
Definition: search_stack.h:43
void AddPaths(const wxString &aPaths, int aIndex=-1)
Insert or append path(s).
T * RegisterSettings(T *aSettings, bool aLoadNow=true)
Takes ownership of the pointer passed in.
void SetKiway(KIWAY *aKiway)
Associate this setting manager with the given Kiway.
This file is part of the common library.
#define _(s)
Functions related to environment variables, including help functions.
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition: frame_type.h:33
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ FRAME_CALC
Definition: frame_type.h:63
@ FRAME_BM2CMP
Definition: frame_type.h:61
@ FRAME_SCH
Definition: frame_type.h:34
@ FRAME_T_COUNT
Definition: frame_type.h:70
@ FRAME_PL_EDITOR
Definition: frame_type.h:59
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
@ FRAME_GERBER
Definition: frame_type.h:57
@ KICAD_MAIN_FRAME_T
Definition: frame_type.h:68
static const std::string ProjectFileExtension
static const std::string LegacyProjectFileExtension
const wxChar *const kicadTraceKeyEvent
Flag to enable wxKeyEvent debug tracing.
std::map< wxString, ENV_VAR_ITEM >::const_iterator ENV_VAR_MAP_CITER
PGM_KICAD & PgmTop()
Definition: kicad.cpp:80
PROJECT & Prj()
Definition: kicad.cpp:595
static PGM_KICAD program
Definition: kicad.cpp:78
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition: kicad.cpp:67
KIWAY Kiway(KFCTL_CPP_PROJECT_SUITE)
#define KFCTL_CPP_PROJECT_SUITE
Running under C++ project mgr, possibly with others.
Definition: kiway.h:159
#define KFCTL_STANDALONE
Running as a standalone Top.
Definition: kiway.h:158
This file contains miscellaneous commonly used macros and functions.
KICOMMON_API std::optional< wxString > GetVersionedEnvVarValue(const std::map< wxString, ENV_VAR_ITEM > &aMap, const wxString &aBaseName)
Attempts to retrieve the value of a versioned environment variable, such as KICAD8_TEMPLATE_DIR.
Definition: env_vars.cpp:83
bool Init()
Perform application-specific initialization tasks.
Definition: unix/app.cpp:40
void Init()
Perform environment initialization tasks.
void SetPgm(PGM_BASE *pgm)
Definition: pgm_base.cpp:1072
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1060
KIWAY Kiway(KFCTL_STANDALONE)
Not publicly visible because most of the action is in PGM_KICAD these days.
Definition: kicad.cpp:437
int OnRun() override
Definition: kicad.cpp:490
APP_KICAD()
Definition: kicad.cpp:438
bool OnInit() override
Definition: kicad.cpp:447
int OnExit() override
Definition: kicad.cpp:480
int FilterEvent(wxEvent &aEvent) override
Definition: kicad.cpp:504
void End()
Definition: bin_mod.cpp:50
void Init()
Definition: bin_mod.cpp:38
SEARCH_STACK m_search
Definition: bin_mod.h:60
void InitSettings(APP_SETTINGS_BASE *aPtr)
Takes ownership of a new application settings object.
Definition: bin_mod.h:53
void SystemDirsAppend(SEARCH_STACK *aSearchStack)
Append system places to aSearchStack in a platform specific way and pertinent to KiCad programs.
System directories search utilities.
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.