Begin moving project file load to new system

This commit is contained in:
Jon Evans 2020-05-25 16:41:24 -04:00
parent a5a19076fd
commit a4fadfcdf2
16 changed files with 217 additions and 78 deletions

View File

@ -87,6 +87,8 @@ EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType,
m_mruPath = wxStandardPaths::Get().GetDocumentsDir();
m_FrameSize = wxSize( s_minsize_x, s_minsize_y );
m_settingsManager = &Pgm().GetSettingsManager();
// Set a reasonable minimal size for the frame
SetSizeHints( s_minsize_x, s_minsize_y, -1, -1, -1, -1 );

View File

@ -36,9 +36,11 @@
#include <kiway.h>
#include <kiface_ids.h>
#include <trace_helpers.h>
#include <settings/project_file.h>
PROJECT::PROJECT()
PROJECT::PROJECT() :
m_projectFile( nullptr )
{
memset( m_elems, 0, sizeof(m_elems) );
}
@ -73,7 +75,7 @@ bool PROJECT::TextVarResolver( wxString* aToken ) const
}
void PROJECT::SetProjectFullName( const wxString& aFullPathAndName )
void PROJECT::setProjectFullName( const wxString& aFullPathAndName )
{
// Compare paths, rather than inodes, to be less surprising to the user.
// Create a temporary wxFileName to normalize the path
@ -92,7 +94,7 @@ void PROJECT::SetProjectFullName( const wxString& aFullPathAndName )
wxASSERT( m_project_name.IsAbsolute() );
wxASSERT( m_project_name.GetExt() == LegacyProjectFileExtension );
wxASSERT( m_project_name.GetExt() == ProjectFileExtension );
// until multiple projects are in play, set an environment variable for the
// the project pointer.
@ -333,6 +335,14 @@ wxConfigBase* PROJECT::configCreate( const SEARCH_STACK& aSList,
wxConfigBase* cfg = 0;
wxString cur_pro_fn = !aProjectFileName ? GetProjectFullName() : aProjectFileName;
wxFileName filename( cur_pro_fn );
if( filename.GetExt() == ProjectFileExtension )
{
filename.SetExt( LegacyProjectFileExtension );
cur_pro_fn = filename.GetFullPath();
}
// If we do not have a project name or specified name, choose an empty file to store the
// temporary configuration data in.
if( cur_pro_fn.IsEmpty() )

View File

@ -45,6 +45,7 @@ JSON_SETTINGS::JSON_SETTINGS( const std::string& aFilename, SETTINGS_LOC aLocati
m_createIfMissing( aCreateIfMissing ),
m_createIfDefault( aCreateIfDefault ),
m_writeFile( aWriteFile ),
m_deleteLegacyAfterMigration( true ),
m_schemaVersion( aSchemaVersion ),
m_manager( nullptr )
{
@ -121,11 +122,11 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
if( aDirectory.empty() )
{
path.Assign( m_filename );
path.SetExt( wxT( "json" ) );
path.SetExt( getFileExt() );
}
else
{
path.Assign( aDirectory, m_filename, wxT( "json" ) );
path.Assign( aDirectory, m_filename, getFileExt() );
}
if( !path.Exists() )
@ -212,7 +213,7 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
// If we migrated, clean up the legacy file (with no extension)
if( legacy_migrated || migrated )
{
if( legacy_migrated && !wxRemoveFile( path.GetFullPath() ) )
if( legacy_migrated && m_deleteLegacyAfterMigration && !wxRemoveFile( path.GetFullPath() ) )
{
wxLogTrace(
traceSettings, "Warning: could not remove legacy file %s", path.GetFullPath() );
@ -261,11 +262,11 @@ bool JSON_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce )
if( aDirectory.empty() )
{
path.Assign( m_filename );
path.SetExt( wxT( "json" ) );
path.SetExt( getFileExt() );
}
else
{
path.Assign( aDirectory, m_filename, wxT( "json" ) );
path.Assign( aDirectory, m_filename, getFileExt() );
}
if( !m_createIfMissing && !path.FileExists() )

View File

@ -34,13 +34,14 @@ const int projectFileSchemaVersion = 1;
PROJECT_FILE::PROJECT_FILE( const std::string& aFullPath ) :
JSON_SETTINGS( aFullPath, SETTINGS_LOC::NONE, projectFileSchemaVersion ),
m_sheets(), m_boards(), m_legacyVars()
m_sheets(), m_boards()
{
// Keep old files around
m_deleteLegacyAfterMigration = false;
m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "sheets", &m_sheets, {} ) );
m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "boards", &m_boards, {} ) );
m_params.emplace_back( new PARAM_MAP<wxString>( "legacy", &m_legacyVars, {} ) );
}
@ -48,7 +49,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
{
bool ret = true;
wxString str;
long dummy;
long index = 0;
// Legacy files don't store board info; they assume board matches project name
// We will leave m_boards empty here so it can be populated with other code
@ -56,18 +57,28 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
auto loadSheetNames =
[&]() -> bool
{
int index = 1;
int sheet = 1;
wxString entry;
nlohmann::json arr = nlohmann::json::array();
wxLogTrace( traceSettings, "Migrating sheet names" );
aLegacyFile->SetPath( GROUP_SHEET_NAMES );
while( aLegacyFile->Read( wxString::Format( "%d", index++ ), &entry ) )
while( aLegacyFile->Read( wxString::Format( "%d", sheet++ ), &entry ) )
{
wxArrayString tokens = wxSplit( entry, ':' );
if( tokens.size() == 2 )
m_sheets.emplace_back( std::make_pair( KIID( tokens[0] ), tokens[1] ) );
{
wxLogTrace( traceSettings, "%d: %s = %s", sheet, tokens[0], tokens[1] );
arr.push_back( nlohmann::json::array( { tokens[0], tokens[1] } ) );
}
}
( *this )[PointerFromString( "sheets" )] = arr;
aLegacyFile->SetPath( "/" );
// TODO: any reason we want to fail on this?
return true;
@ -75,46 +86,73 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
std::vector<wxString> groups;
bool more = aLegacyFile->GetFirstGroup( str, dummy );
while( more )
while( aLegacyFile->GetNextGroup( str, index ) )
{
if( str == GROUP_SHEET_NAMES )
if( str == wxString( GROUP_SHEET_NAMES ).Mid( 1 ) )
ret |= loadSheetNames();
else
groups.emplace_back( str );
more = aLegacyFile->GetNextGroup( str, dummy );
groups.emplace_back( "/" + str );
}
auto loadLegacyPairs =
[&]( const wxString& aGroup = wxEmptyString ) -> bool
[&]( const std::string& aGroup ) -> bool
{
aLegacyFile->SetPath( aGroup );
wxLogTrace( traceSettings, "Migrating group %s", aGroup );
bool success = true;
wxString keyStr;
wxString val;
bool morePairs = aLegacyFile->GetFirstEntry( str, dummy );
index = 0;
while( morePairs )
while( aLegacyFile->GetNextEntry( keyStr, index ) )
{
wxString val = aLegacyFile->Read( str );
std::string key( str.ToUTF8() );
m_legacyVars[key] = val;
morePairs = aLegacyFile->GetNextEntry( str, dummy );
if( !aLegacyFile->Read( keyStr, &val ) )
continue;
std::string key( keyStr.ToUTF8() );
wxLogTrace( traceSettings, " %s = %s", key, val );
try
{
nlohmann::json::json_pointer ptr( "/legacy" + aGroup + "/" + key );
( *this )[ptr] = val;
}
catch( ... )
{
success = false;
}
}
// TODO: any reason we want to fail on this?
return true;
return success;
};
ret &= loadLegacyPairs();
ret &= loadLegacyPairs( "" );
for( const auto& groupName : groups )
ret &= loadLegacyPairs( groupName );
for( size_t i = 0; i < groups.size(); i++ )
{
aLegacyFile->SetPath( groups[i] );
ret &= loadLegacyPairs( groups[i].ToStdString() );
index = 0;
while( aLegacyFile->GetNextGroup( str, index ) )
groups.emplace_back( groups[i] + "/" + str );
aLegacyFile->SetPath( "/" );
}
return ret;
}
wxString PROJECT_FILE::getFileExt() const
{
return ProjectFileExtension;
}
wxString PROJECT_FILE::getLegacyFileExt() const
{
return LegacyProjectFileExtension;

View File

@ -35,6 +35,7 @@
#include <settings/common_settings.h>
#include <settings/project_file.h>
#include <settings/settings_manager.h>
#include <wildcards_and_files_ext.h>
/**
@ -44,6 +45,10 @@
const char* traceSettings = "SETTINGS";
/// Project settings path will be <projectname> + this
#define PROJECT_SETTINGS_DIR_SUFFIX wxT( "-settings" )
SETTINGS_MANAGER::SETTINGS_MANAGER() :
m_common_settings( nullptr ),
m_migration_source()
@ -664,20 +669,28 @@ bool SETTINGS_MANAGER::extractVersion( const std::string& aVersionString, int* a
bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath )
{
// Normalize path to new format even if migrating from a legacy file
wxFileName path( aFullPath );
if( path.GetExt() == LegacyProjectFileExtension )
path.SetExt( ProjectFileExtension );
wxString fullPath = path.GetFullPath();
// Unload if already loaded
if( m_projects.count( aFullPath ) && !UnloadProject( *m_projects.at( aFullPath ).get() ) )
if( m_projects.count( fullPath ) && !UnloadProject( *m_projects.at( fullPath ).get() ) )
return false;
// No MDI yet
if( !m_projects.empty() && !UnloadProject( *m_projects.begin()->second ) )
return false;
wxLogTrace( traceSettings, "Load project %s", aFullPath );
wxLogTrace( traceSettings, "Load project %s", fullPath );
m_projects[aFullPath] = std::make_unique<PROJECT>();
m_projects[aFullPath]->SetProjectFullName( aFullPath );
m_projects[fullPath] = std::make_unique<PROJECT>();
m_projects[fullPath]->setProjectFullName( fullPath );
return loadProjectFile( *m_projects[aFullPath] );
return loadProjectFile( *m_projects[fullPath] );
}
@ -705,6 +718,25 @@ PROJECT& SETTINGS_MANAGER::Prj() const
}
bool SETTINGS_MANAGER::SaveProject()
{
wxString name = Prj().GetProjectFullName();
if( !m_project_files.count( name ) )
return false;
m_project_files[name]->SaveToFile();
return true;
}
wxString SETTINGS_MANAGER::GetProjectSettingsPath() const
{
return Prj().GetProjectPath() + Prj().GetProjectName() + PROJECT_SETTINGS_DIR_SUFFIX;
}
bool SETTINGS_MANAGER::loadProjectFile( PROJECT& aProject )
{
std::string fn( aProject.GetProjectFullName().ToUTF8() );
@ -714,6 +746,8 @@ bool SETTINGS_MANAGER::loadProjectFile( PROJECT& aProject )
m_project_files[aProject.GetProjectFullName()] = file;
aProject.setProjectFile( file );
return file->LoadFromFile();
}

View File

@ -189,7 +189,19 @@ wxString LegacySymbolLibFileWildcard()
wxString ProjectFileWildcard()
{
return _( "KiCad project files" ) + AddFileExtListToFilter( { "pro" } );
return _( "KiCad project files" ) + AddFileExtListToFilter( { "kicad_pro" } );
}
wxString LegacyProjectFileWildcard()
{
return _( "KiCad legacy project files" ) + AddFileExtListToFilter( { "pro" } );
}
wxString AllProjectFilesWildcard()
{
return _( "All KiCad project files" ) + AddFileExtListToFilter( { "kicad_pro", "pro" } );
}

View File

@ -54,6 +54,7 @@
#include <connection_graph.h>
#include <tool/actions.h>
#include <tools/sch_editor_control.h>
#include <settings/settings_manager.h>
#include <netlist.h>
#include <widgets/infobar.h>
@ -303,7 +304,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
if( pro.GetFullPath() != Prj().GetProjectFullName()
|| !Prj().GetElem( PROJECT::ELEM_SCH_PART_LIBS ) )
{
Prj().SetProjectFullName( pro.GetFullPath() );
GetSettingsManager()->LoadProject( pro.GetFullPath() );
// load the libraries here, not in SCH_SCREEN::Draw() which is a context
// that will not tolerate DisplayError() dialog since we're already in an
@ -322,7 +323,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// Make sure the project file name is set (it won't be in standalone mode)
if( pro.GetFullPath() != Prj().GetProjectFullName() )
Prj().SetProjectFullName( pro.GetFullPath() );
GetSettingsManager()->LoadProject( pro.GetFullPath() );
LoadProjectFile();
@ -605,8 +606,8 @@ void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
if( setProject )
{
wxFileName projectFn( dlg.GetPath() );
projectFn.SetExt( LegacyProjectFileExtension );
Prj().SetProjectFullName( projectFn.GetFullPath() );
projectFn.SetExt( ProjectFileExtension );
GetSettingsManager()->LoadProject( projectFn.GetFullPath() );
}
// For now there is only one import plugin

View File

@ -47,6 +47,7 @@ class S3D_CACHE;
class KIWAY;
class SYMBOL_LIB_TABLE;
class FILENAME_RESOLVER;
class PROJECT_FILE;
#define VTBL_ENTRY virtual
@ -58,6 +59,9 @@ class FILENAME_RESOLVER;
*/
class PROJECT
{
friend class SETTINGS_MANAGER; // so that SM can set project path
friend class TEST_NETLISTS_FIXTURE; // TODO(JE) make this not required
public:
/// A PROJECT can hold stuff it knows nothing about, in the form of
@ -84,16 +88,6 @@ public:
// VTBL_ENTRY bool MaybeLoadProjectSettings( const std::vector<wxString>& aFileSet );
/**
* Function SetProjectFullName
* sets the:
* 1) full directory, 2) basename, and 3) extension of the project. This is
* the name of the *.pro file with full absolute path and it also defines
* the name of the project. The project name and the *.pro file names are
* exactly the same, providing the *.pro filename is absolute.
*/
VTBL_ENTRY void SetProjectFullName( const wxString& aFullPathAndName );
/**
* Function GetProjectFullName
* returns the full path and name of the project. This is the same as the
@ -321,6 +315,25 @@ public:
private:
/**
* Sets the:
* 1) full directory, 2) basename, and 3) extension of the project. This is
* the name of the *.pro file with full absolute path and it also defines
* the name of the project. The project name and the *.pro file names are
* exactly the same, providing the *.pro filename is absolute.
*/
VTBL_ENTRY void setProjectFullName( const wxString& aFullPathAndName );
/**
* Sets the backing store file for this project
* Should only be called by SETTINGS_MANGER on load.
* @param aFile is a loaded PROJECT_FILE
*/
VTBL_ENTRY void setProjectFile( PROJECT_FILE* aFile )
{
m_projectFile = aFile;
}
/**
* Function configCreate
* loads a *.pro file and returns a wxConfigBase.
@ -340,6 +353,9 @@ private:
wxFileName m_project_name; ///< \<fullpath\>/\<basename\>.pro
wxString m_pro_date_and_time;
/// Backing store for project data -- owned by SETTINGS_MANAGER
PROJECT_FILE* m_projectFile;
std::map<KIID, wxString> m_sheetNames;
std::map<wxString, wxString> m_textVars;

View File

@ -213,6 +213,11 @@ protected:
bool fromLegacyColor( wxConfigBase* aConfig, const std::string& aKey,
const std::string& aDest );
virtual wxString getFileExt() const
{
return wxT( "json" );
}
virtual wxString getLegacyFileExt() const
{
return wxEmptyString;
@ -245,6 +250,9 @@ protected:
/// Whether or not the backing store file should be written
bool m_writeFile;
/// Whether or not to delete legacy file after migration
bool m_deleteLegacyAfterMigration;
/// Version of this settings schema.
int m_schemaVersion;

View File

@ -52,6 +52,8 @@ public:
virtual bool MigrateFromLegacy( wxConfigBase* aLegacyFile ) override;
protected:
wxString getFileExt() const override;
wxString getLegacyFileExt() const override;
private:
@ -61,13 +63,6 @@ private:
/// A list of board files in this project
std::vector<FILE_INFO_PAIR> m_boards;
/**
* Stores all K/V pairs migrated from a legacy (.pro) file so that the legacy file can be
* removed before these settings are migrated in by PROJECT_SETTINGS objects later.
*/
std::map<std::string, wxString> m_legacyVars;
};
// Specializations to allow directly reading/writing FILE_INFO_PAIRs from JSON

View File

@ -182,9 +182,9 @@ public:
void ReloadColorSettings();
/**
* Loads a project
* Loads a project or sets up a new project with a specified path
* @param aFullPath is the full path to the project
* @return true if the PROJECT_FILE was successfully loaded
* @return true if the PROJECT_FILE was successfully loaded from disk
*/
bool LoadProject( const wxString& aFullPath );
@ -201,6 +201,18 @@ public:
*/
PROJECT& Prj() const;
/**
* Saves the one and only project
* TODO: Update for MDI
* @return true if save was successful
*/
bool SaveProject();
/**
* @return the path to the settings folder for the loaded project
*/
wxString GetProjectSettingsPath() const;
/**
* Checks if a given path is probably a valid KiCad configuration directory.
* Actually it just checks if a file called "kicad_common" exists, because that's probably

View File

@ -176,6 +176,8 @@ extern wxString SchematicSymbolFileWildcard();
extern wxString KiCadSymbolLibFileWildcard();
extern wxString LegacySymbolLibFileWildcard();
extern wxString ProjectFileWildcard();
extern wxString LegacyProjectFileWildcard();
extern wxString AllProjectFilesWildcard();
extern wxString KiCadSchematicFileWildcard();
extern wxString LegacySchematicFileWildcard();
extern wxString BoardFileWildcard();

View File

@ -37,6 +37,7 @@
#include <launch_ext.h>
#include <panel_hotkeys_editor.h>
#include <settings/common_settings.h>
#include <settings/settings_manager.h>
#include <tool/action_toolbar.h>
#include <tool/common_control.h>
#include <tool/tool_manager.h>
@ -203,8 +204,6 @@ void KICAD_MANAGER_FRAME::SetProjectFileName( const wxString& aFullProjectProFil
if( !fn.IsAbsolute() )
fn.MakeAbsolute();
Prj().SetProjectFullName( fn.GetFullPath() );
SetTitle( wxString( "KiCad " ) + GetBuildVersion() );
wxString title = GetTitle() + " " + fn.GetFullPath();
@ -343,12 +342,13 @@ void KICAD_MANAGER_FRAME::LoadProject( const wxFileName& aProjectFileName )
// Save the project file for the currently loaded project.
if( m_active_project )
Prj().ConfigLoad( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
Pgm().GetSettingsManager().SaveProject();
m_active_project = true;
ClearMsg();
SetProjectFileName( aProjectFileName.GetFullPath() );
Prj().ConfigLoad( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
Pgm().GetSettingsManager().LoadProject( aProjectFileName.GetFullPath() );
SetProjectFileName( Prj().GetProjectFullName() );
if( aProjectFileName.IsDirWritable() )
SetMruPath( Prj().GetProjectPath() ); // Only set MRU path if we have write access. Why?
@ -381,13 +381,17 @@ void KICAD_MANAGER_FRAME::CreateNewProject( const wxFileName& aProjectFileName )
// Copy kicad.pro file from template folder.
if( !aProjectFileName.FileExists() )
{
// TODO(JE) provide in new format
wxString srcFileName = sys_search().FindValidPath( "kicad.pro" );
wxFileName destFileName( aProjectFileName );
destFileName.SetExt( LegacyProjectFileExtension );
// Create a minimal project (.pro) file if the template project file could not be copied.
if( !wxFileName::FileExists( srcFileName )
|| !wxCopyFile( srcFileName, aProjectFileName.GetFullPath() ) )
|| !wxCopyFile( srcFileName, destFileName.GetFullPath() ) )
{
Prj().ConfigSave( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
Pgm().GetSettingsManager().SaveProject();
}
}

View File

@ -292,15 +292,17 @@ int KICAD_MANAGER_CONTROL::NewFromTemplate( const TOOL_EVENT& aEvent )
int KICAD_MANAGER_CONTROL::OpenProject( const TOOL_EVENT& aEvent )
{
wxString wildcard = ProjectFileWildcard() + "|" + LegacyProjectFileWildcard() + "|"
+ AllProjectFilesWildcard();
wxString default_dir = m_frame->GetMruPath();
wxFileDialog dlg( m_frame, _( "Open Existing Project" ), default_dir, wxEmptyString,
ProjectFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
if( dlg.ShowModal() == wxID_CANCEL )
return -1;
wxFileName pro( dlg.GetPath() );
pro.SetExt( LegacyProjectFileExtension ); // enforce extension
if( !pro.IsAbsolute() )
pro.MakeAbsolute();

View File

@ -51,6 +51,7 @@
#include <ratsnest/ratsnest_data.h>
#include <wx/wupdlock.h>
#include <settings/settings_manager.h>
//#define USE_INSTRUMENTATION 1
@ -310,7 +311,7 @@ bool PCB_EDIT_FRAME::Files_io_from_id( int id )
wxFileName fn( wxStandardPaths::Get().GetDocumentsDir(), wxT( "noname" ),
LegacyProjectFileExtension );
Prj().SetProjectFullName( fn.GetFullPath() );
GetSettingsManager()->LoadProject( fn.GetFullPath() );
onBoardLoaded();
@ -507,7 +508,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// The calling code should know not to ask me here to change projects unless
// it knows what consequences that will have on other KIFACEs running and using
// this same PROJECT. It can be very harmful if that calling code is stupid.
Prj().SetProjectFullName( pro.GetFullPath() );
GetSettingsManager()->LoadProject( pro.GetFullPath() );
// load project settings before BOARD
LoadProjectSettings();

View File

@ -81,9 +81,10 @@ void TEST_NETLISTS_FIXTURE::loadSchematic( const wxString& aBaseName )
BOOST_TEST_MESSAGE( fn );
wxFileName pro( fn );
pro.SetExt( LegacyProjectFileExtension );
pro.SetExt( ProjectFileExtension );
m_project.SetProjectFullName( pro.GetFullPath() );
// TODO(JE) Make this not required
m_project.setProjectFullName( pro.GetFullPath() );
m_project.SetElem( PROJECT::ELEM_SCH_PART_LIBS, nullptr );
m_schematic.Reset();