Stop showing popup for library load errors

Errors are stored to a vector for display after clicking the warning
icon in the toolbar.
This commit is contained in:
Seth Hillbrand
2025-12-31 13:14:11 -08:00
parent c2461ddaa5
commit 9b9899979b
32 changed files with 508 additions and 61 deletions
+16
View File
@@ -138,6 +138,22 @@ void FOOTPRINT_LIST::DisplayErrors( wxTopLevelWindow* aWindow )
}
wxString FOOTPRINT_LIST::GetErrorMessages()
{
wxString messages;
while( std::unique_ptr<IO_ERROR> error = PopError() )
{
if( !messages.IsEmpty() )
messages += wxS( "\n" );
messages += error->Problem();
}
return messages;
}
static FOOTPRINT_LIST* get_instance_from_id( KIWAY& aKiway, int aId )
{
void* ptr = nullptr;
+20
View File
@@ -1040,6 +1040,26 @@ std::vector<std::pair<wxString, LIB_STATUS>> LIBRARY_MANAGER_ADAPTER::GetLibrary
}
wxString LIBRARY_MANAGER_ADAPTER::GetLibraryLoadErrors() const
{
wxString errors;
for( const auto& [nickname, status] : GetLibraryStatuses() )
{
if( status.load_status == LOAD_STATUS::LOAD_ERROR && status.error )
{
if( !errors.IsEmpty() )
errors += wxS( "\n" );
errors += wxString::Format( _( "Library '%s': %s" ),
nickname, status.error->message );
}
}
return errors;
}
void LIBRARY_MANAGER_ADAPTER::ReloadLibraryEntry( const wxString& aNickname,
LIBRARY_TABLE_SCOPE aScope )
{
+74
View File
@@ -69,6 +69,7 @@
#include <thread_pool.h>
#include <trace_helpers.h>
#include <widgets/kistatusbar.h>
#include <widgets/wx_splash.h>
#ifdef KICAD_IPC_API
@@ -947,6 +948,79 @@ void PGM_BASE::PreloadDesignBlockLibraries( KIWAY* aKiway )
}
void PGM_BASE::RegisterLibraryLoadStatusBar( KISTATUSBAR* aStatusBar )
{
std::lock_guard<std::mutex> lock( m_libraryLoadStatusBarsMutex );
wxLogTrace( traceLibraries, "RegisterLibraryLoadStatusBar: statusBar=%p", aStatusBar );
if( std::find( m_libraryLoadStatusBars.begin(), m_libraryLoadStatusBars.end(), aStatusBar )
== m_libraryLoadStatusBars.end() )
{
m_libraryLoadStatusBars.push_back( aStatusBar );
wxLogTrace( traceLibraries, " -> registered, total count=%zu",
m_libraryLoadStatusBars.size() );
}
else
{
wxLogTrace( traceLibraries, " -> already registered" );
}
}
void PGM_BASE::UnregisterLibraryLoadStatusBar( KISTATUSBAR* aStatusBar )
{
std::lock_guard<std::mutex> lock( m_libraryLoadStatusBarsMutex );
wxLogTrace( traceLibraries, "UnregisterLibraryLoadStatusBar: statusBar=%p", aStatusBar );
m_libraryLoadStatusBars.erase(
std::remove( m_libraryLoadStatusBars.begin(), m_libraryLoadStatusBars.end(),
aStatusBar ),
m_libraryLoadStatusBars.end() );
wxLogTrace( traceLibraries, " -> remaining count=%zu", m_libraryLoadStatusBars.size() );
}
void PGM_BASE::AddLibraryLoadMessages( const std::vector<LOAD_MESSAGE>& aMessages )
{
wxLogTrace( traceLibraries, "AddLibraryLoadMessages: message_count=%zu", aMessages.size() );
if( aMessages.empty() )
return;
std::lock_guard<std::mutex> lock( m_libraryLoadStatusBarsMutex );
wxLogTrace( traceLibraries, " -> registered status bars=%zu",
m_libraryLoadStatusBars.size() );
for( KISTATUSBAR* statusBar : m_libraryLoadStatusBars )
{
if( statusBar )
{
wxLogTrace( traceLibraries, " -> forwarding to statusBar=%p", statusBar );
statusBar->AddLoadWarningMessages( aMessages );
}
}
}
void PGM_BASE::ClearLibraryLoadMessages()
{
std::lock_guard<std::mutex> lock( m_libraryLoadStatusBarsMutex );
wxLogTrace( traceLibraries, "ClearLibraryLoadMessages: status bars=%zu",
m_libraryLoadStatusBars.size() );
for( KISTATUSBAR* statusBar : m_libraryLoadStatusBars )
{
if( statusBar )
statusBar->ClearLoadWarningMessages();
}
}
static PGM_BASE* process;
+31
View File
@@ -33,6 +33,7 @@
#include <fmt/core.h>
#include <macros.h>
#include <string_utils.h>
#include <widgets/kistatusbar.h>
#include <wx_filename.h>
#include <fmt/chrono.h>
#include <wx/log.h>
@@ -1781,3 +1782,33 @@ int SortVariantNames( const wxString& aLhs, const wxString& aRhs )
return StrNumCmp( aLhs, aRhs );
}
std::vector<LOAD_MESSAGE> ExtractLibraryLoadErrors( const wxString& aErrorString, int aSeverity )
{
std::vector<LOAD_MESSAGE> messages;
if( aErrorString.IsEmpty() )
return messages;
// Errors are separated by newlines. We want to keep:
// - Lines starting with "Library '" (library-level errors)
// - Lines containing "Expecting" (file error location)
// And strip:
// - Lines starting with "from " (internal code location info)
wxStringTokenizer tokenizer( aErrorString, wxS( "\n" ), wxTOKEN_STRTOK );
while( tokenizer.HasMoreTokens() )
{
wxString line = tokenizer.GetNextToken();
// Skip internal code location lines (e.g., "from pcb_io_kicad_sexpr_parser.cpp : ...")
if( line.StartsWith( wxS( "from " ) ) )
continue;
if( line.StartsWith( wxS( "Library '" ) ) || line.Contains( wxS( "Expecting" ) ) )
messages.push_back( { line, static_cast<SEVERITY>( aSeverity ) } );
}
return messages;
}
+95 -23
View File
@@ -39,6 +39,7 @@
#include <bitmaps.h>
#include <reporter.h>
#include <dialog_HTML_reporter_base.h>
#include <trace_helpers.h>
#include <wx/dcclient.h>
@@ -316,42 +317,106 @@ void KISTATUSBAR::SetNotificationCount( int aCount )
void KISTATUSBAR::SetLoadWarningMessages( const wxString& aMessages )
{
m_loadWarningMessages.clear();
wxStringTokenizer tokenizer( aMessages, wxS( "\n" ), wxTOKEN_STRTOK );
while( tokenizer.HasMoreTokens() )
{
LOAD_MESSAGE msg;
msg.message = tokenizer.GetNextToken();
msg.severity = RPT_SEVERITY_WARNING; // Default to warning for font substitutions
m_loadWarningMessages.push_back( msg );
}
std::lock_guard<std::mutex> lock( m_loadWarningMutex );
m_loadWarningMessages.clear();
if( m_warningButton )
{
m_warningButton->Show( !m_loadWarningMessages.empty() );
wxStringTokenizer tokenizer( aMessages, wxS( "\n" ), wxTOKEN_STRTOK );
if( !m_loadWarningMessages.empty() )
while( tokenizer.HasMoreTokens() )
{
m_warningButton->SetToolTip(
wxString::Format( _( "View %zu load message(s)" ),
m_loadWarningMessages.size() ) );
LOAD_MESSAGE msg;
msg.message = tokenizer.GetNextToken();
msg.severity = RPT_SEVERITY_WARNING; // Default to warning for font substitutions
m_loadWarningMessages.push_back( msg );
}
Layout();
Refresh();
}
updateWarningUI();
}
void KISTATUSBAR::AddLoadWarningMessages( const std::vector<LOAD_MESSAGE>& aMessages )
{
wxLogTrace( traceLibraries, "KISTATUSBAR::AddLoadWarningMessages: this=%p, count=%zu",
this, aMessages.size() );
if( aMessages.empty() )
return;
{
std::lock_guard<std::mutex> lock( m_loadWarningMutex );
m_loadWarningMessages.insert( m_loadWarningMessages.end(), aMessages.begin(), aMessages.end() );
wxLogTrace( traceLibraries, " -> total messages now=%zu", m_loadWarningMessages.size() );
}
// Update UI on main thread
wxLogTrace( traceLibraries, " -> calling CallAfter for updateWarningUI" );
CallAfter( [this]() { updateWarningUI(); } );
}
size_t KISTATUSBAR::GetLoadWarningCount() const
{
std::lock_guard<std::mutex> lock( m_loadWarningMutex );
return m_loadWarningMessages.size();
}
void KISTATUSBAR::updateWarningUI()
{
wxLogTrace( traceLibraries, "KISTATUSBAR::updateWarningUI: this=%p, m_warningButton=%p",
this, m_warningButton );
if( !m_warningButton )
{
wxLogTrace( traceLibraries, " -> no warning button, returning early" );
return;
}
size_t messageCount;
{
std::lock_guard<std::mutex> lock( m_loadWarningMutex );
messageCount = m_loadWarningMessages.size();
}
wxLogTrace( traceLibraries, " -> message count=%zu, showing button=%s",
messageCount, messageCount > 0 ? "true" : "false" );
m_warningButton->Show( messageCount > 0 );
if( messageCount > 0 )
{
m_warningButton->SetToolTip( wxString::Format( _( "View %zu load message(s)" ), messageCount ) );
// Show count badge on the warning button
m_warningButton->SetShowBadge( true );
wxString badgeText = messageCount > 99
? wxString( "99+" )
: wxString::Format( wxS( "%zu" ), messageCount );
m_warningButton->SetBadgeText( badgeText );
wxLogTrace( traceLibraries, " -> badge set to '%s'", badgeText );
}
Layout();
Refresh();
wxLogTrace( traceLibraries, " -> Layout and Refresh complete" );
}
void KISTATUSBAR::ClearLoadWarningMessages()
{
m_loadWarningMessages.clear();
{
std::lock_guard<std::mutex> lock( m_loadWarningMutex );
m_loadWarningMessages.clear();
}
if( m_warningButton )
{
m_warningButton->Hide();
m_warningButton->SetShowBadge( false );
m_warningButton->SetBadgeText( wxEmptyString );
Layout();
Refresh();
}
@@ -360,12 +425,19 @@ void KISTATUSBAR::ClearLoadWarningMessages()
void KISTATUSBAR::onLoadWarningsIconClick( wxCommandEvent& aEvent )
{
if( m_loadWarningMessages.empty() )
// Copy messages under lock to avoid holding lock during modal dialog
std::vector<LOAD_MESSAGE> messages;
{
std::lock_guard<std::mutex> lock( m_loadWarningMutex );
messages = m_loadWarningMessages;
}
if( messages.empty() )
return;
DIALOG_HTML_REPORTER dlg( GetParent(), wxID_ANY, _( "Load Messages" ) );
for( const LOAD_MESSAGE& msg : m_loadWarningMessages )
for( const LOAD_MESSAGE& msg : messages )
dlg.m_Reporter->Report( msg.message, msg.severity );
dlg.m_Reporter->Flush();
+5 -1
View File
@@ -43,6 +43,7 @@
#include <tool/editor_conditions.h>
#include <tool/tool_dispatcher.h>
#include <tool/tool_manager.h>
#include <widgets/kistatusbar.h>
#include <widgets/wx_progress_reporters.h>
#include <cvpcb_association.h>
@@ -908,7 +909,10 @@ bool CVPCB_MAINFRAME::LoadFootprintFiles()
m_FootprintsList->ReadFootprintFiles( adapter, nullptr, &progressReporter );
if( m_FootprintsList->GetErrorCount() )
m_FootprintsList->DisplayErrors( this );
{
if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
statusBar->SetLoadWarningMessages( m_FootprintsList->GetErrorMessages() );
}
return true;
}
+2 -1
View File
@@ -308,7 +308,8 @@ FOOTPRINT* DISPLAY_FOOTPRINTS_FRAME::GetFootprint( const wxString& aFootprintNam
}
catch( const IO_ERROR& ioe )
{
DisplayErrorMessage( this, _( "Error loading footprint" ), ioe.What() );
aReporter.Report( wxString::Format( _( "Error loading footprint: %s" ), ioe.What() ),
RPT_SEVERITY_ERROR );
return nullptr;
}
+14
View File
@@ -45,6 +45,7 @@
#include <pgm_base.h>
#include <libraries/symbol_library_adapter.h>
#include <widgets/sch_design_block_pane.h>
#include <widgets/kistatusbar.h>
#include <wx/log.h>
#include <trace_helpers.h>
@@ -1089,12 +1090,25 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
break;
case MAIL_RELOAD_LIB:
{
if( m_designBlocksPane && m_designBlocksPane->IsShown() )
{
m_designBlocksPane->RefreshLibs();
SyncView();
}
// Show any symbol library load errors in the status bar
if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
{
SYMBOL_LIBRARY_ADAPTER* adapter = PROJECT_SCH::SymbolLibAdapter( &Prj() );
wxString errors = adapter->GetLibraryLoadErrors();
if( !errors.IsEmpty() )
statusBar->SetLoadWarningMessages( errors );
}
break;
}
default:;
+25
View File
@@ -50,11 +50,14 @@
#include <symbol_editor_settings.h>
#include <sexpr/sexpr.h>
#include <sexpr/sexpr_parser.h>
#include <string_utils.h>
#include <trace_helpers.h>
#include <thread_pool.h>
#include <kiface_ids.h>
#include <widgets/kistatusbar.h>
#include <netlist_exporters/netlist_exporter_kicad.h>
#include <wx/ffile.h>
#include <wx/tokenzr.h>
#include <wildcards_and_files_ext.h>
#include <schematic.h>
@@ -482,6 +485,8 @@ void IFACE::PreloadLibraries( KIWAY* aKiway )
if( m_libraryPreloadInProgress.load() )
return;
Pgm().ClearLibraryLoadMessages();
m_libraryPreloadBackgroundJob =
Pgm().GetBackgroundJobMonitor().Create( _( "Loading Symbol Libraries" ) );
@@ -530,6 +535,26 @@ void IFACE::PreloadLibraries( KIWAY* aKiway )
adapter->BlockUntilLoaded();
// Collect library load errors for async reporting
wxString errors = adapter->GetLibraryLoadErrors();
wxLogTrace( traceLibraries, "eeschema PreloadLibraries: errors.IsEmpty()=%d, length=%zu",
errors.IsEmpty(), errors.length() );
std::vector<LOAD_MESSAGE> messages =
ExtractLibraryLoadErrors( errors, RPT_SEVERITY_ERROR );
if( !messages.empty() )
{
wxLogTrace( traceLibraries, " -> collected %zu messages, calling AddLibraryLoadMessages",
messages.size() );
Pgm().AddLibraryLoadMessages( messages );
}
else
{
wxLogTrace( traceLibraries, " -> no errors from symbol libraries" );
}
Pgm().GetBackgroundJobMonitor().Remove( m_libraryPreloadBackgroundJob );
m_libraryPreloadBackgroundJob.reset();
m_libraryPreloadInProgress.store( false );
+1 -1
View File
@@ -641,7 +641,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
// Update all symbol library links for all sheets.
schematic.UpdateSymbolLinks();
schematic.UpdateSymbolLinks( &loadReporter );
m_infoBar->RemoveAllButtons();
m_infoBar->AddCloseButton();
+1 -1
View File
@@ -537,7 +537,7 @@ SCH_SHEET* SCH_IO_ALTIUM::LoadSchematicFile( const wxString& aFileName, SCHEMATI
m_errorMessages.clear();
SCH_SCREENS allSheets( m_rootSheet );
allSheets.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
allSheets.UpdateSymbolLinks( &LOAD_INFO_REPORTER::GetInstance() ); // Update all symbol library links for all sheets.
allSheets.ClearEditFlags();
// Set up the default netclass wire & bus width based on imported wires & buses.
+1 -1
View File
@@ -476,7 +476,7 @@ SCH_SHEET* SCH_IO_EAGLE::LoadSchematicFile( const wxString& aFileName, SCHEMATIC
m_pi->SaveLibrary( getLibFileName().GetFullPath() );
SCH_SCREENS allSheets( m_rootSheet );
allSheets.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
allSheets.UpdateSymbolLinks( &LOAD_INFO_REPORTER::GetInstance() ); // Update all symbol library links for all sheets.
return m_rootSheet;
}
@@ -593,7 +593,7 @@ LIB_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseLibSymbol( LIB_SYMBOL_MAP& aSymbolLi
}
catch( const IO_ERROR& e )
{
wxLogError( e.What() );
m_parseWarnings.push_back( e.What() );
}
SyncLineReaderWith( embeddedFilesParser );
@@ -3012,7 +3012,7 @@ void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopya
}
catch( const PARSE_ERROR& e )
{
wxLogError( e.What() );
m_parseWarnings.push_back( e.What() );
}
SyncLineReaderWith( embeddedFilesParser );
@@ -113,6 +113,12 @@ public:
int GetParsedRequiredVersion() const { return m_requiredVersion; }
/**
* Return any non-fatal parse warnings that occurred during parsing.
* These are errors that were handled gracefully but should be reported to the user.
*/
const std::vector<wxString>& GetParseWarnings() const { return m_parseWarnings; }
private:
// Group membership info refers to other Uuids in the file.
// We don't want to rely on group declarations being last in the file, so
@@ -276,6 +282,8 @@ private:
int m_maxError;
std::vector<GROUP_INFO> m_groupInfos;
std::vector<wxString> m_parseWarnings; ///< Non-fatal warnings collected during parsing
};
#endif // SCH_IO_KICAD_SEXPR_PARSER_H_
+11
View File
@@ -226,6 +226,11 @@ public:
return error;
}
void PushError( std::unique_ptr<IO_ERROR> aError )
{
m_errors.move_push( std::move( aError ) );
}
/**
* Read all the footprints provided by the combination of aTable and aNickname.
*
@@ -243,6 +248,12 @@ public:
void DisplayErrors( wxTopLevelWindow* aCaller = nullptr );
/**
* Returns all accumulated errors as a newline-separated string for display in the
* status bar. This consumes the errors (pops them from the queue).
*/
wxString GetErrorMessages();
FOOTPRINT_LIBRARY_ADAPTER* GetAdapter() const { return m_adapter; }
/**
+3
View File
@@ -148,6 +148,9 @@ public:
/// Returns a list of all library nicknames and their status (even if they failed to load)
std::vector<std::pair<wxString, LIB_STATUS>> GetLibraryStatuses() const;
/// Returns all library load errors as newline-separated strings for display
wxString GetLibraryLoadErrors() const;
void ReloadLibraryEntry( const wxString& aNickname,
LIBRARY_TABLE_SCOPE aScope = LIBRARY_TABLE_SCOPE::BOTH );
+28
View File
@@ -37,6 +37,7 @@
#include <exception>
#include <map>
#include <future>
#include <mutex>
#include <vector>
#include <memory>
#include <search_stack.h>
@@ -49,6 +50,8 @@ class wxWindow;
class wxSplashScreen;
class wxSingleInstanceChecker;
class KISTATUSBAR;
struct LOAD_MESSAGE;
struct BACKGROUND_JOB;
class BACKGROUND_JOBS_MONITOR;
class NOTIFICATIONS_MANAGER;
@@ -360,6 +363,28 @@ public:
*/
void PreloadDesignBlockLibraries( KIWAY* aKiway );
/**
* Register a status bar to receive library load warning messages.
* Multiple status bars can be registered (one per open frame).
*/
void RegisterLibraryLoadStatusBar( KISTATUSBAR* aStatusBar );
/**
* Unregister a status bar from receiving library load warning messages.
*/
void UnregisterLibraryLoadStatusBar( KISTATUSBAR* aStatusBar );
/**
* Add library load messages to all registered status bars.
* Thread-safe - can be called from background threads.
*/
void AddLibraryLoadMessages( const std::vector<LOAD_MESSAGE>& aMessages );
/**
* Clear library load messages from all registered status bars.
*/
void ClearLibraryLoadMessages();
/**
* wxWidgets on MSW tends to crash if you spool up more than one print job at a time.
*/
@@ -432,6 +457,9 @@ protected:
std::future<void> m_libraryPreloadReturn;
std::atomic_bool m_libraryPreloadInProgress;
std::atomic_bool m_libraryPreloadAbort;
std::vector<KISTATUSBAR*> m_libraryLoadStatusBars;
mutable std::mutex m_libraryLoadStatusBarsMutex;
};
+13
View File
@@ -511,4 +511,17 @@ KICOMMON_API wxString GetDefaultVariantName();
KICOMMON_API int SortVariantNames( const wxString& aLhs, const wxString& aRhs );
struct LOAD_MESSAGE;
/**
* Parse library load error messages, extracting user-facing information while
* stripping internal code locations.
*
* @param aErrorString is the raw error string from GetLibraryLoadErrors()
* @param aSeverity is the severity to assign to all extracted messages
* @return vector of LOAD_MESSAGE with cleaned error text
*/
KICOMMON_API std::vector<LOAD_MESSAGE> ExtractLibraryLoadErrors( const wxString& aErrorString,
int aSeverity );
#endif // STRING_UTILS_H
+14
View File
@@ -27,6 +27,7 @@
#include <kicommon.h>
#include <optional>
#include <mutex>
#include <vector>
#include <widgets/report_severity.h>
@@ -116,11 +117,23 @@ public:
void SetLoadWarningMessages( const wxString& aMessages );
void ClearLoadWarningMessages();
/**
* Add warning/error messages thread-safely.
* Can be called from any thread. UI update is deferred to main thread.
*/
void AddLoadWarningMessages( const std::vector<LOAD_MESSAGE>& aMessages );
/**
* Get current message count (thread-safe).
*/
size_t GetLoadWarningCount() const;
private:
void onSize( wxSizeEvent& aEvent );
void onBackgroundProgressClick( wxMouseEvent& aEvent );
void onNotificationsIconClick( wxCommandEvent& aEvent );
void onLoadWarningsIconClick( wxCommandEvent& aEvent );
void updateWarningUI(); ///< Update warning button visibility and badge (main thread only)
enum class FIELD
{
@@ -139,6 +152,7 @@ private:
wxStaticText* m_backgroundTxt;
BITMAP_BUTTON* m_notificationsButton;
BITMAP_BUTTON* m_warningButton;
mutable std::mutex m_loadWarningMutex; ///< Protects m_loadWarningMessages
std::vector<LOAD_MESSAGE> m_loadWarningMessages;
int m_normalFieldsCount;
STYLE_FLAGS m_styleFlags;
+6 -1
View File
@@ -172,6 +172,7 @@ KICAD_MANAGER_FRAME::KICAD_MANAGER_FRAME( wxWindow* parent, const wxString& titl
CreateStatusBar( 2 );
Pgm().GetBackgroundJobMonitor().RegisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
Pgm().GetNotificationsManager().RegisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
Pgm().RegisterLibraryLoadStatusBar( (KISTATUSBAR*) GetStatusBar() );
GetStatusBar()->SetFont( KIUI::GetStatusFont( this ) );
// Give an icon
@@ -324,6 +325,7 @@ KICAD_MANAGER_FRAME::~KICAD_MANAGER_FRAME()
Pgm().GetBackgroundJobMonitor().UnregisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
Pgm().GetNotificationsManager().UnregisterStatusBar( (KISTATUSBAR*) GetStatusBar() );
Pgm().UnregisterLibraryLoadStatusBar( (KISTATUSBAR*) GetStatusBar() );
// Shutdown all running tools
if( m_toolManager )
@@ -383,7 +385,10 @@ void KICAD_MANAGER_FRAME::onNotebookPageCloseRequest( wxAuiNotebookEvent& evt )
wxStatusBar* KICAD_MANAGER_FRAME::OnCreateStatusBar( int number, long style, wxWindowID id,
const wxString& name )
{
return new KISTATUSBAR( number, this, id );
return new KISTATUSBAR( number, this, id,
static_cast<KISTATUSBAR::STYLE_FLAGS>(
KISTATUSBAR::NOTIFICATION_ICON | KISTATUSBAR::CANCEL_BUTTON
| KISTATUSBAR::WARNING_ICON ) );
}
+16
View File
@@ -57,6 +57,9 @@
#include <trace_helpers.h>
#include <netlist_reader/netlist_reader.h>
#include <widgets/pcb_design_block_pane.h>
#include <widgets/kistatusbar.h>
#include <project_pcb.h>
#include <footprint_library_adapter.h>
#include <wx/log.h>
/* Execute a remote command sent via a socket on port KICAD_PCB_PORT_SERVICE_NUMBER
@@ -726,8 +729,21 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
break;
case MAIL_RELOAD_LIB:
{
m_designBlocksPane->RefreshLibs();
// Show any footprint library load errors in the status bar
if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
{
FOOTPRINT_LIBRARY_ADAPTER* adapter = PROJECT_PCB::FootprintLibAdapter( &Prj() );
wxString errors = adapter->GetLibraryLoadErrors();
if( !errors.IsEmpty() )
statusBar->SetLoadWarningMessages( errors );
}
break;
}
// many many others.
default:
+3 -9
View File
@@ -31,7 +31,6 @@
#include <kidialog.h>
#include <core/arraydim.h>
#include <thread_pool.h>
#include <dialog_HTML_reporter_base.h>
#include <gestfich.h>
#include <local_history.h>
#include <pcb_edit_frame.h>
@@ -623,7 +622,6 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
std::placeholders::_1 ) );
}
DIALOG_HTML_REPORTER errorReporter( this );
bool failedLoad = false;
try
@@ -660,8 +658,10 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// measure the time to load a BOARD.
int64_t startTime = GetRunningMicroSecs();
#endif
// Use loadReporter for import issues - they will be shown in the status bar
// warning icon instead of a modal dialog
if( config()->m_System.show_import_issues )
pi->SetReporter( errorReporter.m_Reporter );
pi->SetReporter( &loadReporter );
else
pi->SetReporter( &NULL_REPORTER::GetInstance() );
@@ -718,12 +718,6 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// compiled.
Raise();
if( errorReporter.m_Reporter->HasMessage() )
{
errorReporter.m_Reporter->Flush(); // Build HTML messages
errorReporter.ShowModal();
}
// Skip (possibly expensive) connectivity build here; we build it below after load
SetBoard( loadedBoard, false, &progressReporter );
+5 -1
View File
@@ -70,6 +70,7 @@
#include <tools/pcb_group_tool.h>
#include <tools/position_relative_tool.h>
#include <widgets/appearance_controls.h>
#include <widgets/kistatusbar.h>
#include <widgets/lib_tree.h>
#include <widgets/panel_selection_filter.h>
#include <widgets/pcb_properties_panel.h>
@@ -1151,7 +1152,10 @@ void FOOTPRINT_EDIT_FRAME::initLibraryTree()
progressReporter.Show( false );
if( GFootprintList.GetErrorCount() )
GFootprintList.DisplayErrors( this );
{
if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
statusBar->SetLoadWarningMessages( GFootprintList.GetErrorMessages() );
}
m_adapter = FP_TREE_SYNCHRONIZING_ADAPTER::Create( this, footprints );
auto adapter = static_cast<FP_TREE_SYNCHRONIZING_ADAPTER*>( m_adapter.get() );
+22 -10
View File
@@ -45,20 +45,32 @@ void FOOTPRINT_INFO_IMPL::load()
FOOTPRINT_LIBRARY_ADAPTER* adapter = m_owner->GetAdapter();
wxCHECK( adapter, /* void */ );
std::unique_ptr<FOOTPRINT> footprint( adapter->LoadFootprint( m_nickname, m_fpname, false ) );
if( footprint == nullptr ) // Should happen only with malformed/broken libraries
try
{
std::unique_ptr<FOOTPRINT> footprint( adapter->LoadFootprint( m_nickname, m_fpname, false ) );
if( footprint == nullptr ) // Should happen only with malformed/broken libraries
{
m_pad_count = 0;
m_unique_pad_count = 0;
}
else
{
m_pad_count = footprint->GetPadCount( DO_NOT_INCLUDE_NPTH );
m_unique_pad_count = footprint->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
m_keywords = footprint->GetKeywords();
m_doc = footprint->GetLibDescription();
}
}
catch( const IO_ERROR& ioe )
{
// Store error in the owner's error list for later display
if( m_owner )
m_owner->PushError( std::make_unique<IO_ERROR>( ioe ) );
m_pad_count = 0;
m_unique_pad_count = 0;
}
else
{
m_pad_count = footprint->GetPadCount( DO_NOT_INCLUDE_NPTH );
m_unique_pad_count = footprint->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
m_keywords = footprint->GetKeywords();
m_doc = footprint->GetLibDescription();
}
m_loaded = true;
}
+13 -5
View File
@@ -364,12 +364,20 @@ FOOTPRINT* FOOTPRINT_LIBRARY_ADAPTER::LoadFootprint( const wxString& aNickname,
{
if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
{
if( FOOTPRINT* footprint = pcbplugin( *lib )->FootprintLoad( getUri( ( *lib )->row ), aName ) )
try
{
LIB_ID id = footprint->GetFPID();
id.SetLibNickname( ( *lib )->row->Nickname() );
footprint->SetFPID( id );
return footprint;
if( FOOTPRINT* footprint = pcbplugin( *lib )->FootprintLoad( getUri( ( *lib )->row ), aName ) )
{
LIB_ID id = footprint->GetFPID();
id.SetLibNickname( ( *lib )->row->Nickname() );
footprint->SetFPID( id );
return footprint;
}
}
catch( const IO_ERROR& ioe )
{
wxLogTrace( traceLibraries, "LoadFootprint: error loading %s:%s: %s",
aNickname, aName, ioe.What() );
}
}
else
+3 -1
View File
@@ -36,6 +36,7 @@
#include <kiway.h>
#include <kiway_express.h>
#include <netlist_reader/pcb_netlist.h>
#include <widgets/kistatusbar.h>
#include <widgets/msgpanel.h>
#include <widgets/wx_listbox.h>
#include <widgets/wx_aui_utils.h>
@@ -492,7 +493,8 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
if( fp_info_list->GetErrorCount() )
{
fp_info_list->DisplayErrors( this );
if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
statusBar->SetLoadWarningMessages( fp_info_list->GetErrorMessages() );
// For footprint libraries that support one footprint per file, there may have been
// valid footprints read so show the footprints that loaded properly.
+3 -1
View File
@@ -23,6 +23,7 @@
#include <string_utils.h>
#include <footprint.h>
#include <footprint_library_adapter.h>
#include <trace_helpers.h>
#include <wx/log.h>
@@ -126,7 +127,8 @@ public:
}
catch( const IO_ERROR& ioe )
{
wxLogError( _( "Error loading footprint %s from library '%s'." ) + wxS( "\n%s" ),
// Log to trace instead of error to avoid popup dialogs
wxLogTrace( traceLibraries, "Error loading footprint %s from library '%s': %s",
m_lib_id.GetLibItemName().wx_str(),
m_lib_id.GetLibNickname().wx_str(),
ioe.What() );
@@ -190,6 +190,17 @@ void FP_CACHE::Load()
footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
m_footprints.insert( fpName, new FP_CACHE_ENTRY( footprint, fn ) );
// Collect any non-fatal parse warnings
for( const wxString& warning : parser.GetParseWarnings() )
{
if( !cacheError.IsEmpty() )
cacheError += wxT( "\n\n" );
cacheError += wxString::Format( _( "Warning in file '%s'" ) + '\n',
fn.GetFullPath() );
cacheError += warning;
}
}
catch( const IO_ERROR& ioe )
{
@@ -73,6 +73,7 @@
#include <progress_reporter.h>
#include <board_stackup_manager/stackup_predefined_prms.h>
#include <pgm_base.h>
#include <trace_helpers.h>
// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
// base64 code. Needed for PCB_REFERENCE_IMAGE
@@ -1173,7 +1174,7 @@ BOARD* PCB_IO_KICAD_SEXPR_PARSER::parseBOARD_unchecked()
}
catch( const PARSE_ERROR& e )
{
wxLogError( e.What() );
m_parseWarnings.push_back( e.What() );
}
SyncLineReaderWith( embeddedFilesParser );
@@ -5306,7 +5307,7 @@ FOOTPRINT* PCB_IO_KICAD_SEXPR_PARSER::parseFOOTPRINT_unchecked( wxArrayString* a
}
catch( const PARSE_ERROR& e )
{
wxLogError( e.What() );
m_parseWarnings.push_back( e.What() );
}
SyncLineReaderWith( embeddedFilesParser );
@@ -136,6 +136,12 @@ public:
*/
bool IsValidBoardHeader();
/**
* Return any non-fatal parse warnings that occurred during parsing.
* These are errors that were handled gracefully but should be reported to the user.
*/
const std::vector<wxString>& GetParseWarnings() const { return m_parseWarnings; }
private:
// Group membership info refers to other Uuids in the file.
@@ -449,6 +455,8 @@ private:
std::vector<GENERATOR_INFO> m_generatorInfos;
std::function<bool( wxString aTitle, int aIcon, wxString aMsg, wxString aAction )> m_queryUserCallback;
std::vector<wxString> m_parseWarnings; ///< Non-fatal warnings collected during parsing
};
+45
View File
@@ -62,7 +62,12 @@
#include <panel_3D_raytracing_options.h>
#include <project_pcb.h>
#include <python_scripting.h>
#include <string_utils.h>
#include <thread_pool.h>
#include <trace_helpers.h>
#include <widgets/kistatusbar.h>
#include <wx/tokenzr.h>
#include "invoke_pcb_dialog.h"
#include <wildcards_and_files_ext.h>
@@ -611,6 +616,8 @@ void IFACE::PreloadLibraries( KIWAY* aKiway )
if( m_libraryPreloadInProgress.load() )
return;
Pgm().ClearLibraryLoadMessages();
m_libraryPreloadBackgroundJob =
Pgm().GetBackgroundJobMonitor().Create( _( "Loading Footprint Libraries" ) );
@@ -659,9 +666,47 @@ void IFACE::PreloadLibraries( KIWAY* aKiway )
adapter->BlockUntilLoaded();
// Collect and report library load errors from adapter
wxString errors = adapter->GetLibraryLoadErrors();
wxLogTrace( traceLibraries, "pcbnew PreloadLibraries: adapter errors.IsEmpty()=%d, length=%zu",
errors.IsEmpty(), errors.length() );
if( !errors.IsEmpty() )
{
std::vector<LOAD_MESSAGE> messages = ExtractLibraryLoadErrors( errors, RPT_SEVERITY_ERROR );
wxLogTrace( traceLibraries, " -> adapter: collected %zu messages", messages.size() );
if( !messages.empty() )
Pgm().AddLibraryLoadMessages( messages );
}
else
{
wxLogTrace( traceLibraries, " -> no errors from footprint adapter" );
}
// TODO: Remove once fp-info-cache isn't a thing
GFootprintList.ReadFootprintFiles( adapter, nullptr, reporter.get() );
// Also collect errors from GFootprintList
wxLogTrace( traceLibraries, " -> GFootprintList.GetErrorCount()=%u", GFootprintList.GetErrorCount() );
if( GFootprintList.GetErrorCount() > 0 )
{
std::vector<LOAD_MESSAGE> messages =
ExtractLibraryLoadErrors( GFootprintList.GetErrorMessages(), RPT_SEVERITY_ERROR );
wxLogTrace( traceLibraries, " -> GFootprintList: collected %zu messages", messages.size() );
if( !messages.empty() )
Pgm().AddLibraryLoadMessages( messages );
}
else
{
wxLogTrace( traceLibraries, " -> no errors from GFootprintList" );
}
Pgm().GetBackgroundJobMonitor().Remove( m_libraryPreloadBackgroundJob );
m_libraryPreloadBackgroundJob.reset();
m_libraryPreloadInProgress.store( false );
+6 -1
View File
@@ -45,6 +45,7 @@
#include <kiface_base.h>
#include <tool/actions.h>
#include <tool/tool_manager.h>
#include <widgets/kistatusbar.h>
// When a new footprint is selected, a custom event is sent, for instance to update
// 3D viewer. So define a FP_SELECTION_EVENT event
@@ -79,7 +80,11 @@ PANEL_FOOTPRINT_CHOOSER::PANEL_FOOTPRINT_CHOOSER( PCB_BASE_FRAME* aFrame, wxTopL
delete progressReporter;
if( GFootprintList.GetErrorCount() )
GFootprintList.DisplayErrors( aParent );
{
// Show errors in status bar instead of popup dialog
if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( aFrame->GetStatusBar() ) )
statusBar->SetLoadWarningMessages( GFootprintList.GetErrorMessages() );
}
m_adapter = FP_TREE_MODEL_ADAPTER::Create( aFrame, footprints );
FP_TREE_MODEL_ADAPTER* adapter = static_cast<FP_TREE_MODEL_ADAPTER*>( m_adapter.get() );