Installment one of project Save As... feature.
ADD: Adds Save As... to the File menu for the project window. Fixes: lp:594051 * https://bugs.launchpad.net/kicad/+bug/594051
This commit is contained in:
parent
d6e0ec2f24
commit
b5904b0401
|
@ -359,6 +359,21 @@ bool CanPrintFile( const wxString& file )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CopyFile( const wxString& aSrcPath, const wxString& aDestPath, std::string& aErrors )
|
||||||
|
{
|
||||||
|
if( !wxCopyFile( aSrcPath, aDestPath ) )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
if( !aErrors.empty() )
|
||||||
|
aErrors += "\n";
|
||||||
|
|
||||||
|
msg.Printf( _( "Cannot copy file \"%s\"." ), aDestPath );
|
||||||
|
aErrors += msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString QuoteFullPath( wxFileName& fn, wxPathFormat format )
|
wxString QuoteFullPath( wxFileName& fn, wxPathFormat format )
|
||||||
{
|
{
|
||||||
return wxT( "\"" ) + fn.GetFullPath( format ) + wxT( "\"" );
|
return wxT( "\"" ) + fn.GetFullPath( format ) + wxT( "\"" );
|
||||||
|
|
|
@ -28,6 +28,7 @@ include_directories(
|
||||||
./tools
|
./tools
|
||||||
../common
|
../common
|
||||||
../common/dialogs
|
../common/dialogs
|
||||||
|
../libs/sexpr/include
|
||||||
${INC_AFTER}
|
${INC_AFTER}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -353,6 +354,7 @@ target_include_directories( eeschema_kiface PUBLIC
|
||||||
|
|
||||||
target_link_libraries( eeschema_kiface
|
target_link_libraries( eeschema_kiface
|
||||||
common
|
common
|
||||||
|
sexpr
|
||||||
${wxWidgets_LIBRARIES}
|
${wxWidgets_LIBRARIES}
|
||||||
${GDI_PLUS_LIBRARIES}
|
${GDI_PLUS_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
#include <sch_edit_frame.h>
|
#include <sch_edit_frame.h>
|
||||||
#include <lib_edit_frame.h>
|
#include <lib_edit_frame.h>
|
||||||
#include <viewlib_frame.h>
|
#include <viewlib_frame.h>
|
||||||
#include <eda_text.h>
|
|
||||||
#include <general.h>
|
#include <general.h>
|
||||||
#include <class_libentry.h>
|
#include <class_libentry.h>
|
||||||
#include <transform.h>
|
#include <transform.h>
|
||||||
|
@ -42,6 +41,9 @@
|
||||||
#include <dialogs/panel_sym_lib_table.h>
|
#include <dialogs/panel_sym_lib_table.h>
|
||||||
#include <kiway.h>
|
#include <kiway.h>
|
||||||
#include <sim/sim_plot_frame.h>
|
#include <sim/sim_plot_frame.h>
|
||||||
|
#include <kiface_ids.h>
|
||||||
|
#include <libs/sexpr/include/sexpr/sexpr.h>
|
||||||
|
#include <libs/sexpr/include/sexpr/sexpr_parser.h>
|
||||||
|
|
||||||
// The main sheet of the project
|
// The main sheet of the project
|
||||||
SCH_SHEET* g_RootSheet = NULL;
|
SCH_SHEET* g_RootSheet = NULL;
|
||||||
|
@ -127,6 +129,16 @@ static struct IFACE : public KIFACE_I
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function SaveFileAs
|
||||||
|
* Saving a file under a different name is delegated to the various KIFACEs because
|
||||||
|
* the project doesn't know the internal format of the various files (which may have
|
||||||
|
* paths in them that need updating).
|
||||||
|
*/
|
||||||
|
void SaveFileAs( const std::string& aProjectBasePath, const std::string& aProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors ) override;
|
||||||
|
|
||||||
} kiface( "eeschema", KIWAY::FACE_SCH );
|
} kiface( "eeschema", KIWAY::FACE_SCH );
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -291,3 +303,149 @@ void IFACE::OnKifaceEnd()
|
||||||
wxConfigSaveSetups( KifaceSettings(), cfg_params() );
|
wxConfigSaveSetups( KifaceSettings(), cfg_params() );
|
||||||
end_common();
|
end_common();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void traverseSEXPR( SEXPR::SEXPR* aNode,
|
||||||
|
const std::function<void( SEXPR::SEXPR* )>& aVisitor )
|
||||||
|
{
|
||||||
|
aVisitor( aNode );
|
||||||
|
|
||||||
|
if( aNode->IsList() )
|
||||||
|
{
|
||||||
|
for( int i = 0; i < aNode->GetNumberOfChildren(); i++ )
|
||||||
|
traverseSEXPR( aNode->GetChild( i ), aVisitor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IFACE::SaveFileAs( const std::string& aProjectBasePath, const std::string& aProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors )
|
||||||
|
{
|
||||||
|
wxFileName destFile( aSrcFilePath );
|
||||||
|
wxString destPath = destFile.GetPath();
|
||||||
|
wxString ext = destFile.GetExt();
|
||||||
|
|
||||||
|
if( destPath.StartsWith( aProjectBasePath ) )
|
||||||
|
{
|
||||||
|
destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
|
||||||
|
destFile.SetPath( destPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ext == "sch" || ext == "sch-bak" )
|
||||||
|
{
|
||||||
|
if( destFile.GetName() == aProjectName )
|
||||||
|
destFile.SetName( aNewProjectName );
|
||||||
|
|
||||||
|
// JEY TODO: need to update at least sheet-paths...
|
||||||
|
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else if( ext == "sym" )
|
||||||
|
{
|
||||||
|
// Symbols are not project-specific. Keep their source names.
|
||||||
|
wxCopyFile( aSrcFilePath, destFile.GetFullPath() );
|
||||||
|
}
|
||||||
|
else if( ext == "lib" )
|
||||||
|
{
|
||||||
|
if( destFile.GetName() == aProjectName )
|
||||||
|
destFile.SetName( aNewProjectName );
|
||||||
|
else if( destFile.GetName() == aProjectName + "-cache" )
|
||||||
|
destFile.SetName( aNewProjectName + "-cache" );
|
||||||
|
else if( destFile.GetName() == aProjectName + "-rescue" )
|
||||||
|
destFile.SetName( aNewProjectName + "-rescue" );
|
||||||
|
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else if( ext == "net" )
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if( destFile.GetName() == aProjectName )
|
||||||
|
destFile.SetName( aNewProjectName );
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SEXPR::PARSER parser;
|
||||||
|
std::unique_ptr<SEXPR::SEXPR> sexpr( parser.ParseFromFile( aSrcFilePath ) );
|
||||||
|
|
||||||
|
traverseSEXPR( sexpr.get(), [&]( SEXPR::SEXPR* node )
|
||||||
|
{
|
||||||
|
if( node->IsList() && node->GetNumberOfChildren() > 1
|
||||||
|
&& node->GetChild( 0 )->IsSymbol()
|
||||||
|
&& node->GetChild( 0 )->GetSymbol() == "source" )
|
||||||
|
{
|
||||||
|
auto pathNode = dynamic_cast<SEXPR::SEXPR_STRING*>( node->GetChild( 1 ) );
|
||||||
|
wxString path( pathNode->m_value );
|
||||||
|
|
||||||
|
if( path == aProjectName + ".sch" )
|
||||||
|
path = aNewProjectName + ".sch";
|
||||||
|
else if( path == aProjectBasePath + "/" + aProjectName + ".sch" )
|
||||||
|
path = aNewProjectBasePath + "/" + aNewProjectName + ".sch";
|
||||||
|
else if( path.StartsWith( aProjectBasePath ) )
|
||||||
|
path.Replace( aProjectBasePath, aNewProjectBasePath, false );
|
||||||
|
|
||||||
|
pathNode->m_value = path;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
wxFile destNetList( destFile.GetFullPath(), wxFile::write );
|
||||||
|
|
||||||
|
if( destNetList.IsOpened() )
|
||||||
|
success = destNetList.Write( sexpr->AsString( 0 ) );
|
||||||
|
|
||||||
|
// wxFile dtor will close the file
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !success )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
if( !aErrors.empty() )
|
||||||
|
aErrors += "\n";
|
||||||
|
|
||||||
|
msg.Printf( _( "Cannot copy file \"%s\"." ), destFile.GetFullPath() );
|
||||||
|
aErrors += msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( destFile.GetName() == "sym-lib-table" )
|
||||||
|
{
|
||||||
|
SYMBOL_LIB_TABLE symbolLibTable;
|
||||||
|
symbolLibTable.Load( aSrcFilePath );
|
||||||
|
|
||||||
|
for( int i = 0; i < symbolLibTable.GetCount(); i++ )
|
||||||
|
{
|
||||||
|
LIB_TABLE_ROW& row = symbolLibTable.At( i );
|
||||||
|
wxString uri = row.GetFullURI();
|
||||||
|
|
||||||
|
uri.Replace( "/" + aProjectName + "-cache.lib", "/" + aNewProjectName + "-cache.lib" );
|
||||||
|
uri.Replace( "/" + aProjectName + "-rescue.lib", "/" + aNewProjectName + "-rescue.lib" );
|
||||||
|
uri.Replace( "/" + aProjectName + ".lib", "/" + aNewProjectName + ".lib" );
|
||||||
|
|
||||||
|
row.SetFullURI( uri );
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
symbolLibTable.Save( destFile.GetFullPath() );
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
if( !aErrors.empty() )
|
||||||
|
aErrors += "\n";
|
||||||
|
|
||||||
|
msg.Printf( _( "Cannot copy file \"%s\"." ), destFile.GetFullPath() );
|
||||||
|
aErrors += msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unexpected filetype for Eeschema::SaveFileAs()" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <pgm_base.h>
|
#include <pgm_base.h>
|
||||||
#include <gerbview.h>
|
#include <gerbview.h>
|
||||||
#include <gerbview_frame.h>
|
#include <gerbview_frame.h>
|
||||||
|
#include <gestfich.h>
|
||||||
|
#include "json11.hpp"
|
||||||
|
|
||||||
const wxChar* g_GerberPageSizeList[] =
|
const wxChar* g_GerberPageSizeList[] =
|
||||||
{
|
{
|
||||||
|
@ -92,6 +94,16 @@ static struct IFACE : public KIFACE_I
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function SaveFileAs
|
||||||
|
* Saving a file under a different name is delegated to the various KIFACEs because
|
||||||
|
* the project doesn't know the internal format of the various files (which may have
|
||||||
|
* paths in them that need updating).
|
||||||
|
*/
|
||||||
|
void SaveFileAs( const std::string& aProjectBasePath, const std::string& aProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors ) override;
|
||||||
|
|
||||||
} kiface( "gerbview", KIWAY::FACE_GERBVIEW );
|
} kiface( "gerbview", KIWAY::FACE_GERBVIEW );
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -130,3 +142,114 @@ void IFACE::OnKifaceEnd()
|
||||||
{
|
{
|
||||||
end_common();
|
end_common();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IFACE::SaveFileAs( const std::string& aProjectBasePath, const std::string& aProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors )
|
||||||
|
{
|
||||||
|
wxFileName destFile( aSrcFilePath );
|
||||||
|
wxString destPath = destFile.GetPath();
|
||||||
|
wxString ext = destFile.GetExt();
|
||||||
|
|
||||||
|
if( destPath.StartsWith( aProjectBasePath ) )
|
||||||
|
{
|
||||||
|
destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
|
||||||
|
destFile.SetPath( destPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ext == "gbr" )
|
||||||
|
{
|
||||||
|
wxString destFileName = destFile.GetName();
|
||||||
|
|
||||||
|
if( destFileName.StartsWith( aProjectName + "-" ) )
|
||||||
|
{
|
||||||
|
destFileName.Replace( aProjectName, aNewProjectName, false );
|
||||||
|
destFile.SetName( destFileName );
|
||||||
|
}
|
||||||
|
|
||||||
|
wxCopyFile( aSrcFilePath, destFile.GetFullPath() );
|
||||||
|
}
|
||||||
|
else if( ext == "gbrjob" )
|
||||||
|
{
|
||||||
|
if( destFile.GetName() == aProjectName + "-job" )
|
||||||
|
destFile.SetName( aNewProjectName + "-job" );
|
||||||
|
|
||||||
|
FILE_LINE_READER jobfileReader( aSrcFilePath );
|
||||||
|
|
||||||
|
char* line;
|
||||||
|
wxString data;
|
||||||
|
|
||||||
|
while( ( line = jobfileReader.ReadLine() ) )
|
||||||
|
data << line << '\n';
|
||||||
|
|
||||||
|
// detect the file format: old (deprecated) gerber format or official JSON format
|
||||||
|
if( !data.Contains( "{" ) )
|
||||||
|
{
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::string err;
|
||||||
|
json11::Json json = json11::Json::parse(TO_UTF8( data ), err );
|
||||||
|
|
||||||
|
if( err.empty() )
|
||||||
|
{
|
||||||
|
for( auto& entry : json[ "FilesAttributes" ].array_items() )
|
||||||
|
{
|
||||||
|
wxString path = entry[ "Path" ].string_value();
|
||||||
|
|
||||||
|
if( path.StartsWith( aProjectName + "-" ) )
|
||||||
|
{
|
||||||
|
path.Replace( aProjectName, aNewProjectName, false );
|
||||||
|
entry[ "Path" ].set_string_value( path.ToStdString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxFile destJobFile( destFile.GetFullPath(), wxFile::write );
|
||||||
|
|
||||||
|
if( destJobFile.IsOpened() )
|
||||||
|
success = destJobFile.Write( json.dump( 0 ) );
|
||||||
|
|
||||||
|
// wxFile dtor will close the file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !success )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
if( !aErrors.empty() )
|
||||||
|
aErrors += "\n";
|
||||||
|
|
||||||
|
msg.Printf( _( "Cannot copy file \"%s\"." ), destFile.GetFullPath() );
|
||||||
|
aErrors += msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( ext == "drl" )
|
||||||
|
{
|
||||||
|
wxString destFileName = destFile.GetName();
|
||||||
|
|
||||||
|
if( destFileName == aProjectName )
|
||||||
|
destFileName = aNewProjectName;
|
||||||
|
else if( destFileName.StartsWith( aProjectName + "-" ) )
|
||||||
|
destFileName.Replace( aProjectName, aNewProjectName, false );
|
||||||
|
|
||||||
|
destFile.SetName( destFileName );
|
||||||
|
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unexpected filetype for GerbView::SaveFileAs()" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,18 +50,27 @@ struct NullStruct
|
||||||
* Serialization
|
* Serialization
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void dump( NullStruct, string& out )
|
static void indent( int level, string& out )
|
||||||
|
{
|
||||||
|
for( int i = 0; i < level * 2; i++ )
|
||||||
|
out += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dump( NullStruct, int level, string& out )
|
||||||
{
|
{
|
||||||
out += "null";
|
out += "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dump( double value, string& out )
|
static void dump( double value, int level, string& out )
|
||||||
{
|
{
|
||||||
|
indent( level, out );
|
||||||
|
|
||||||
if( std::isfinite( value ) )
|
if( std::isfinite( value ) )
|
||||||
{
|
{
|
||||||
char buf[32];
|
char buf[32];
|
||||||
snprintf( buf, sizeof buf, "%.17g", value );
|
snprintf( buf, sizeof buf, "%.3f", value );
|
||||||
out += buf;
|
out += buf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -71,8 +80,10 @@ static void dump( double value, string& out )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dump( int value, string& out )
|
static void dump( int value, int level, string& out )
|
||||||
{
|
{
|
||||||
|
indent( level, out );
|
||||||
|
|
||||||
char buf[32];
|
char buf[32];
|
||||||
|
|
||||||
snprintf( buf, sizeof buf, "%d", value );
|
snprintf( buf, sizeof buf, "%d", value );
|
||||||
|
@ -80,14 +91,18 @@ static void dump( int value, string& out )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dump( bool value, string& out )
|
static void dump( bool value, int level, string& out )
|
||||||
{
|
{
|
||||||
|
indent( level, out );
|
||||||
|
|
||||||
out += value ? "true" : "false";
|
out += value ? "true" : "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dump( const string& value, string& out )
|
static void dump( const string& value, int level, string& out )
|
||||||
{
|
{
|
||||||
|
indent( level, out );
|
||||||
|
|
||||||
out += '"';
|
out += '"';
|
||||||
|
|
||||||
for( size_t i = 0; i < value.length(); i++ )
|
for( size_t i = 0; i < value.length(); i++ )
|
||||||
|
@ -150,49 +165,71 @@ static void dump( const string& value, string& out )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dump( const Json::array& values, string& out )
|
static void dump( const Json::array& values, int level, string& out )
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
out += "[";
|
indent( level, out );
|
||||||
|
out += "[\n";
|
||||||
|
|
||||||
for( const auto& value : values )
|
for( const auto& value : values )
|
||||||
{
|
{
|
||||||
if( !first )
|
if( !first )
|
||||||
out += ", ";
|
out += ",\n";
|
||||||
|
|
||||||
value.dump( out );
|
value.dump( level + 1, out );
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out += "\n";
|
||||||
|
indent( level, out );
|
||||||
out += "]";
|
out += "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dump( const Json::object& values, string& out )
|
static void dump( const Json::object& values, int level, string& out )
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
out += "{";
|
indent( level, out );
|
||||||
|
out += "{\n";
|
||||||
|
|
||||||
|
for( int i : { 1, 2 } )
|
||||||
|
{
|
||||||
for( const auto& kv : values )
|
for( const auto& kv : values )
|
||||||
{
|
{
|
||||||
if( !first )
|
if( i == 1 && kv.first != "Header" )
|
||||||
out += ", ";
|
continue;
|
||||||
|
else if( i == 2 && kv.first == "Header" )
|
||||||
|
continue;
|
||||||
|
|
||||||
dump( kv.first, out );
|
if( !first )
|
||||||
|
out += ",\n";
|
||||||
|
|
||||||
|
dump( kv.first, level + 1, out );
|
||||||
out += ": ";
|
out += ": ";
|
||||||
kv.second.dump( out );
|
if( kv.second.is_object() || kv.second.is_array() )
|
||||||
|
{
|
||||||
|
out += "\n";
|
||||||
|
kv.second.dump( level + 1, out );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kv.second.dump( 1, out );
|
||||||
|
}
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out += "\n";
|
||||||
|
indent( level, out );
|
||||||
out += "}";
|
out += "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Json::dump( string& out ) const
|
void Json::dump( int level, string& out ) const
|
||||||
{
|
{
|
||||||
m_ptr->dump( out );
|
m_ptr->dump( level, out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -226,8 +263,8 @@ protected:
|
||||||
return m_value < static_cast<const Value<tag, T>*>(other)->m_value;
|
return m_value < static_cast<const Value<tag, T>*>(other)->m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const T m_value;
|
T m_value;
|
||||||
void dump( string& out ) const override { json11::dump( m_value, out ); }
|
void dump( int level, string& out ) const override { json11::dump( m_value, level, out ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class JsonDouble final : public Value<Json::NUMBER, double>
|
class JsonDouble final : public Value<Json::NUMBER, double>
|
||||||
|
@ -276,6 +313,11 @@ class JsonString final : public Value<Json::STRING, string>
|
||||||
{
|
{
|
||||||
const string& string_value() const override { return m_value; }
|
const string& string_value() const override { return m_value; }
|
||||||
|
|
||||||
|
void set_string_value( string value ) const override
|
||||||
|
{
|
||||||
|
const_cast<JsonString*>( this )->m_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit JsonString( const string& value ) : Value( value ) {}
|
explicit JsonString( const string& value ) : Value( value ) {}
|
||||||
explicit JsonString( string&& value ) : Value( move( value ) ) {}
|
explicit JsonString( string&& value ) : Value( move( value ) ) {}
|
||||||
|
@ -436,6 +478,12 @@ const string& Json::string_value() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Json::set_string_value( string value ) const
|
||||||
|
{
|
||||||
|
m_ptr->set_string_value( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const vector<Json>& Json::array_items() const
|
const vector<Json>& Json::array_items() const
|
||||||
{
|
{
|
||||||
return m_ptr->array_items();
|
return m_ptr->array_items();
|
||||||
|
@ -483,6 +531,10 @@ const string& JsonValue::string_value() const
|
||||||
return statics().empty_string;
|
return statics().empty_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonValue::set_string_value( string value ) const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const vector<Json>& JsonValue::array_items() const
|
const vector<Json>& JsonValue::array_items() const
|
||||||
{
|
{
|
||||||
|
@ -1135,7 +1187,7 @@ bool Json::has_shape( const shape& types, string& err ) const
|
||||||
{
|
{
|
||||||
if( !is_object() )
|
if( !is_object() )
|
||||||
{
|
{
|
||||||
err = "expected JSON object, got " + dump();
|
err = "expected JSON object, got " + dump( 0 );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,7 +1195,7 @@ bool Json::has_shape( const shape& types, string& err ) const
|
||||||
{
|
{
|
||||||
if( (*this)[item.first].type() != item.second )
|
if( (*this)[item.first].type() != item.second )
|
||||||
{
|
{
|
||||||
err = "bad type for " + item.first + " in " + dump();
|
err = "bad type for " + item.first + " in " + dump( 0 );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,8 @@ public:
|
||||||
// Return the enclosed string if this is a string, "" otherwise.
|
// Return the enclosed string if this is a string, "" otherwise.
|
||||||
const std::string& string_value() const;
|
const std::string& string_value() const;
|
||||||
|
|
||||||
|
void set_string_value( std::string value ) const;
|
||||||
|
|
||||||
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
||||||
const array& array_items() const;
|
const array& array_items() const;
|
||||||
|
|
||||||
|
@ -161,13 +163,13 @@ public:
|
||||||
const Json& operator[]( const std::string& key ) const;
|
const Json& operator[]( const std::string& key ) const;
|
||||||
|
|
||||||
// Serialize.
|
// Serialize.
|
||||||
void dump( std::string& out ) const;
|
void dump( int level, std::string& out ) const;
|
||||||
|
|
||||||
std::string dump() const
|
std::string dump( int level ) const
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
|
|
||||||
dump( out );
|
dump( level, out );
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,11 +238,12 @@ protected:
|
||||||
virtual Json::Type type() const = 0;
|
virtual Json::Type type() const = 0;
|
||||||
virtual bool equals( const JsonValue* other ) const = 0;
|
virtual bool equals( const JsonValue* other ) const = 0;
|
||||||
virtual bool less( const JsonValue* other ) const = 0;
|
virtual bool less( const JsonValue* other ) const = 0;
|
||||||
virtual void dump( std::string& out ) const = 0;
|
virtual void dump( int level, std::string& out ) const = 0;
|
||||||
virtual double number_value() const;
|
virtual double number_value() const;
|
||||||
virtual int int_value() const;
|
virtual int int_value() const;
|
||||||
virtual bool bool_value() const;
|
virtual bool bool_value() const;
|
||||||
virtual const std::string& string_value() const;
|
virtual const std::string& string_value() const;
|
||||||
|
virtual void set_string_value( std::string value ) const;
|
||||||
virtual const Json::array& array_items() const;
|
virtual const Json::array& array_items() const;
|
||||||
virtual const Json& operator[]( size_t i ) const;
|
virtual const Json& operator[]( size_t i ) const;
|
||||||
virtual const Json::object& object_items() const;
|
virtual const Json::object& object_items() const;
|
||||||
|
|
|
@ -59,6 +59,14 @@ void OpenFile( const wxString& file );
|
||||||
void PrintFile( const wxString& file );
|
void PrintFile( const wxString& file );
|
||||||
bool CanPrintFile( const wxString& file );
|
bool CanPrintFile( const wxString& file );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function CopyFile
|
||||||
|
* @param aSrcPath
|
||||||
|
* @param aDestPath
|
||||||
|
* @param aErrors a string to *append* any errors to
|
||||||
|
*/
|
||||||
|
void CopyFile( const wxString& aSrcPath, const wxString& aDestPath, std::string& aErrors );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function EDA_FILE_SELECTOR
|
* Function EDA_FILE_SELECTOR
|
||||||
*
|
*
|
||||||
|
|
|
@ -211,6 +211,22 @@ struct KIFACE
|
||||||
VTBL_ENTRY wxWindow* CreateWindow( wxWindow* aParent, int aClassId,
|
VTBL_ENTRY wxWindow* CreateWindow( wxWindow* aParent, int aClassId,
|
||||||
KIWAY* aKIWAY, int aCtlBits = 0 ) = 0;
|
KIWAY* aKIWAY, int aCtlBits = 0 ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function SaveFileAs
|
||||||
|
* Saving a file under a different name is delegated to the various KIFACEs because
|
||||||
|
* the project doesn't know the internal format of the various files (which may have
|
||||||
|
* paths in them that need updating).
|
||||||
|
*/
|
||||||
|
VTBL_ENTRY void SaveFileAs( const std::string& srcProjectBasePath,
|
||||||
|
const std::string& srcProjectName,
|
||||||
|
const std::string& newProjectBasePath,
|
||||||
|
const std::string& newProjectName,
|
||||||
|
const std::string& srcFilePath,
|
||||||
|
std::string& aErrors )
|
||||||
|
{
|
||||||
|
// If a KIFACE owns files then it needs to implement this....
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function IfaceOrAddress
|
* Function IfaceOrAddress
|
||||||
* returns a pointer to the requested object. The safest way to use this
|
* returns a pointer to the requested object. The safest way to use this
|
||||||
|
|
|
@ -66,6 +66,9 @@ void KICAD_MANAGER_FRAME::ReCreateMenuBar()
|
||||||
fileMenu->AddItem( KICAD_MANAGER_ACTIONS::openProject, SELECTION_CONDITIONS::ShowAlways );
|
fileMenu->AddItem( KICAD_MANAGER_ACTIONS::openProject, SELECTION_CONDITIONS::ShowAlways );
|
||||||
fileMenu->AddMenu( openRecentMenu, SELECTION_CONDITIONS::ShowAlways );
|
fileMenu->AddMenu( openRecentMenu, SELECTION_CONDITIONS::ShowAlways );
|
||||||
|
|
||||||
|
fileMenu->AddSeparator();
|
||||||
|
fileMenu->AddItem( ACTIONS::saveAs, SELECTION_CONDITIONS::ShowAlways );
|
||||||
|
|
||||||
fileMenu->AddSeparator();
|
fileMenu->AddSeparator();
|
||||||
fileMenu->AddItem( ID_IMPORT_EAGLE_PROJECT,
|
fileMenu->AddItem( ID_IMPORT_EAGLE_PROJECT,
|
||||||
_( "Import EAGLE Project..." ),
|
_( "Import EAGLE Project..." ),
|
||||||
|
|
|
@ -51,7 +51,7 @@ TOOL_ACTION KICAD_MANAGER_ACTIONS::openProject( "kicad.Control.openProject",
|
||||||
AS_GLOBAL,
|
AS_GLOBAL,
|
||||||
MD_CTRL + 'O', LEGACY_HK_NAME( "Open Project" ),
|
MD_CTRL + 'O', LEGACY_HK_NAME( "Open Project" ),
|
||||||
_( "Open Project..." ), _( "Open an existing project" ),
|
_( "Open Project..." ), _( "Open an existing project" ),
|
||||||
open_project_xpm );
|
directory_xpm );
|
||||||
|
|
||||||
TOOL_ACTION KICAD_MANAGER_ACTIONS::editSchematic( "kicad.Control.editSchematic",
|
TOOL_ACTION KICAD_MANAGER_ACTIONS::editSchematic( "kicad.Control.editSchematic",
|
||||||
AS_GLOBAL,
|
AS_GLOBAL,
|
||||||
|
|
|
@ -313,6 +313,242 @@ int KICAD_MANAGER_CONTROL::OpenProject( const TOOL_EVENT& aEvent )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SAVE_AS_TRAVERSER : public wxDirTraverser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
KICAD_MANAGER_FRAME* m_frame;
|
||||||
|
|
||||||
|
wxString m_projectDirPath;
|
||||||
|
wxString m_projectName;
|
||||||
|
wxString m_newProjectDirPath;
|
||||||
|
wxString m_newProjectName;
|
||||||
|
|
||||||
|
wxFileName m_newProjectFile;
|
||||||
|
std::string m_errors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SAVE_AS_TRAVERSER( KICAD_MANAGER_FRAME* aFrame,
|
||||||
|
const std::string& aSrcProjectDirPath,
|
||||||
|
const std::string& aSrcProjectName,
|
||||||
|
const std::string& aNewProjectDirPath,
|
||||||
|
const std::string& aNewProjectName ) :
|
||||||
|
m_frame( aFrame ),
|
||||||
|
m_projectDirPath( aSrcProjectDirPath ),
|
||||||
|
m_projectName( aSrcProjectName ),
|
||||||
|
m_newProjectDirPath( aNewProjectDirPath ),
|
||||||
|
m_newProjectName( aNewProjectName )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual wxDirTraverseResult OnFile( const wxString& aSrcFilePath )
|
||||||
|
{
|
||||||
|
wxFileName destFile( aSrcFilePath );
|
||||||
|
wxString ext = destFile.GetExt();
|
||||||
|
bool atRoot = destFile.GetPath() == m_projectDirPath;
|
||||||
|
|
||||||
|
if( ext == "pro" )
|
||||||
|
{
|
||||||
|
wxString destPath = destFile.GetPath();
|
||||||
|
|
||||||
|
if( destPath.StartsWith( m_projectDirPath ) )
|
||||||
|
{
|
||||||
|
destPath.Replace( m_projectDirPath, m_newProjectDirPath, false );
|
||||||
|
destFile.SetPath( destPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( destFile.GetName() == m_projectName )
|
||||||
|
{
|
||||||
|
destFile.SetName( m_newProjectName );
|
||||||
|
|
||||||
|
if( atRoot )
|
||||||
|
m_newProjectFile = destFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently all paths in the settings file are relative, so we can just do a
|
||||||
|
// straight copy
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), m_errors );
|
||||||
|
}
|
||||||
|
else if( ext == "sch"
|
||||||
|
|| ext == "sch-bak"
|
||||||
|
|| ext == "sym"
|
||||||
|
|| ext == "lib"
|
||||||
|
|| ext == "net"
|
||||||
|
|| destFile.GetName() == "sym-lib-table" )
|
||||||
|
{
|
||||||
|
KIFACE* eeschema = m_frame->Kiway().KiFACE( KIWAY::FACE_SCH );
|
||||||
|
eeschema->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath,
|
||||||
|
m_newProjectName, aSrcFilePath, m_errors );
|
||||||
|
}
|
||||||
|
else if( ext == "kicad_pcb"
|
||||||
|
|| ext == "kicad_pcb-bak"
|
||||||
|
|| ext == "brd"
|
||||||
|
|| ext == "kicad_mod"
|
||||||
|
|| ext == "mod"
|
||||||
|
|| ext == "cmp"
|
||||||
|
|| destFile.GetName() == "fp-lib-table" )
|
||||||
|
{
|
||||||
|
KIFACE* pcbnew = m_frame->Kiway().KiFACE( KIWAY::FACE_PCB );
|
||||||
|
pcbnew->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath,
|
||||||
|
m_newProjectName, aSrcFilePath, m_errors );
|
||||||
|
}
|
||||||
|
else if( ext == "kicad_wks" )
|
||||||
|
{
|
||||||
|
KIFACE* pleditor = m_frame->Kiway().KiFACE( KIWAY::FACE_PL_EDITOR );
|
||||||
|
pleditor->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath,
|
||||||
|
m_newProjectName, aSrcFilePath, m_errors );
|
||||||
|
}
|
||||||
|
else if( ext == "gbr"
|
||||||
|
|| ext == "gbrjob"
|
||||||
|
|| ext == "drl" )
|
||||||
|
{
|
||||||
|
KIFACE* gerbview = m_frame->Kiway().KiFACE( KIWAY::FACE_GERBVIEW );
|
||||||
|
gerbview->SaveFileAs( m_projectDirPath, m_projectName, m_newProjectDirPath,
|
||||||
|
m_newProjectName, aSrcFilePath, m_errors );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Everything we don't recognize just gets a straight copy
|
||||||
|
wxString destPath = destFile.GetPath();
|
||||||
|
|
||||||
|
if( destPath.StartsWith( m_projectDirPath ) )
|
||||||
|
{
|
||||||
|
destPath.Replace( m_projectDirPath, m_newProjectDirPath, false );
|
||||||
|
destFile.SetPath( destPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( destFile.GetName() == m_projectName )
|
||||||
|
destFile.SetName( m_newProjectName );
|
||||||
|
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), m_errors );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: what about these?
|
||||||
|
MacrosFileExtension;
|
||||||
|
FootprintPlaceFileExtension;
|
||||||
|
KiCadFootprintLibPathExtension;
|
||||||
|
GedaPcbFootprintLibFileExtension;
|
||||||
|
EagleFootprintLibPathExtension;
|
||||||
|
KiCadLib3DShapesPathExtension;
|
||||||
|
SpecctraDsnFileExtension;
|
||||||
|
IpcD356FileExtension;
|
||||||
|
*/
|
||||||
|
|
||||||
|
return wxDIR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual wxDirTraverseResult OnDir( const wxString& dirPath )
|
||||||
|
{
|
||||||
|
wxFileName destDir( dirPath );
|
||||||
|
wxString destDirPath = destDir.GetPath(); // strips off last directory
|
||||||
|
|
||||||
|
if( destDirPath.StartsWith( m_projectDirPath ) )
|
||||||
|
{
|
||||||
|
destDirPath.Replace( m_projectDirPath, m_newProjectDirPath, false );
|
||||||
|
destDir.SetPath( destDirPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( destDir.GetName() == m_projectName )
|
||||||
|
destDir.SetName( m_newProjectName );
|
||||||
|
|
||||||
|
if( !wxMkdir( destDir.GetFullPath() ) )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
if( !m_errors.empty() )
|
||||||
|
m_errors += "\n";
|
||||||
|
|
||||||
|
msg.Printf( _( "Cannot copy file \"%s\"." ), destDir.GetFullPath() );
|
||||||
|
m_errors += msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wxDIR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString GetErrors() { return m_errors; }
|
||||||
|
|
||||||
|
wxFileName GetNewProjectFile() { return m_newProjectFile; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int KICAD_MANAGER_CONTROL::SaveProjectAs( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
wxFileName currentProjectFile( Prj().GetProjectFullName() );
|
||||||
|
wxString currentProjectDirPath = currentProjectFile.GetPath();
|
||||||
|
wxString currentProjectName = Prj().GetProjectName();
|
||||||
|
|
||||||
|
wxString default_dir = m_frame->GetMruPath();
|
||||||
|
|
||||||
|
if( default_dir == currentProjectDirPath
|
||||||
|
|| default_dir == currentProjectDirPath + wxFileName::GetPathSeparator() )
|
||||||
|
{
|
||||||
|
// Don't start within the current project
|
||||||
|
wxFileName default_dir_fn( default_dir );
|
||||||
|
default_dir_fn.RemoveLastDir();
|
||||||
|
default_dir = default_dir_fn.GetPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
wxFileDialog dlg( m_frame, _( "Save Project To" ), default_dir, wxEmptyString, wxEmptyString,
|
||||||
|
wxFD_SAVE );
|
||||||
|
|
||||||
|
if( dlg.ShowModal() == wxID_CANCEL )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wxFileName newProjectDir( dlg.GetPath() );
|
||||||
|
|
||||||
|
if( !newProjectDir.IsAbsolute() )
|
||||||
|
newProjectDir.MakeAbsolute();
|
||||||
|
|
||||||
|
if( wxDirExists( newProjectDir.GetFullPath() ) )
|
||||||
|
{
|
||||||
|
msg.Printf( _( "\"%s\" already exists." ), newProjectDir.GetFullPath() );
|
||||||
|
DisplayErrorMessage( m_frame, msg );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !wxMkdir( newProjectDir.GetFullPath() ) )
|
||||||
|
{
|
||||||
|
msg.Printf( _( "Directory \"%s\" could not be created.\n\n"
|
||||||
|
"Please make sure you have write permissions and try again." ),
|
||||||
|
newProjectDir.GetPath() );
|
||||||
|
DisplayErrorMessage( m_frame, msg );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !newProjectDir.IsDirWritable() )
|
||||||
|
{
|
||||||
|
msg.Printf( _( "Cannot write to folder \"%s\"." ), newProjectDir.GetFullPath() );
|
||||||
|
wxMessageDialog msgDlg( m_frame, msg, _( "Error!" ), wxICON_ERROR | wxOK | wxCENTER );
|
||||||
|
msgDlg.SetExtendedMessage( _( "Please check your access permissions to this folder "
|
||||||
|
"and try again." ) );
|
||||||
|
msgDlg.ShowModal();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxString& newProjectDirPath = newProjectDir.GetFullPath();
|
||||||
|
const wxString& newProjectName = newProjectDir.GetName();
|
||||||
|
wxDir currentProjectDir( currentProjectDirPath );
|
||||||
|
|
||||||
|
SAVE_AS_TRAVERSER traverser( m_frame,
|
||||||
|
currentProjectDirPath, currentProjectName,
|
||||||
|
newProjectDirPath, newProjectName );
|
||||||
|
|
||||||
|
currentProjectDir.Traverse( traverser );
|
||||||
|
|
||||||
|
if( !traverser.GetErrors().empty() )
|
||||||
|
DisplayErrorMessage( m_frame, traverser.GetErrors() );
|
||||||
|
|
||||||
|
if( traverser.GetNewProjectFile().FileExists() )
|
||||||
|
{
|
||||||
|
m_frame->CreateNewProject( traverser.GetNewProjectFile() );
|
||||||
|
m_frame->LoadProject( traverser.GetNewProjectFile() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int KICAD_MANAGER_CONTROL::Refresh( const TOOL_EVENT& aEvent )
|
int KICAD_MANAGER_CONTROL::Refresh( const TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
m_frame->RefreshProjectTree();
|
m_frame->RefreshProjectTree();
|
||||||
|
@ -492,6 +728,7 @@ void KICAD_MANAGER_CONTROL::setTransitions()
|
||||||
Go( &KICAD_MANAGER_CONTROL::NewProject, KICAD_MANAGER_ACTIONS::newProject.MakeEvent() );
|
Go( &KICAD_MANAGER_CONTROL::NewProject, KICAD_MANAGER_ACTIONS::newProject.MakeEvent() );
|
||||||
Go( &KICAD_MANAGER_CONTROL::NewFromTemplate, KICAD_MANAGER_ACTIONS::newFromTemplate.MakeEvent() );
|
Go( &KICAD_MANAGER_CONTROL::NewFromTemplate, KICAD_MANAGER_ACTIONS::newFromTemplate.MakeEvent() );
|
||||||
Go( &KICAD_MANAGER_CONTROL::OpenProject, KICAD_MANAGER_ACTIONS::openProject.MakeEvent() );
|
Go( &KICAD_MANAGER_CONTROL::OpenProject, KICAD_MANAGER_ACTIONS::openProject.MakeEvent() );
|
||||||
|
Go( &KICAD_MANAGER_CONTROL::SaveProjectAs, ACTIONS::saveAs.MakeEvent() );
|
||||||
|
|
||||||
Go( &KICAD_MANAGER_CONTROL::Refresh, ACTIONS::zoomRedraw.MakeEvent() );
|
Go( &KICAD_MANAGER_CONTROL::Refresh, ACTIONS::zoomRedraw.MakeEvent() );
|
||||||
Go( &KICAD_MANAGER_CONTROL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
|
Go( &KICAD_MANAGER_CONTROL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
|
||||||
|
|
|
@ -49,6 +49,7 @@ public:
|
||||||
int NewProject( const TOOL_EVENT& aEvent );
|
int NewProject( const TOOL_EVENT& aEvent );
|
||||||
int NewFromTemplate( const TOOL_EVENT& aEvent );
|
int NewFromTemplate( const TOOL_EVENT& aEvent );
|
||||||
int OpenProject( const TOOL_EVENT& aEvent );
|
int OpenProject( const TOOL_EVENT& aEvent );
|
||||||
|
int SaveProjectAs( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
int Refresh( const TOOL_EVENT& aEvent );
|
int Refresh( const TOOL_EVENT& aEvent );
|
||||||
int UpdateMenu( const TOOL_EVENT& aEvent );
|
int UpdateMenu( const TOOL_EVENT& aEvent );
|
||||||
|
|
|
@ -156,7 +156,7 @@ namespace SEXPR
|
||||||
result = "\n";
|
result = "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
result.append( aLevel* 4, ' ' );
|
result.append( aLevel * 2, ' ' );
|
||||||
aLevel++;
|
aLevel++;
|
||||||
result += "(";
|
result += "(";
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,16 @@ static struct IFACE : public KIFACE_I
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function SaveFileAs
|
||||||
|
* Saving a file under a different name is delegated to the various KIFACEs because
|
||||||
|
* the project doesn't know the internal format of the various files (which may have
|
||||||
|
* paths in them that need updating).
|
||||||
|
*/
|
||||||
|
void SaveFileAs( const std::string& aProjectBasePath, const std::string& aSrcProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors ) override;
|
||||||
|
|
||||||
} kiface( "pl_editor", KIWAY::FACE_PL_EDITOR );
|
} kiface( "pl_editor", KIWAY::FACE_PL_EDITOR );
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -124,3 +134,32 @@ void IFACE::OnKifaceEnd()
|
||||||
{
|
{
|
||||||
end_common();
|
end_common();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IFACE::SaveFileAs( const std::string& aProjectBasePath, const std::string& aSrcProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors )
|
||||||
|
{
|
||||||
|
wxFileName destFile( aSrcFilePath );
|
||||||
|
wxString destPath = destFile.GetPath();
|
||||||
|
wxString ext = destFile.GetExt();
|
||||||
|
|
||||||
|
if( destPath.StartsWith( aProjectBasePath ) )
|
||||||
|
{
|
||||||
|
destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
|
||||||
|
destFile.SetPath( destPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ext == "kicad_wks" )
|
||||||
|
{
|
||||||
|
if( destFile.GetName() == aSrcProjectName )
|
||||||
|
destFile.SetName( aNewProjectName );
|
||||||
|
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unexpected filetype for Pcbnew::SaveFileAs()" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,13 +40,10 @@
|
||||||
#include <macros.h>
|
#include <macros.h>
|
||||||
#include <pcb_edit_frame.h>
|
#include <pcb_edit_frame.h>
|
||||||
#include <eda_dde.h>
|
#include <eda_dde.h>
|
||||||
#include <wx/stdpaths.h>
|
|
||||||
#include <wx/file.h>
|
#include <wx/file.h>
|
||||||
#include <wx/snglinst.h>
|
#include <wx/snglinst.h>
|
||||||
#include <wx/dir.h>
|
|
||||||
#include <gestfich.h>
|
#include <gestfich.h>
|
||||||
#include <pcbnew.h>
|
#include <pcbnew.h>
|
||||||
#include <wildcards_and_files_ext.h>
|
|
||||||
#include <class_board.h>
|
#include <class_board.h>
|
||||||
#include <class_draw_panel_gal.h>
|
#include <class_draw_panel_gal.h>
|
||||||
#include <fp_lib_table.h>
|
#include <fp_lib_table.h>
|
||||||
|
@ -55,7 +52,6 @@
|
||||||
#include <footprint_wizard_frame.h>
|
#include <footprint_wizard_frame.h>
|
||||||
#include <footprint_preview_panel.h>
|
#include <footprint_preview_panel.h>
|
||||||
#include <footprint_info_impl.h>
|
#include <footprint_info_impl.h>
|
||||||
#include <gl_context_mgr.h>
|
|
||||||
#include <dialog_configure_paths.h>
|
#include <dialog_configure_paths.h>
|
||||||
#include "invoke_pcb_dialog.h"
|
#include "invoke_pcb_dialog.h"
|
||||||
#include "dialog_global_fp_lib_table_config.h"
|
#include "dialog_global_fp_lib_table_config.h"
|
||||||
|
@ -165,6 +161,16 @@ static struct IFACE : public KIFACE_I
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function SaveFileAs
|
||||||
|
* Saving a file under a different name is delegated to the various KIFACEs because
|
||||||
|
* the project doesn't know the internal format of the various files (which may have
|
||||||
|
* paths in them that need updating).
|
||||||
|
*/
|
||||||
|
void SaveFileAs( const std::string& aProjectBasePath, const std::string& aSrcProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors ) override;
|
||||||
|
|
||||||
} kiface( "pcbnew", KIWAY::FACE_PCB );
|
} kiface( "pcbnew", KIWAY::FACE_PCB );
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -378,3 +384,90 @@ void IFACE::OnKifaceEnd()
|
||||||
|
|
||||||
end_common();
|
end_common();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IFACE::SaveFileAs( const std::string& aProjectBasePath, const std::string& aSrcProjectName,
|
||||||
|
const std::string& aNewProjectBasePath, const std::string& aNewProjectName,
|
||||||
|
const std::string& aSrcFilePath, std::string& aErrors )
|
||||||
|
{
|
||||||
|
wxFileName destFile( aSrcFilePath );
|
||||||
|
wxString destPath = destFile.GetPath();
|
||||||
|
wxString ext = destFile.GetExt();
|
||||||
|
|
||||||
|
if( destPath.StartsWith( aProjectBasePath ) )
|
||||||
|
{
|
||||||
|
destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
|
||||||
|
destFile.SetPath( destPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ext == "kicad_pcb" || ext == "kicad_pcb-bak" )
|
||||||
|
{
|
||||||
|
if( destFile.GetName() == aSrcProjectName )
|
||||||
|
destFile.SetName( aNewProjectName );
|
||||||
|
|
||||||
|
// JEY TODO: are there filepaths in a PCB file that need updating?
|
||||||
|
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else if( ext == "brd" )
|
||||||
|
{
|
||||||
|
if( destFile.GetName() == aSrcProjectName )
|
||||||
|
destFile.SetName( aNewProjectName );
|
||||||
|
|
||||||
|
// JEY TODO: are there filepaths in a legacy PCB file that need updating?
|
||||||
|
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else if( ext == "mod" || ext == "kicad_mod" )
|
||||||
|
{
|
||||||
|
// Footprints are not project-specific. Keep their source names.
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else if( ext == "cmp" )
|
||||||
|
{
|
||||||
|
// JEY TODO
|
||||||
|
}
|
||||||
|
else if( ext == "rpt" )
|
||||||
|
{
|
||||||
|
// DRC must be the "gold standard". Since we can't gaurantee that there aren't
|
||||||
|
// any non-deterministic cases in the save-as algorithm, we don't want to certify
|
||||||
|
// the result with the source's DRC report. Therefore copy it under the old
|
||||||
|
// name.
|
||||||
|
CopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
|
||||||
|
}
|
||||||
|
else if( destFile.GetName() == "fp-lib-table" )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FP_LIB_TABLE fpLibTable;
|
||||||
|
fpLibTable.Load( aSrcFilePath );
|
||||||
|
|
||||||
|
for( int i = 0; i < fpLibTable.GetCount(); i++ )
|
||||||
|
{
|
||||||
|
LIB_TABLE_ROW& row = fpLibTable.At( i );
|
||||||
|
wxString uri = row.GetFullURI();
|
||||||
|
|
||||||
|
uri.Replace( "/" + aSrcProjectName + ".pretty", "/" + aNewProjectName + ".pretty" );
|
||||||
|
|
||||||
|
row.SetFullURI( uri );
|
||||||
|
}
|
||||||
|
|
||||||
|
fpLibTable.Save( destFile.GetFullPath() );
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
if( !aErrors.empty() )
|
||||||
|
aErrors += "\n";
|
||||||
|
|
||||||
|
msg.Printf( _( "Cannot copy file \"%s\"." ), destFile.GetFullPath() );
|
||||||
|
aErrors += msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unexpected filetype for Pcbnew::SaveFileAs()" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue