kicad-source/kicad/project_template.cpp
Seth Hillbrand 0b2d4d4879 Revise Copyright statement to align with TLF
Recommendation is to avoid using the year nomenclature as this
information is already encoded in the git repo.  Avoids needing to
repeatly update.

Also updates AUTHORS.txt from current repo with contributor names
2025-01-01 14:12:04 -08:00

380 lines
12 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 Brian Sidebotham <brian.sidebotham@gmail.com>
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <wx/bitmap.h>
#include <wx/dir.h>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/log.h>
#include <wildcards_and_files_ext.h>
#include "project_template.h"
#define SEP wxFileName::GetPathSeparator()
PROJECT_TEMPLATE::PROJECT_TEMPLATE( const wxString& aPath )
{
m_basePath = wxFileName::DirName( aPath );
m_metaPath = wxFileName::DirName( aPath + SEP + METADIR );
m_metaHtmlFile = wxFileName::FileName( aPath + SEP + METADIR + SEP + METAFILE_INFO_HTML );
m_metaIconFile = wxFileName::FileName( aPath + SEP + METADIR + SEP + METAFILE_ICON );
m_title = wxEmptyString;
// Test the project template requirements to make sure aPath is a valid template structure.
if( !wxFileName::DirExists( m_basePath.GetPath() ) )
{
// Error, the path doesn't exist!
m_title = _( "Could open the template path!" ) + wxS( " " ) + aPath;
}
else if( !wxFileName::DirExists( m_metaPath.GetPath() ) )
{
// Error, the meta information directory doesn't exist!
m_title = _( "Couldn't open the meta information directory for this template!" ) +
wxS( " " ) + m_metaPath.GetPath();
}
else if( !wxFileName::FileExists( m_metaHtmlFile.GetFullPath() ) )
{
// Error, the meta information directory doesn't contain the informational html file!
m_title = _( "Couldn't find the meta HTML information file for this template!" );
}
// Try to load an icon
if( !wxFileName::FileExists( m_metaIconFile.GetFullPath() ) )
m_metaIcon = &wxNullBitmap;
else
m_metaIcon = new wxBitmap( m_metaIconFile.GetFullPath(), wxBITMAP_TYPE_PNG );
}
class FILE_TRAVERSER : public wxDirTraverser
{
public:
FILE_TRAVERSER( std::vector<wxFileName>& files, const wxString exclude ) :
m_files( files ),
m_exclude( exclude )
{ }
virtual wxDirTraverseResult OnFile( const wxString& filename ) override
{
wxFileName fn( filename );
wxString path( fn.GetPathWithSep() );
bool exclude = fn.GetName().Contains( "fp-info-cache" )
|| fn.GetName().StartsWith( FILEEXT::AutoSaveFilePrefix )
|| fn.GetName().StartsWith( FILEEXT::LockFilePrefix );
if( !exclude )
m_files.emplace_back( wxFileName( filename ) );
if( path != m_oldPath )
{
const wxString gitfiles[] = { wxT( ".gitignore" ), wxT( ".gitattributes" ) };
for( const wxString& file : gitfiles )
{
if( wxFileExists( path + file ) )
m_files.emplace_back( wxFileName( path + file ) );
}
m_oldPath = path;
}
return wxDIR_CONTINUE;
}
virtual wxDirTraverseResult OnDir( const wxString& dirname ) override
{
wxDirTraverseResult result = wxDIR_IGNORE;
bool exclude = dirname.StartsWith( m_exclude ) || dirname.EndsWith( "-backups" );
if( !exclude )
{
m_files.emplace_back( wxFileName::DirName( dirname ) );
result = wxDIR_CONTINUE;
}
return result;
}
private:
std::vector<wxFileName>& m_files;
wxString m_exclude;
wxString m_oldPath;
};
std::vector<wxFileName> PROJECT_TEMPLATE::GetFileList()
{
std::vector<wxFileName> files;
FILE_TRAVERSER sink( files, m_metaPath.GetPath() );
wxDir dir( m_basePath.GetPath() );
dir.Traverse( sink, wxEmptyString, ( wxDIR_FILES | wxDIR_DIRS ) );
return files;
}
wxString PROJECT_TEMPLATE::GetPrjDirName()
{
return m_basePath.GetDirs()[ m_basePath.GetDirCount() - 1 ];
}
PROJECT_TEMPLATE::~PROJECT_TEMPLATE()
{
}
wxFileName PROJECT_TEMPLATE::GetHtmlFile()
{
return m_metaHtmlFile;
}
wxBitmap* PROJECT_TEMPLATE::GetIcon()
{
return m_metaIcon;
}
size_t PROJECT_TEMPLATE::GetDestinationFiles( const wxFileName& aNewProjectPath,
std::vector< wxFileName >& aDestFiles )
{
std::vector< wxFileName > srcFiles = GetFileList();
// Find the template file name base. this is the name of the .pro template file
wxString basename;
bool multipleProjectFilesFound = false;
for( wxFileName& file : srcFiles )
{
if( file.GetExt() == FILEEXT::ProjectFileExtension
|| file.GetExt() == FILEEXT::LegacyProjectFileExtension )
{
if( !basename.IsEmpty() && basename != file.GetName() )
multipleProjectFilesFound = true;
basename = file.GetName();
}
}
if( multipleProjectFilesFound )
basename = GetPrjDirName();
for( wxFileName& srcFile : srcFiles )
{
// Replace the template path
wxFileName destFile = srcFile;
// Replace the template filename with the project filename for the new project creation
wxString name = destFile.GetName();
name.Replace( basename, aNewProjectPath.GetName() );
destFile.SetName( name );
// Replace the template path with the project path.
wxString path = destFile.GetPathWithSep();
path.Replace( m_basePath.GetPathWithSep(), aNewProjectPath.GetPathWithSep() );
destFile.SetPath( path );
aDestFiles.push_back( destFile );
}
return aDestFiles.size();
}
bool PROJECT_TEMPLATE::CreateProject( wxFileName& aNewProjectPath, wxString* aErrorMsg )
{
// CreateProject copy the files from template to the new project folder and renames files
// which have the same name as the template .kicad_pro file
bool result = true;
std::vector<wxFileName> srcFiles = GetFileList();
// Find the template file name base. this is the name of the .kicad_pro (or .pro) template
// file
wxString basename;
bool multipleProjectFilesFound = false;
for( wxFileName& file : srcFiles )
{
if( file.GetExt() == FILEEXT::ProjectFileExtension
|| file.GetExt() == FILEEXT::LegacyProjectFileExtension )
{
if( !basename.IsEmpty() && basename != file.GetName() )
multipleProjectFilesFound = true;
basename = file.GetName();
}
}
if( multipleProjectFilesFound )
basename = GetPrjDirName();
for( wxFileName& srcFile : srcFiles )
{
// Replace the template path
wxFileName destFile = srcFile;
// Replace the template filename with the project filename for the new project creation
wxString currname = destFile.GetName();
if( destFile.GetExt() == FILEEXT::DrawingSheetFileExtension )
{
// Don't rename drawing sheet definitions; they're often shared
}
else if( destFile.GetName().EndsWith( "-cache" )
|| destFile.GetName().EndsWith( "-rescue" ) )
{
currname.Replace( basename, aNewProjectPath.GetName() );
}
else if( destFile.GetExt() == FILEEXT::LegacySymbolDocumentFileExtension
|| destFile.GetExt() == FILEEXT::LegacySymbolLibFileExtension
// Footprint libraries are directories not files, so GetExt() won't work
|| destFile.GetPath().EndsWith( '.' + FILEEXT::KiCadFootprintLibPathExtension ) )
{
// Don't rename project-specific libraries. This will break the library tables and
// cause broken links in the schematic/pcb.
}
else
{
currname.Replace( basename, aNewProjectPath.GetName() );
}
destFile.SetName( currname );
// Replace the template path with the project path for the new project creation
// but keep the sub directory name, if exists
wxString destpath = destFile.GetPathWithSep();
destpath.Replace( m_basePath.GetPathWithSep(), aNewProjectPath.GetPathWithSep() );
// Check to see if the path already exists, if not attempt to create it here.
if( !wxFileName::DirExists( destpath ) )
{
if( !wxFileName::Mkdir( destpath, 0777, wxPATH_MKDIR_FULL ) )
{
if( aErrorMsg )
{
if( !aErrorMsg->empty() )
*aErrorMsg += "\n";
wxString msg;
msg.Printf( _( "Cannot create folder '%s'." ), destpath );
*aErrorMsg += msg;
}
continue;
}
}
destFile.SetPath( destpath );
if( srcFile.FileExists() && !wxCopyFile( srcFile.GetFullPath(), destFile.GetFullPath() ) )
{
if( aErrorMsg )
{
if( !aErrorMsg->empty() )
*aErrorMsg += "\n";
wxString msg;
msg.Printf( _( "Cannot copy file '%s'." ), destFile.GetFullPath() );
*aErrorMsg += msg;
}
result = false;
}
}
return result;
}
wxString* PROJECT_TEMPLATE::GetTitle()
{
wxFFileInputStream input( GetHtmlFile().GetFullPath() );
wxString separator( wxT( "\x9" ) );
wxTextInputStream text( input, separator, wxConvUTF8 );
/* Open HTML file and get the text between the title tags */
if( m_title == wxEmptyString )
{
int start = 0;
int finish = 0;
bool done = false;
bool hasStart = false;
while( input.IsOk() && !input.Eof() && !done )
{
wxString line = text.ReadLine();
wxString upperline = line.Clone().Upper();
start = upperline.Find( wxT( "<TITLE>" ) );
finish = upperline.Find( wxT( "</TITLE>" ) );
int length = finish - start - 7;
// find the opening tag
if( start != wxNOT_FOUND )
{
if( finish != wxNOT_FOUND )
{
m_title = line( start + 7, length );
done = true;
}
else
{
m_title = line.Mid( start + 7 );
hasStart = true;
}
}
else
{
if( finish != wxNOT_FOUND )
{
m_title += line.SubString( 0, finish - 1 );
done = true;
}
else if( hasStart )
m_title += line;
}
}
// Remove line endings
m_title.Replace( wxT( "\r" ), wxT( "" ) );
m_title.Replace( wxT( "\n" ), wxT( "" ) );
m_title.Trim( false ); // Trim from left
m_title.Trim(); // Trim from right
}
return &m_title;
}