ADDED: Change watcher for libraries
When editing or viewing library symbols, the files are watched for underlying changes. If any occur, the user is either prompted to reload (if reloading would overwrite their current edits) or the file is silently updated to the current version on disk. This also sets a custom assertion handler to avoid unneeded crashes when recieving invalid SAMBA packets and turns off assertions entirely when running in release (non-debug) mode
This commit is contained in:
parent
d94fd5f979
commit
14f6e32c74
|
@ -222,10 +222,9 @@ bool EDA_DRAW_FRAME::LockFile( const wxString& aFileName )
|
|||
|
||||
m_file_checker = std::make_unique<LOCKFILE>( aFileName );
|
||||
|
||||
// If the file is not valid, or is successfully locked, return true
|
||||
// Invalid lockfiles are the result of bad permissions, so this is
|
||||
// likely not a file that we can override regardless
|
||||
return !m_file_checker->Valid() || m_file_checker->Locked();
|
||||
// If the file is valid, return true. This could mean that the file is
|
||||
// locked or it could mean that the file is read-only
|
||||
return m_file_checker->Valid();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -133,6 +133,26 @@ private:
|
|||
};
|
||||
wxIMPLEMENT_DYNAMIC_CLASS(HtmlModule, wxModule);
|
||||
|
||||
// Define a custom assertion handler
|
||||
void CustomAssertHandler(const wxString& file,
|
||||
int line,
|
||||
const wxString& func,
|
||||
const wxString& cond,
|
||||
const wxString& msg)
|
||||
{
|
||||
// Log the assertion details to standard log
|
||||
if (!msg.empty())
|
||||
{
|
||||
wxLogError( "Assertion failed at %s:%d in %s: %s - %s",
|
||||
file, line, func, cond, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError( "Assertion failed at %s:%d in %s: %s",
|
||||
file, line, func, cond);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Struct APP_SINGLE_TOP
|
||||
* implements a bare naked wxApp (so that we don't become dependent on
|
||||
|
@ -149,6 +169,9 @@ struct APP_SINGLE_TOP : public wxApp
|
|||
|
||||
bool OnInit() override
|
||||
{
|
||||
wxDISABLE_DEBUG_SUPPORT();
|
||||
wxSetAssertHandler( CustomAssertHandler );
|
||||
|
||||
// Perform platform-specific init tasks
|
||||
if( !KIPLATFORM::APP::Init() )
|
||||
return false;
|
||||
|
|
|
@ -188,6 +188,7 @@ DISPLAY_FOOTPRINTS_FRAME::~DISPLAY_FOOTPRINTS_FRAME()
|
|||
|
||||
delete GetScreen();
|
||||
SetScreen( nullptr ); // Be sure there is no double deletion
|
||||
setFPWatcher( nullptr );
|
||||
}
|
||||
|
||||
|
||||
|
@ -454,6 +455,7 @@ FOOTPRINT* DISPLAY_FOOTPRINTS_FRAME::GetFootprint( const wxString& aFootprintNam
|
|||
|
||||
if( footprint )
|
||||
{
|
||||
footprint->SetFPID( fpid );
|
||||
footprint->SetParent( (EDA_ITEM*) GetBoard() );
|
||||
footprint->SetPosition( VECTOR2I( 0, 0 ) );
|
||||
return footprint;
|
||||
|
@ -465,6 +467,35 @@ FOOTPRINT* DISPLAY_FOOTPRINTS_FRAME::GetFootprint( const wxString& aFootprintNam
|
|||
}
|
||||
|
||||
|
||||
void DISPLAY_FOOTPRINTS_FRAME::ReloadFootprint( FOOTPRINT* aFootprint )
|
||||
{
|
||||
if( !aFootprint || !m_currentComp )
|
||||
return;
|
||||
|
||||
GetBoard()->DeleteAllFootprints();
|
||||
GetBoard()->GetNetInfo().RemoveUnusedNets();
|
||||
GetCanvas()->GetView()->Clear();
|
||||
|
||||
|
||||
for( PAD* pad : aFootprint->Pads() )
|
||||
{
|
||||
const COMPONENT_NET& net = m_currentComp->GetNet( pad->GetNumber() );
|
||||
|
||||
if( !net.GetPinFunction().IsEmpty() )
|
||||
{
|
||||
NETINFO_ITEM* netinfo = new NETINFO_ITEM( GetBoard() );
|
||||
netinfo->SetNetname( net.GetPinFunction() );
|
||||
GetBoard()->Add( netinfo );
|
||||
pad->SetNet( netinfo );
|
||||
}
|
||||
}
|
||||
|
||||
GetBoard()->Add( aFootprint );
|
||||
updateView();
|
||||
GetCanvas()->Refresh();
|
||||
}
|
||||
|
||||
|
||||
void DISPLAY_FOOTPRINTS_FRAME::InitDisplay()
|
||||
{
|
||||
CVPCB_MAINFRAME* parentframe = (CVPCB_MAINFRAME *) GetParent();
|
||||
|
@ -517,6 +548,7 @@ void DISPLAY_FOOTPRINTS_FRAME::InitDisplay()
|
|||
GetBoard()->Add( footprint );
|
||||
m_currentFootprint = footprintName;
|
||||
m_currentComp = comp;
|
||||
setFPWatcher( footprint );
|
||||
}
|
||||
|
||||
if( fpInfo )
|
||||
|
|
|
@ -95,6 +95,7 @@ public:
|
|||
|
||||
SELECTION& GetCurrentSelection() override;
|
||||
|
||||
void ReloadFootprint( FOOTPRINT* aFootprint ) override;
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
||||
protected:
|
||||
|
|
|
@ -107,6 +107,8 @@ SCH_BASE_FRAME::SCH_BASE_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aWindo
|
|||
selTool->OnIdle( aEvent );
|
||||
}
|
||||
} );
|
||||
|
||||
m_watcherDebounceTimer.Bind( wxEVT_TIMER, &SCH_BASE_FRAME::OnSymChangeDebounceTimer, this );
|
||||
}
|
||||
|
||||
|
||||
|
@ -619,3 +621,108 @@ wxString SCH_BASE_FRAME::SelectLibraryFromList()
|
|||
|
||||
return libName;
|
||||
}
|
||||
|
||||
|
||||
void SCH_BASE_FRAME::setSymWatcher( const LIB_ID* aID )
|
||||
{
|
||||
Unbind( wxEVT_FSWATCHER, &SCH_BASE_FRAME::OnSymChange, this );
|
||||
|
||||
if( !aID )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "No symbol library specified, disabling watcher" );
|
||||
m_watcher.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
wxString libfullname;
|
||||
SYMBOL_LIB_TABLE* tbl = Prj().SchSymbolLibTable();
|
||||
|
||||
if( !tbl )
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
const SYMBOL_LIB_TABLE_ROW* row = tbl->FindRow( aID->GetLibNickname() );
|
||||
|
||||
if( !row )
|
||||
return;
|
||||
|
||||
libfullname = row->GetFullURI( true );
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
DisplayInfoMessage( this, e.what() );
|
||||
return;
|
||||
}
|
||||
catch( const IO_ERROR& error )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Error: %s", error.What() );
|
||||
return;
|
||||
}
|
||||
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Setting up watcher for %s", libfullname );
|
||||
m_watcherFileName.Assign( libfullname );
|
||||
|
||||
if( !m_watcherFileName.FileExists() )
|
||||
return;
|
||||
|
||||
wxLog::EnableLogging( false );
|
||||
m_watcherLastModified = m_watcherFileName.GetModificationTime();
|
||||
wxLog::EnableLogging( true );
|
||||
|
||||
Bind( wxEVT_FSWATCHER, &SCH_BASE_FRAME::OnSymChange, this );
|
||||
m_watcher = std::make_unique<wxFileSystemWatcher>();
|
||||
m_watcher->SetOwner( this );
|
||||
|
||||
wxFileName fn;
|
||||
fn.AssignDir( m_watcherFileName.GetPath() );
|
||||
fn.DontFollowLink();
|
||||
|
||||
m_watcher->AddTree( fn );
|
||||
}
|
||||
|
||||
|
||||
void SCH_BASE_FRAME::OnSymChange( wxFileSystemWatcherEvent& aEvent )
|
||||
{
|
||||
SYMBOL_LIBS* libs = Prj().SchLibs();
|
||||
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "OnSymChange: %s, watcher file: %s",
|
||||
aEvent.GetPath().GetFullPath(), m_watcherFileName.GetFullPath() );
|
||||
|
||||
if( !libs || !m_watcher || !m_watcher.get() || m_watcherFileName.GetPath().IsEmpty() )
|
||||
return;
|
||||
|
||||
if( aEvent.GetPath() != m_watcherFileName )
|
||||
return;
|
||||
|
||||
// Start the debounce timer (set to 1 second)
|
||||
if( !m_watcherDebounceTimer.StartOnce( 1000 ) )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Failed to start the debounce timer" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCH_BASE_FRAME::OnSymChangeDebounceTimer( wxTimerEvent& aEvent )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "OnSymChangeDebounceTimer" );
|
||||
// Disable logging to avoid spurious messages and check if the file has changed
|
||||
wxLog::EnableLogging( false );
|
||||
wxDateTime lastModified = m_watcherFileName.GetModificationTime();
|
||||
wxLog::EnableLogging( true );
|
||||
|
||||
if( lastModified == m_watcherLastModified || !lastModified.IsValid() )
|
||||
return;
|
||||
|
||||
m_watcherLastModified = lastModified;
|
||||
|
||||
if( !GetScreen()->IsContentModified() || IsOK( this, _( "The library containing the current symbol has changed.\n"
|
||||
"Do you want to reload the library?" ) ) )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Sending refresh symbol mail" );
|
||||
std::string libName = m_watcherFileName.GetFullPath().ToStdString();
|
||||
Kiway().ExpressMail( FRAME_SCH_VIEWER, MAIL_REFRESH_SYMBOL, libName );
|
||||
Kiway().ExpressMail( FRAME_SCH_SYMBOL_EDITOR, MAIL_REFRESH_SYMBOL, libName );
|
||||
}
|
||||
}
|
|
@ -35,8 +35,11 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
#include <wx/event.h>
|
||||
#include <wx/fswatcher.h>
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
#include <template_fieldnames.h>
|
||||
|
||||
|
@ -251,6 +254,16 @@ public:
|
|||
|
||||
void ActivateGalCanvas() override;
|
||||
|
||||
/**
|
||||
* Handler for Symbol change events. Responds to the filesystem watcher set in #setSymWatcher.
|
||||
*/
|
||||
void OnSymChange( wxFileSystemWatcherEvent& aEvent );
|
||||
|
||||
/**
|
||||
* Handler for the filesystem watcher debounce timer.
|
||||
*/
|
||||
void OnSymChangeDebounceTimer( wxTimerEvent& aEvent );
|
||||
|
||||
protected:
|
||||
void handleActivateEvent( wxActivateEvent& aEvent ) override;
|
||||
|
||||
|
@ -265,11 +278,24 @@ protected:
|
|||
*/
|
||||
bool saveSymbolLibTables( bool aGlobal, bool aProject );
|
||||
|
||||
/**
|
||||
* Creates (or removes) a watcher on the specified symbol library
|
||||
* @param aSymbol If nullptr, the watcher is removed. Otherwise, set a change watcher
|
||||
*/
|
||||
void setSymWatcher( const LIB_ID* aSymbol );
|
||||
|
||||
/// These are only used by symbol_editor. Eeschema should be using the one inside
|
||||
/// the SCHEMATIC.
|
||||
SCHEMATIC_SETTINGS m_base_frame_defaults;
|
||||
|
||||
private:
|
||||
|
||||
/// These are file watchers for the symbol library tables.
|
||||
std::unique_ptr<wxFileSystemWatcher> m_watcher;
|
||||
wxFileName m_watcherFileName;
|
||||
wxDateTime m_watcherLastModified;
|
||||
wxTimer m_watcherDebounceTimer;
|
||||
|
||||
NL_SCHEMATIC_PLUGIN* m_spaceMouse;
|
||||
};
|
||||
|
||||
|
|
|
@ -264,6 +264,8 @@ SYMBOL_EDIT_FRAME::~SYMBOL_EDIT_FRAME()
|
|||
if( m_toolManager )
|
||||
m_toolManager->ShutdownAllTools();
|
||||
|
||||
setSymWatcher( nullptr );
|
||||
|
||||
if( IsSymbolFromSchematic() )
|
||||
{
|
||||
delete m_symbol;
|
||||
|
@ -1371,6 +1373,52 @@ void SYMBOL_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
|||
break;
|
||||
}
|
||||
|
||||
case MAIL_REFRESH_SYMBOL:
|
||||
{
|
||||
SYMBOL_LIB_TABLE* tbl = Prj().SchSymbolLibTable();
|
||||
LIB_SYMBOL* symbol = GetCurSymbol();
|
||||
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Received refresh symbol request for %s",
|
||||
payload );
|
||||
|
||||
if( !tbl || !symbol )
|
||||
break;
|
||||
|
||||
wxString libName = symbol->GetLibId().GetLibNickname();
|
||||
const SYMBOL_LIB_TABLE_ROW* row = tbl->FindRow( libName );
|
||||
|
||||
if( !row )
|
||||
return;
|
||||
|
||||
wxFileName libfullname( row->GetFullURI( true ) );
|
||||
|
||||
wxFileName changedLib( mail.GetPayload() );
|
||||
wxLogTrace( "KICAD_LIB_WATCH",
|
||||
"Received refresh symbol request for %s, current symbols is %s",
|
||||
changedLib.GetFullPath(), libfullname.GetFullPath() );
|
||||
|
||||
if( changedLib == libfullname )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Refreshing symbol %s", symbol->GetName() );
|
||||
|
||||
m_libMgr->UpdateLibraryBuffer( libName );
|
||||
|
||||
LIB_SYMBOL* lib_symbol = m_libMgr->GetBufferedSymbol( symbol->GetName(), libName );
|
||||
wxCHECK2_MSG( lib_symbol, break, wxString::Format( "Symbol %s not found in library %s",
|
||||
symbol->GetName(), libName ) );
|
||||
|
||||
// The buffered screen for the symbol
|
||||
SCH_SCREEN* symbol_screen = m_libMgr->GetScreen( lib_symbol->GetName(), libName );
|
||||
|
||||
SetScreen( symbol_screen );
|
||||
SetCurSymbol( new LIB_SYMBOL( *lib_symbol ), false );
|
||||
RebuildSymbolUnitsList();
|
||||
SetShowDeMorgan( GetCurSymbol()->HasConversion() );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
@ -1518,6 +1566,7 @@ void SYMBOL_EDIT_FRAME::LoadSymbolFromSchematic( SCH_SYMBOL* aSymbol )
|
|||
|
||||
SetScreen( tmpScreen );
|
||||
SetCurSymbol( symbol.release(), true );
|
||||
setSymWatcher( nullptr );
|
||||
|
||||
ReCreateMenuBar();
|
||||
ReCreateHToolbar();
|
||||
|
|
|
@ -198,6 +198,7 @@ bool SYMBOL_EDIT_FRAME::LoadSymbol( const LIB_ID& aLibId, int aUnit, int aConver
|
|||
|
||||
m_centerItemOnIdle = libId;
|
||||
Bind( wxEVT_IDLE, &SYMBOL_EDIT_FRAME::centerItemIdleHandler, this );
|
||||
setSymWatcher( &libId );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -305,9 +306,11 @@ bool SYMBOL_EDIT_FRAME::LoadOneLibrarySymbolAux( LIB_SYMBOL* aEntry, const wxStr
|
|||
|
||||
ClearUndoRedoList();
|
||||
|
||||
// Let tools add things to the view if necessary
|
||||
if( m_toolManager )
|
||||
m_toolManager->ResetTools( TOOL_BASE::MODEL_RELOAD );
|
||||
if( !IsSymbolFromSchematic() )
|
||||
{
|
||||
LIB_ID libId = GetCurSymbol()->GetLibId();
|
||||
setSymWatcher( &libId );
|
||||
}
|
||||
|
||||
// Display the document information based on the entry selected just in
|
||||
// case the entry is an alias.
|
||||
|
|
|
@ -310,6 +310,49 @@ SYMBOL_LIB* SYMBOL_LIBS::AddLibrary( const wxString& aFileName, SYMBOL_LIBS::ite
|
|||
}
|
||||
|
||||
|
||||
bool SYMBOL_LIBS::ReloadLibrary( const wxString &aFileName )
|
||||
{
|
||||
SYMBOL_LIB *lib;
|
||||
wxFileName fn = aFileName;
|
||||
|
||||
// Check if the library already exists.
|
||||
if( !( lib = FindLibrary( fn.GetName() ) ) )
|
||||
return false;
|
||||
|
||||
// Create a clone of the library pointer in case we need to re-add it
|
||||
SYMBOL_LIB *cloneLib = lib;
|
||||
|
||||
// Try to find the iterator of the library
|
||||
for( auto it = begin(); it != end(); ++it )
|
||||
{
|
||||
if( it->GetName() == fn.GetName() )
|
||||
{
|
||||
// Remove the old library and keep the pointer
|
||||
lib = &*it;
|
||||
release( it );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to reload the library
|
||||
try
|
||||
{
|
||||
lib = SYMBOL_LIB::LoadSymbolLibrary( aFileName );
|
||||
|
||||
// If the library is successfully reloaded, add it back to the set.
|
||||
push_back( lib );
|
||||
return true;
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
// If an exception occurs, ensure that the SYMBOL_LIBS remains unchanged
|
||||
// by re-adding the old library back to the set.
|
||||
push_back( cloneLib );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SYMBOL_LIB* SYMBOL_LIBS::FindLibrary( const wxString& aName )
|
||||
{
|
||||
for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
|
||||
|
|
|
@ -84,6 +84,14 @@ public:
|
|||
*/
|
||||
SYMBOL_LIB* AddLibrary( const wxString& aFileName, SYMBOL_LIBS::iterator& aIterator );
|
||||
|
||||
/**
|
||||
* Refreshes the library from the (possibly updated) contents on disk
|
||||
*
|
||||
* @param aFileName is the file name of the symbol library
|
||||
* @return true if successfully updated
|
||||
*/
|
||||
bool ReloadLibrary( const wxString& aFileName );
|
||||
|
||||
/**
|
||||
* Load all of the project's libraries into this container, which should
|
||||
* be cleared before calling it.
|
||||
|
|
|
@ -831,6 +831,35 @@ SYMBOL_LIBRARY_MANAGER::LIB_BUFFER& SYMBOL_LIBRARY_MANAGER::getLibraryBuffer(
|
|||
}
|
||||
|
||||
|
||||
bool SYMBOL_LIBRARY_MANAGER::UpdateLibraryBuffer( const wxString& aLibrary )
|
||||
{
|
||||
try
|
||||
{
|
||||
m_libs.erase( aLibrary );
|
||||
getLibraryBuffer( aLibrary );
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
wxLogError( _( "Error updating library buffer: %s" ), e.what() );
|
||||
return false;
|
||||
}
|
||||
catch( const IO_ERROR& e )
|
||||
{
|
||||
wxLogError( _( "Error updating library buffer: %s" ), e.What() );
|
||||
return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
wxLogError( _( "Error updating library buffer." ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
getLibraryBuffer( aLibrary );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SYMBOL_LIBRARY_MANAGER::SYMBOL_BUFFER::SYMBOL_BUFFER( LIB_SYMBOL* aSymbol,
|
||||
std::unique_ptr<SCH_SCREEN> aScreen ) :
|
||||
m_screen( std::move( aScreen ) ),
|
||||
|
|
|
@ -117,6 +117,11 @@ public:
|
|||
bool UpdateSymbolAfterRename( LIB_SYMBOL* aSymbol, const wxString& oldAlias,
|
||||
const wxString& aLibrary );
|
||||
|
||||
/**
|
||||
* Update the library buffer with a new version of the library.
|
||||
*/
|
||||
bool UpdateLibraryBuffer( const wxString& aLibrary );
|
||||
|
||||
/**
|
||||
* Remove the symbol from the symbol buffer.
|
||||
* It is required to save the library to have the symbol removed in the schematic editor.
|
||||
|
|
|
@ -1312,6 +1312,7 @@ SELECTION& SYMBOL_VIEWER_FRAME::GetCurrentSelection()
|
|||
|
||||
void SYMBOL_VIEWER_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
||||
{
|
||||
|
||||
switch( mail.Command() )
|
||||
{
|
||||
case MAIL_RELOAD_LIB:
|
||||
|
@ -1319,6 +1320,32 @@ void SYMBOL_VIEWER_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
|||
ReCreateLibList();
|
||||
break;
|
||||
}
|
||||
case MAIL_REFRESH_SYMBOL:
|
||||
{
|
||||
SYMBOL_LIB_TABLE* tbl = Prj().SchSymbolLibTable();
|
||||
LIB_SYMBOL* symbol = GetSelectedSymbol();
|
||||
const SYMBOL_LIB_TABLE_ROW* row = tbl->FindRow( symbol->GetLibId().GetLibNickname() );
|
||||
|
||||
if( !row )
|
||||
return;
|
||||
|
||||
wxString libfullname = row->GetFullURI( true );
|
||||
|
||||
if( symbol )
|
||||
{
|
||||
wxString lib( mail.GetPayload() );
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Received refresh symbol request for %s, current symbols is %s", lib, libfullname );
|
||||
|
||||
if( lib == libfullname )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Refreshing symbol %s", symbol->GetName() );
|
||||
updatePreviewSymbol();
|
||||
GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public:
|
|||
* Use #ReleaseFile() to undo this.
|
||||
*
|
||||
* @param aFileName full path to the file.
|
||||
* @return false if the file was already locked, true otherwise.
|
||||
* @return true if the file is locked or read-only, false otherwise.
|
||||
*/
|
||||
bool LockFile( const wxString& aFileName );
|
||||
|
||||
|
|
|
@ -53,7 +53,8 @@ enum MAIL_T
|
|||
MAIL_LIB_EDIT,
|
||||
MAIL_FP_EDIT,
|
||||
MAIL_RELOAD_LIB, // Reload Library List if one was added
|
||||
MAIL_RELOAD_PLUGINS // Reload python plugins
|
||||
MAIL_RELOAD_PLUGINS, // Reload python plugins
|
||||
MAIL_REFRESH_SYMBOL // Refresh symbol in symbol viewer
|
||||
};
|
||||
|
||||
#endif // MAIL_TYPE_H_
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#include <richio.h>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/fswatcher.h>
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
/* Forward declarations of classes. */
|
||||
class APP_SETTINGS_BASE;
|
||||
|
@ -183,6 +186,15 @@ public:
|
|||
virtual const PCB_PLOT_PARAMS& GetPlotSettings() const;
|
||||
virtual void SetPlotSettings( const PCB_PLOT_PARAMS& aSettings );
|
||||
|
||||
/**
|
||||
* Reload the footprint from the library.
|
||||
* @param aFootprint is the footprint to reload.
|
||||
*/
|
||||
virtual void ReloadFootprint( FOOTPRINT* aFootprint )
|
||||
{
|
||||
wxFAIL_MSG( wxT( "Attempted to reload a footprint for PCB_BASE_FRAME that does not override!" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the #m_Pcb member in such as way as to ensure deleting any previous #BOARD.
|
||||
*/
|
||||
|
@ -392,6 +404,16 @@ public:
|
|||
*/
|
||||
void RemoveBoardChangeListener( wxEvtHandler* aListener );
|
||||
|
||||
/**
|
||||
* Handler for FP change events. Responds to the filesystem watcher set in #setFPWatcher.
|
||||
*/
|
||||
void OnFPChange( wxFileSystemWatcherEvent& aEvent );
|
||||
|
||||
/**
|
||||
* Handler for the filesystem watcher debounce timer.
|
||||
*/
|
||||
void OnFpChangeDebounceTimer( wxTimerEvent& aEvent );
|
||||
|
||||
protected:
|
||||
bool canCloseWindow( wxCloseEvent& aCloseEvent ) override;
|
||||
|
||||
|
@ -416,6 +438,12 @@ protected:
|
|||
|
||||
void rebuildConnectivity();
|
||||
|
||||
/**
|
||||
* Creates (or removes) a watcher on the specified footprint
|
||||
* @param aFootprint If nullptr, the watcher is removed. Otherwise, set a change watcher
|
||||
*/
|
||||
void setFPWatcher( FOOTPRINT* aFootprint );
|
||||
|
||||
protected:
|
||||
BOARD* m_pcb;
|
||||
PCB_DISPLAY_OPTIONS m_displayOptions;
|
||||
|
@ -424,6 +452,11 @@ protected:
|
|||
private:
|
||||
NL_PCBNEW_PLUGIN* m_spaceMouse;
|
||||
|
||||
std::unique_ptr<wxFileSystemWatcher> m_watcher;
|
||||
wxFileName m_watcherFileName;
|
||||
wxDateTime m_watcherLastModified;
|
||||
wxTimer m_watcherDebounceTimer;
|
||||
|
||||
std::vector<wxEvtHandler*> m_boardChangeListeners;
|
||||
};
|
||||
|
||||
|
|
|
@ -407,6 +407,26 @@ void PGM_KICAD::Destroy()
|
|||
KIWAY Kiway( &Pgm(), KFCTL_CPP_PROJECT_SUITE );
|
||||
|
||||
|
||||
// Define a custom assertion handler
|
||||
void CustomAssertHandler(const wxString& file,
|
||||
int line,
|
||||
const wxString& func,
|
||||
const wxString& cond,
|
||||
const wxString& msg)
|
||||
{
|
||||
// Log the assertion details to standard log
|
||||
if (!msg.empty())
|
||||
{
|
||||
wxLogError( "Assertion failed at %s:%d in %s: %s - %s",
|
||||
file, line, func, cond, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError( "Assertion failed at %s:%d in %s: %s",
|
||||
file, line, func, cond);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not publicly visible because most of the action is in #PGM_KICAD these days.
|
||||
*/
|
||||
|
@ -421,6 +441,9 @@ struct APP_KICAD : public wxApp
|
|||
|
||||
bool OnInit() override
|
||||
{
|
||||
wxDISABLE_DEBUG_SUPPORT();
|
||||
wxSetAssertHandler( CustomAssertHandler );
|
||||
|
||||
// Perform platform-specific init tasks
|
||||
if( !KIPLATFORM::APP::Init() )
|
||||
return false;
|
||||
|
@ -438,10 +461,8 @@ struct APP_KICAD : public wxApp
|
|||
{
|
||||
program.OnPgmExit();
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
// Avoid wxLog crashing when used in destructors.
|
||||
wxLog::EnableLogging( false );
|
||||
#endif
|
||||
|
||||
return wxApp::OnExit();
|
||||
}
|
||||
|
|
|
@ -338,6 +338,9 @@ FOOTPRINT_EDIT_FRAME::~FOOTPRINT_EDIT_FRAME()
|
|||
// save the footprint in the PROJECT
|
||||
retainLastFootprint();
|
||||
|
||||
// Clear the watched file
|
||||
setFPWatcher( nullptr );
|
||||
|
||||
delete m_selectionFilterPanel;
|
||||
delete m_appearancePanel;
|
||||
delete m_treePane;
|
||||
|
@ -535,7 +538,7 @@ void FOOTPRINT_EDIT_FRAME::restoreLastFootprint()
|
|||
}
|
||||
|
||||
|
||||
void FOOTPRINT_EDIT_FRAME::AddFootprintToBoard( FOOTPRINT* aFootprint )
|
||||
void FOOTPRINT_EDIT_FRAME::ReloadFootprint( FOOTPRINT* aFootprint )
|
||||
{
|
||||
m_originalFootprintCopy.reset( static_cast<FOOTPRINT*>( aFootprint->Clone() ) );
|
||||
m_originalFootprintCopy->SetParent( nullptr );
|
||||
|
@ -566,6 +569,17 @@ void FOOTPRINT_EDIT_FRAME::AddFootprintToBoard( FOOTPRINT* aFootprint )
|
|||
}
|
||||
|
||||
|
||||
void FOOTPRINT_EDIT_FRAME::AddFootprintToBoard( FOOTPRINT* aFootprint )
|
||||
{
|
||||
ReloadFootprint( aFootprint );
|
||||
|
||||
if( IsCurrentFPFromBoard() )
|
||||
setFPWatcher( nullptr );
|
||||
else
|
||||
setFPWatcher( aFootprint );
|
||||
}
|
||||
|
||||
|
||||
const wxChar* FOOTPRINT_EDIT_FRAME::GetFootprintEditorFrameName()
|
||||
{
|
||||
return FOOTPRINT_EDIT_FRAME_NAME;
|
||||
|
|
|
@ -278,6 +278,12 @@ public:
|
|||
*/
|
||||
void AddFootprintToBoard( FOOTPRINT* aFootprint ) override;
|
||||
|
||||
/**
|
||||
* Override from PCB_BASE_FRAME which reloads the footprint from the library without
|
||||
* setting the footprint watcher
|
||||
*/
|
||||
void ReloadFootprint( FOOTPRINT* aFootprint ) override;
|
||||
|
||||
/**
|
||||
* Update visible items after a language change.
|
||||
*/
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "footprint_viewer_frame.h"
|
||||
#include <wx/choicdlg.h>
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/fswatcher.h>
|
||||
|
||||
|
||||
// unique, "file local" translations:
|
||||
|
|
|
@ -68,9 +68,10 @@
|
|||
using namespace std::placeholders;
|
||||
|
||||
|
||||
#define NEXT_PART 1
|
||||
#define NEW_PART 0
|
||||
#define PREVIOUS_PART -1
|
||||
#define NEXT_PART 1
|
||||
#define PREVIOUS_PART 2
|
||||
#define RELOAD_PART 3
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE( FOOTPRINT_VIEWER_FRAME, PCB_BASE_FRAME )
|
||||
|
@ -260,7 +261,10 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
|
|||
FOOTPRINT* footprint = loadFootprint( id );
|
||||
|
||||
if( footprint )
|
||||
{
|
||||
GetBoard()->Add( footprint );
|
||||
setFPWatcher( footprint );
|
||||
}
|
||||
}
|
||||
|
||||
drawPanel->DisplayBoard( m_pcb );
|
||||
|
@ -361,6 +365,7 @@ FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
|
|||
// Be sure any event cannot be fired after frame deletion:
|
||||
GetCanvas()->SetEvtHandlerEnabled( false );
|
||||
m_fpList->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( FOOTPRINT_VIEWER_FRAME::DClickOnFootprintList ), nullptr, this );
|
||||
setFPWatcher( nullptr );
|
||||
}
|
||||
|
||||
|
||||
|
@ -753,42 +758,7 @@ void FOOTPRINT_VIEWER_FRAME::ClickOnFootprintList( wxCommandEvent& aEvent )
|
|||
if( getCurFootprintName().CmpNoCase( name ) != 0 )
|
||||
{
|
||||
setCurFootprintName( name );
|
||||
|
||||
// Delete the current footprint (MUST reset tools first)
|
||||
GetToolManager()->ResetTools( TOOL_BASE::MODEL_RELOAD );
|
||||
|
||||
GetBoard()->DeleteAllFootprints();
|
||||
GetBoard()->GetNetInfo().RemoveUnusedNets();
|
||||
|
||||
LIB_ID id;
|
||||
id.SetLibNickname( getCurNickname() );
|
||||
id.SetLibItemName( getCurFootprintName() );
|
||||
|
||||
FOOTPRINT* footprint = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
footprint = loadFootprint( id );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
wxString msg = wxString::Format( _( "Could not load footprint '%s' from library '%s'."
|
||||
"\n\n%s" ),
|
||||
getCurFootprintName(),
|
||||
getCurNickname(),
|
||||
ioe.Problem() );
|
||||
DisplayError( this, msg );
|
||||
}
|
||||
|
||||
if( footprint )
|
||||
displayFootprint( footprint );
|
||||
|
||||
UpdateTitle();
|
||||
|
||||
updateView();
|
||||
|
||||
GetCanvas()->Refresh();
|
||||
Update3DView( true, true );
|
||||
SelectAndViewFootprint( NEW_PART );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1049,6 +1019,14 @@ void FOOTPRINT_VIEWER_FRAME::OnUpdateFootprintButton( wxUpdateUIEvent& aEvent )
|
|||
}
|
||||
|
||||
|
||||
void FOOTPRINT_VIEWER_FRAME::ReloadFootprint( FOOTPRINT* aFootprint )
|
||||
{
|
||||
setCurNickname( aFootprint->GetFPID().GetLibNickname() );
|
||||
setCurFootprintName( aFootprint->GetFPID().GetLibItemName() );
|
||||
SelectAndViewFootprint( RELOAD_PART );
|
||||
}
|
||||
|
||||
|
||||
void FOOTPRINT_VIEWER_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
||||
{
|
||||
const std::string& payload = mail.GetPayload();
|
||||
|
@ -1247,6 +1225,9 @@ void FOOTPRINT_VIEWER_FRAME::SelectAndViewFootprint( int aMode )
|
|||
if( footprint )
|
||||
displayFootprint( footprint );
|
||||
|
||||
if( aMode != RELOAD_PART )
|
||||
setFPWatcher( footprint );
|
||||
|
||||
Update3DView( true, true );
|
||||
updateView();
|
||||
}
|
||||
|
|
|
@ -72,6 +72,12 @@ public:
|
|||
*/
|
||||
void OnUpdateFootprintButton( wxUpdateUIEvent& aEvent );
|
||||
|
||||
/**
|
||||
* Override from PCB_BASE_FRAME which reloads the footprint from the library without
|
||||
* setting the footprint watcher
|
||||
*/
|
||||
void ReloadFootprint( FOOTPRINT* aFootprint ) override;
|
||||
|
||||
///< @copydoc EDADRAW_FRAME::UpdateMsgPanel
|
||||
void UpdateMsgPanel() override;
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include <pcb_base_frame.h>
|
||||
#include <pcb_draw_panel_gal.h>
|
||||
#include <pgm_base.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
#include <zoom_defines.h>
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
@ -81,6 +82,7 @@ PCB_BASE_FRAME::PCB_BASE_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrame
|
|||
m_originTransforms( *this ),
|
||||
m_spaceMouse( nullptr )
|
||||
{
|
||||
m_watcherDebounceTimer.Bind( wxEVT_TIMER, &PCB_BASE_FRAME::OnFpChangeDebounceTimer, this );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1138,3 +1140,115 @@ void PCB_BASE_FRAME::SetDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions, boo
|
|||
if( aRefresh )
|
||||
canvas->Refresh();
|
||||
}
|
||||
|
||||
|
||||
void PCB_BASE_FRAME::setFPWatcher( FOOTPRINT* aFootprint )
|
||||
{
|
||||
Unbind( wxEVT_FSWATCHER, &PCB_BASE_FRAME::OnFPChange, this );
|
||||
|
||||
if( !aFootprint )
|
||||
{
|
||||
m_watcher.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
wxString libfullname;
|
||||
FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs();
|
||||
|
||||
if( !aFootprint || !tbl )
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
const FP_LIB_TABLE_ROW* row = tbl->FindRow( aFootprint->GetFPID().GetLibNickname() );
|
||||
|
||||
if( !row )
|
||||
return;
|
||||
|
||||
libfullname = row->GetFullURI( true );
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
DisplayInfoMessage( this, e.what() );
|
||||
return;
|
||||
}
|
||||
catch( const IO_ERROR& error )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Error: %s", error.What() );
|
||||
return;
|
||||
}
|
||||
|
||||
m_watcherFileName.Assign( libfullname, aFootprint->GetFPID().GetLibItemName(),
|
||||
KiCadFootprintFileExtension );
|
||||
|
||||
if( !m_watcherFileName.FileExists() )
|
||||
return;
|
||||
|
||||
m_watcherLastModified = m_watcherFileName.GetModificationTime();
|
||||
|
||||
Bind( wxEVT_FSWATCHER, &PCB_BASE_FRAME::OnFPChange, this );
|
||||
m_watcher = std::make_unique<wxFileSystemWatcher>();
|
||||
m_watcher->SetOwner( this );
|
||||
|
||||
wxFileName fn;
|
||||
fn.AssignDir( m_watcherFileName.GetPath() );
|
||||
fn.DontFollowLink();
|
||||
|
||||
m_watcher->AddTree( fn );
|
||||
}
|
||||
|
||||
|
||||
void PCB_BASE_FRAME::OnFPChange( wxFileSystemWatcherEvent& aEvent )
|
||||
{
|
||||
if( aEvent.GetPath() != m_watcherFileName.GetFullPath() )
|
||||
return;
|
||||
|
||||
// Start the debounce timer (set to 1 second)
|
||||
if( !m_watcherDebounceTimer.StartOnce( 1000 ) )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "Failed to start the debounce timer" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCB_BASE_FRAME::OnFpChangeDebounceTimer( wxTimerEvent& aEvent )
|
||||
{
|
||||
wxLogTrace( "KICAD_LIB_WATCH", "OnFpChangeDebounceTimer" );
|
||||
|
||||
// Disable logging to avoid spurious messages and check if the file has changed
|
||||
wxLog::EnableLogging( false );
|
||||
wxDateTime lastModified = m_watcherFileName.GetModificationTime();
|
||||
wxLog::EnableLogging( true );
|
||||
|
||||
if( lastModified == m_watcherLastModified || !lastModified.IsValid() )
|
||||
return;
|
||||
|
||||
m_watcherLastModified = lastModified;
|
||||
|
||||
FOOTPRINT* fp = GetBoard()->GetFirstFootprint();
|
||||
FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs();
|
||||
|
||||
if( !fp || !tbl )
|
||||
return;
|
||||
|
||||
if( !GetScreen()->IsContentModified()
|
||||
|| IsOK( this, _( "The library containing the current footprint has changed.\n"
|
||||
"Do you want to reload the footprint?" ) ) )
|
||||
{
|
||||
wxString fpname = fp->GetFPID().GetLibItemName();
|
||||
wxString nickname = fp->GetFPID().GetLibNickname();
|
||||
|
||||
try
|
||||
{
|
||||
FOOTPRINT* newfp = tbl->FootprintLoad( nickname, fpname );
|
||||
|
||||
if( newfp )
|
||||
ReloadFootprint( newfp );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
DisplayError( this, ioe.What() );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue