Add PROJECT_FILE and basic load/unload methods

This commit is contained in:
Jon Evans 2020-05-25 13:06:53 -04:00
parent b5adf5bb21
commit a7708fa6dc
21 changed files with 363 additions and 32 deletions

View File

@ -398,6 +398,7 @@ set( COMMON_SRCS
settings/common_settings.cpp
settings/json_settings.cpp
settings/nested_settings.cpp
settings/project_file.cpp
settings/settings_manager.cpp
libeval/numeric_evaluator.cpp

View File

@ -92,7 +92,7 @@ void PROJECT::SetProjectFullName( const wxString& aFullPathAndName )
wxASSERT( m_project_name.IsAbsolute() );
wxASSERT( m_project_name.GetExt() == ProjectFileExtension );
wxASSERT( m_project_name.GetExt() == LegacyProjectFileExtension );
// until multiple projects are in play, set an environment variable for the
// the project pointer.
@ -279,7 +279,7 @@ static bool copy_pro_file_template( const SEARCH_STACK& aSearchS, const wxString
return false;
}
wxString templateFile = wxT( "kicad." ) + ProjectFileExtension;
wxString templateFile = wxT( "kicad." ) + LegacyProjectFileExtension;
wxString kicad_pro_template = aSearchS.FindValidPath( templateFile );
@ -289,7 +289,7 @@ static bool copy_pro_file_template( const SEARCH_STACK& aSearchS, const wxString
__func__, TO_UTF8( templateFile ) );
wxFileName templ( wxStandardPaths::Get().GetDocumentsDir(),
wxT( "kicad" ), ProjectFileExtension );
wxT( "kicad" ), LegacyProjectFileExtension );
if( !templ.IsFileReadable() )
{

View File

@ -84,14 +84,16 @@ void JSON_SETTINGS::Load()
}
void JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
{
// First, load all params to default values
clear();
Load();
bool migrated = false;
bool legacy_migrated = false;
bool success = true;
bool migrated = false;
bool legacy_migrated = false;
LOCALE_IO locale;
auto migrateFromLegacy = [&] ( wxFileName& aPath ) {
@ -114,12 +116,22 @@ void JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
legacy_migrated = true;
};
wxFileName path( aDirectory, m_filename, "json" );
wxFileName path;
if( aDirectory.empty() )
{
path.Assign( m_filename );
path.SetExt( wxT( "json" ) );
}
else
{
path.Assign( aDirectory, m_filename, wxT( "json" ) );
}
if( !path.Exists() )
{
// Case 1: legacy migration, no .json extension yet
path.ClearExt();
path.SetExt( getLegacyFileExt() );
if( path.Exists() )
{
@ -133,6 +145,10 @@ void JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
if( path.Exists() )
migrateFromLegacy( path );
}
else
{
success = false;
}
}
else
{
@ -151,6 +167,7 @@ void JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
catch( ... )
{
wxLogTrace( traceSettings, "%s: file version could not be read!", m_filename );
success = false;
}
if( filever >= 0 && filever < m_schemaVersion )
@ -204,6 +221,8 @@ void JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
// And write-out immediately so that we don't lose data if the program later crashes.
SaveToFile( aDirectory );
}
return success;
}
@ -233,7 +252,17 @@ bool JSON_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce )
if( !m_writeFile )
return false;
wxFileName path( aDirectory, m_filename, "json" );
wxFileName path;
if( aDirectory.empty() )
{
path.Assign( m_filename );
path.SetExt( wxT( "json" ) );
}
else
{
path.Assign( aDirectory, m_filename, wxT( "json" ) );
}
if( !m_createIfMissing && !path.FileExists() )
{

View File

@ -46,9 +46,10 @@ NESTED_SETTINGS::~NESTED_SETTINGS()
}
void NESTED_SETTINGS::LoadFromFile( const std::string& aDirectory )
bool NESTED_SETTINGS::LoadFromFile( const std::string& aDirectory )
{
clear();
bool success = false;
if( m_parent )
{
@ -58,6 +59,8 @@ void NESTED_SETTINGS::LoadFromFile( const std::string& aDirectory )
wxLogTrace( traceSettings, "Loaded NESTED_SETTINGS %s with schema %d", GetFilename(),
m_schemaVersion );
success = true;
}
catch( ... )
{
@ -67,6 +70,8 @@ void NESTED_SETTINGS::LoadFromFile( const std::string& aDirectory )
}
Load();
return success;
}

View File

@ -0,0 +1,135 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 CERN
* @author Jon Evans <jon@craftyjon.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config_params.h>
#include <settings/common_settings.h>
#include <settings/parameters.h>
#include <settings/project_file.h>
#include <wildcards_and_files_ext.h>
#include <wx/config.h>
#include <wx/log.h>
extern const char* traceSettings;
///! Update the schema version whenever a migration is required
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_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, {} ) );
}
bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
{
bool ret = true;
wxString str;
long dummy;
// 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
auto loadSheetNames =
[&]() -> bool
{
int index = 1;
wxString entry;
aLegacyFile->SetPath( GROUP_SHEET_NAMES );
while( aLegacyFile->Read( wxString::Format( "%d", index++ ), &entry ) )
{
wxArrayString tokens = wxSplit( entry, ':' );
if( tokens.size() == 2 )
m_sheets.emplace_back( std::make_pair( KIID( tokens[0] ), tokens[1] ) );
}
// TODO: any reason we want to fail on this?
return true;
};
std::vector<wxString> groups;
bool more = aLegacyFile->GetFirstGroup( str, dummy );
while( more )
{
if( str == GROUP_SHEET_NAMES )
ret |= loadSheetNames();
else
groups.emplace_back( str );
more = aLegacyFile->GetNextGroup( str, dummy );
}
auto loadLegacyPairs =
[&]( const wxString& aGroup = wxEmptyString ) -> bool
{
aLegacyFile->SetPath( aGroup );
bool morePairs = aLegacyFile->GetFirstEntry( str, dummy );
while( morePairs )
{
wxString val = aLegacyFile->Read( str );
std::string key( str.ToUTF8() );
m_legacyVars[key] = val;
morePairs = aLegacyFile->GetNextEntry( str, dummy );
}
// TODO: any reason we want to fail on this?
return true;
};
ret &= loadLegacyPairs();
for( const auto& groupName : groups )
ret &= loadLegacyPairs( groupName );
return ret;
}
wxString PROJECT_FILE::getLegacyFileExt() const
{
return LegacyProjectFileExtension;
}
void to_json( nlohmann::json& aJson, const FILE_INFO_PAIR& aPair )
{
aJson = nlohmann::json::array( { aPair.first.AsString().ToUTF8(), aPair.second.ToUTF8() } );
}
void from_json( const nlohmann::json& aJson, FILE_INFO_PAIR& aPair )
{
wxASSERT( aJson.is_array() && aJson.size() == 2 );
aPair.first = KIID( wxString( aJson[0].get<std::string>().c_str(), wxConvUTF8 ) );
aPair.second = wxString( aJson[1].get<std::string>().c_str(), wxConvUTF8 );
}

View File

@ -29,10 +29,12 @@
#include <dialogs/dialog_migrate_settings.h>
#include <gestfich.h>
#include <macros.h>
#include <project.h>
#include <settings/app_settings.h>
#include <settings/common_settings.h>
#include <settings/settings_manager.h>
#include <settings/color_settings.h>
#include <settings/common_settings.h>
#include <settings/project_file.h>
#include <settings/settings_manager.h>
/**
@ -330,6 +332,9 @@ std::string SETTINGS_MANAGER::GetPathForSettingsFile( JSON_SETTINGS* aSettings )
case SETTINGS_LOC::COLORS:
return GetColorSettingsPath();
case SETTINGS_LOC::NONE:
return "";
default:
wxASSERT_MSG( false, "Unknown settings location!" );
}
@ -654,3 +659,49 @@ bool SETTINGS_MANAGER::extractVersion( const std::string& aVersionString, int* a
return false;
}
bool SETTINGS_MANAGER::LoadProject( PROJECT& aProject )
{
wxString name = aProject.GetProjectFullName();
std::string fn( name.ToUTF8() );
// Unload if already loaded
if( m_project_files.count( name ) )
UnloadProject( aProject );
PROJECT_FILE* file =
static_cast<PROJECT_FILE*>( RegisterSettings( new PROJECT_FILE( fn ), false ) );
m_project_files[aProject.GetProjectFullName()] = file;
return file->LoadFromFile();
}
bool SETTINGS_MANAGER::UnloadProject( PROJECT& aProject )
{
wxString name = aProject.GetProjectFullName();
if( !m_project_files.count( name ) )
return false;
PROJECT_FILE* file = m_project_files[name];
auto it = std::find_if( m_settings.begin(), m_settings.end(),
[&file]( const std::unique_ptr<JSON_SETTINGS>& aPtr )
{
return aPtr.get() == file;
} );
if( it != m_settings.end() )
{
wxLogTrace( traceSettings, "Unload project %s", ( *it )->GetFilename() );
( *it )->SaveToFile();
m_settings.erase( it );
}
m_project_files.erase( name );
return true;
}

View File

@ -120,7 +120,8 @@ const std::string LegacySymbolLibFileExtension( "lib" );
const std::string VrmlFileExtension( "wrl" );
const std::string ProjectFileExtension( "pro" );
const std::string ProjectFileExtension( "kicad_pro" );
const std::string LegacyProjectFileExtension( "pro" );
const std::string LegacySchematicFileExtension( "sch" );
const std::string KiCadSchematicFileExtension( "kicad_sch" );
const std::string NetlistFileExtension( "net" );

View File

@ -56,7 +56,7 @@ bool DIALOG_SCH_IMPORT_SETTINGS::TransferDataToWindow()
void DIALOG_SCH_IMPORT_SETTINGS::OnBrowseClicked( wxCommandEvent& event )
{
wxFileName fn = m_frame->Schematic().Root().GetFileName();
fn.SetExt( ProjectFileExtension );
fn.SetExt( LegacyProjectFileExtension );
wxFileDialog dlg( this, _( "Import Settings From" ), fn.GetPath(), fn.GetFullName(),
ProjectFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR );

View File

@ -369,7 +369,7 @@ void SCH_EDIT_FRAME::SaveProjectSettings()
PROJECT& prj = Prj();
wxFileName fn = Schematic().RootScreen()->GetFileName(); //ConfigFileName
fn.SetExt( ProjectFileExtension );
fn.SetExt( LegacyProjectFileExtension );
if( !fn.HasName() || !IsWritable( fn ) )
return;

View File

@ -260,7 +260,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
return false;
wxFileName pro = fullFileName;
pro.SetExt( ProjectFileExtension );
pro.SetExt( LegacyProjectFileExtension );
bool is_new = !wxFileName::IsFileReadable( fullFileName );
@ -605,7 +605,7 @@ void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
if( setProject )
{
wxFileName projectFn( dlg.GetPath() );
projectFn.SetExt( ProjectFileExtension );
projectFn.SetExt( LegacyProjectFileExtension );
Prj().SetProjectFullName( projectFn.GetFullPath() );
}

View File

@ -38,6 +38,7 @@ enum class SETTINGS_LOC {
PROJECT, ///< The settings directory inside a project folder
COLORS, ///< The color scheme directory (e.g. ~/.config/kicad/colors/)
NESTED, ///< Not stored in a file, but inside another JSON_SETTINGS
NONE, ///< No directory prepended, full path in filename (used for PROJECT_FILE)
};
@ -73,8 +74,9 @@ public:
/**
* Loads the backing file from disk and then calls Load()
* @param aDirectory is the path to the file
* @return true if the file was found on disk and loaded or migrated
*/
virtual void LoadFromFile( const std::string& aDirectory );
virtual bool LoadFromFile( const std::string& aDirectory = "" );
/**
* Calls Store() and then writes the contents of the JSON document to a file
@ -82,7 +84,7 @@ public:
* @param aForce if true will always save, even if contents are not modified
* @return true if the file was saved
*/
virtual bool SaveToFile( const std::string& aDirectory, bool aForce = false );
virtual bool SaveToFile( const std::string& aDirectory = "", bool aForce = false );
/**
* Resets all parameters to default values. Does NOT write to file or update underlying JSON.
@ -211,6 +213,11 @@ protected:
bool fromLegacyColor( wxConfigBase* aConfig, const std::string& aKey,
const std::string& aDest );
virtual wxString getLegacyFileExt() const
{
return wxEmptyString;
}
/// The filename (not including path) of this settings file
std::string m_filename;

View File

@ -40,7 +40,7 @@ public:
* Loads the JSON document from the parent and then calls Load()
* @param aDirectory
*/
void LoadFromFile( const std::string& aDirectory = "" ) override;
bool LoadFromFile( const std::string& aDirectory = "" ) override;
/**
* Calls Store() and then saves the JSON document contents into the parent JSON_SETTINGS

View File

@ -0,0 +1,79 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 CERN
* @author Jon Evans <jon@craftyjon.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_PROJECT_FILE_H
#define KICAD_PROJECT_FILE_H
#include <common.h>
#include <settings/json_settings.h>
/**
* For files like sheets and boards, a pair of that object KIID and display name
* Display name is typically blank for the project root sheet
*/
typedef std::pair<KIID, wxString> FILE_INFO_PAIR;
/**
* PROJECT_FILE is the backing store for a PROJECT, in JSON format.
*
* There is either zero or one PROJECT_FILE for every PROJECT
* (you can have a dummy PROJECT that has no file)
*/
class PROJECT_FILE : public JSON_SETTINGS
{
public:
/**
* Constructs the project file for a project
* @param aFullPath is the full disk path to the project
*/
PROJECT_FILE( const std::string& aFullPath );
virtual ~PROJECT_FILE() {}
virtual bool MigrateFromLegacy( wxConfigBase* aLegacyFile ) override;
protected:
wxString getLegacyFileExt() const override;
private:
/// An list of schematic sheets in this project
std::vector<FILE_INFO_PAIR> m_sheets;
/// 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
void to_json( nlohmann::json& aJson, const FILE_INFO_PAIR& aPair );
void from_json( const nlohmann::json& aJson, FILE_INFO_PAIR& aPair );
#endif

View File

@ -26,6 +26,8 @@
class COLOR_SETTINGS;
class COMMON_SETTINGS;
class PROJECT;
class PROJECT_FILE;
class SETTINGS_MANAGER
@ -179,6 +181,20 @@ public:
*/
void ReloadColorSettings();
/**
* Registers a project and attempts to load the associated PROJECT_FILE
* @param aProject is the project object to load
* @return true if the PROJECT_FILE was successfully loaded
*/
bool LoadProject( PROJECT& aProject );
/**
* Saves, unloads and unregisters the given project
* @param aProject is the project object to unload
* @return true if the PROJECT file was successfully saved
*/
bool UnloadProject( PROJECT& aProject );
/**
* 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
@ -269,6 +285,12 @@ private:
/// True if settings loaded successfully at construction
bool m_ok;
/// Project files, mapped according to project full name
std::map<wxString, PROJECT_FILE*> m_project_files;
/// A list of project settings objects for each loaded project
// std::map<wxString, std::vector<PROJECT_SETTINGS*>> m_project_settings;
};
#endif

View File

@ -115,6 +115,7 @@ extern const std::string SchematicBackupFileExtension;
extern const std::string VrmlFileExtension;
extern const std::string ProjectFileExtension;
extern const std::string LegacyProjectFileExtension;
extern const std::string LegacySchematicFileExtension;
extern const std::string KiCadSchematicFileExtension;
extern const std::string NetlistFileExtension;

View File

@ -77,7 +77,7 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
wxFileName pro = sch;
pro.SetExt( ProjectFileExtension );
pro.SetExt( LegacyProjectFileExtension );
wxString protitle = _( "KiCad Project Destination" );
@ -115,7 +115,7 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
}
wxFileName pcb( sch );
pro.SetExt( ProjectFileExtension ); // enforce extension
pro.SetExt( LegacyProjectFileExtension ); // enforce extension
pcb.SetExt( LegacyPcbFileExtension ); // enforce extension
if( !pro.IsAbsolute() )

View File

@ -95,10 +95,10 @@ int KICAD_MANAGER_CONTROL::NewProject( const TOOL_EVENT& aEvent )
// wxFileName automatically extracts an extension. But if it isn't
// a .pro extension, we should keep it as part of the filename
if( !pro.GetExt().IsEmpty()
&& pro.GetExt().ToStdString() != ProjectFileExtension )
&& pro.GetExt().ToStdString() != LegacyProjectFileExtension )
pro.SetName( pro.GetName() + wxT( "." ) + pro.GetExt() );
pro.SetExt( ProjectFileExtension ); // enforce extension
pro.SetExt( LegacyProjectFileExtension ); // enforce extension
if( !pro.IsAbsolute() )
pro.MakeAbsolute();
@ -193,10 +193,10 @@ int KICAD_MANAGER_CONTROL::NewFromTemplate( const TOOL_EVENT& aEvent )
// wxFileName automatically extracts an extension. But if it isn't
// a .pro extension, we should keep it as part of the filename
if( !fn.GetExt().IsEmpty()
&& fn.GetExt().ToStdString() != ProjectFileExtension )
&& fn.GetExt().ToStdString() != LegacyProjectFileExtension )
fn.SetName( fn.GetName() + wxT( "." ) + fn.GetExt() );
fn.SetExt( ProjectFileExtension ); // enforce extension
fn.SetExt( LegacyProjectFileExtension ); // enforce extension
if( !fn.IsAbsolute() )
fn.MakeAbsolute();
@ -300,7 +300,7 @@ int KICAD_MANAGER_CONTROL::OpenProject( const TOOL_EVENT& aEvent )
return -1;
wxFileName pro( dlg.GetPath() );
pro.SetExt( ProjectFileExtension ); // enforce extension
pro.SetExt( LegacyProjectFileExtension ); // enforce extension
if( !pro.IsAbsolute() )
pro.MakeAbsolute();

View File

@ -509,7 +509,7 @@ void TREE_PROJECT_FRAME::ReCreateTreePrj()
fn.Clear();
fn.SetPath( wxStandardPaths::Get().GetDocumentsDir() );
fn.SetName( NAMELESS_PROJECT );
fn.SetExt( ProjectFileExtension );
fn.SetExt( LegacyProjectFileExtension );
}
bool prjOpened = fn.FileExists();

View File

@ -56,7 +56,7 @@ bool DIALOG_IMPORT_SETTINGS::TransferDataToWindow()
void DIALOG_IMPORT_SETTINGS::OnBrowseClicked( wxCommandEvent& event )
{
wxFileName fn = m_frame->GetBoard()->GetFileName();
fn.SetExt( ProjectFileExtension );
fn.SetExt( LegacyProjectFileExtension );
wxFileDialog dlg( this, _( "Import Settings From" ), fn.GetPath(), fn.GetFullName(),
ProjectFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR );

View File

@ -308,7 +308,7 @@ bool PCB_EDIT_FRAME::Files_io_from_id( int id )
return false;
wxFileName fn( wxStandardPaths::Get().GetDocumentsDir(), wxT( "noname" ),
ProjectFileExtension );
LegacyProjectFileExtension );
Prj().SetProjectFullName( fn.GetFullPath() );
@ -477,7 +477,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
ReleaseFile();
wxFileName pro = fullFileName;
pro.SetExt( ProjectFileExtension );
pro.SetExt( LegacyProjectFileExtension );
bool is_new = !wxFileName::IsFileReadable( fullFileName );

View File

@ -81,7 +81,7 @@ void TEST_NETLISTS_FIXTURE::loadSchematic( const wxString& aBaseName )
BOOST_TEST_MESSAGE( fn );
wxFileName pro( fn );
pro.SetExt( ProjectFileExtension );
pro.SetExt( LegacyProjectFileExtension );
m_project.SetProjectFullName( pro.GetFullPath() );
m_project.SetElem( PROJECT::ELEM_SCH_PART_LIBS, nullptr );