Prevent KiCad from overwriting existing files with template files.

Add tests to check for existing files before copying template files.

Split load project, create new project, create new project from template,
and load project from history command events into simple methods that
perform the correct actions before loading the project.  This greatly
simplifies the code.

Create a load project method that can be called from the event handlers
above.  This simplified a lot of duplicate code that existed in many of
these event handlers.

Remove the dummy "noname.pro" file creation which created required some
ugly code to prevent this file from being created unexpectedly.  Now
KiCad launches without setting a project file name when appropriate.

Fixes lp:1713951

https://bugs.launchpad.net/kicad/+bug/1713951
This commit is contained in:
Wayne Stambaugh 2017-08-31 11:15:20 -04:00
parent eec1366ead
commit 525e04ce75
8 changed files with 410 additions and 365 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2004-2015 Jean-Pierre Charras * Copyright (C) 2004-2015 Jean-Pierre Charras
* Copyright (C) 2004-2015 KiCad Developers, see change_log.txt for contributors. * Copyright (C) 2004-2017 KiCad Developers, see change_log.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -46,24 +46,12 @@
void KICAD_MANAGER_FRAME::OnFileHistory( wxCommandEvent& event ) void KICAD_MANAGER_FRAME::OnFileHistory( wxCommandEvent& event )
{ {
wxString fn = GetFileFromHistory( event.GetId(), wxFileName projFileName = GetFileFromHistory( event.GetId(), _( "KiCad project file" ),
_( "KiCad project file" ), &PgmTop().GetFileHistory() ); &PgmTop().GetFileHistory() );
if( !projFileName.FileExists() )
if( fn.size() )
{
// Any open KIFACE's must be closed before changing the project.
// We never want a KIWAY_PLAYER open on a KIWAY that isn't in the same project,
// and then break one project.
// Remember when saving files, the full path is build from the current project path
// User is prompted here to close those KIWAY_PLAYERs:
if( !Kiway.PlayersClose( false ) )
return; return;
// We can now set the new project filename and load this project LoadProject( projFileName );
SetProjectFileName( fn );
wxCommandEvent cmd( 0, wxID_ANY );
OnLoadProject( cmd );
}
} }
@ -80,7 +68,7 @@ void KICAD_MANAGER_FRAME::OnUnarchiveFiles( wxCommandEvent& event )
if( zipfiledlg.ShowModal() == wxID_CANCEL ) if( zipfiledlg.ShowModal() == wxID_CANCEL )
return; return;
wxString msg = wxString::Format( _("\nOpen '%s'\n" ), GetChars( zipfiledlg.GetPath() ) ); wxString msg = wxString::Format( _( "\nOpen '%s'\n" ), GetChars( zipfiledlg.GetPath() ) );
PrintMsg( msg ); PrintMsg( msg );
wxDirDialog dirDlg( this, _( "Target Directory" ), fn.GetPath(), wxDirDialog dirDlg( this, _( "Target Directory" ), fn.GetPath(),
@ -178,7 +166,8 @@ void KICAD_MANAGER_FRAME::OnArchiveFiles( wxCommandEvent& event )
if( !ostream.IsOk() ) // issue to create the file. Perhaps not writable dir if( !ostream.IsOk() ) // issue to create the file. Perhaps not writable dir
{ {
wxMessageBox( wxString::Format( _( "Unable to create zip archive file '%s'" ), zipfilename ) ); wxMessageBox( wxString::Format( _( "Unable to create zip archive file '%s'" ),
zipfilename ) );
return; return;
} }

View File

@ -98,8 +98,6 @@ bool PGM_KICAD::OnPgmInit()
SystemDirsAppend( &bases ); SystemDirsAppend( &bases );
// DBG( bases.Show( (std::string(__func__) + " bases").c_str() );)
for( unsigned i = 0; i < bases.GetCount(); ++i ) for( unsigned i = 0; i < bases.GetCount(); ++i )
{ {
wxFileName fn( bases[i], wxEmptyString ); wxFileName fn( bases[i], wxEmptyString );
@ -108,8 +106,6 @@ bool PGM_KICAD::OnPgmInit()
fn.AppendDir( wxT( "template" ) ); fn.AppendDir( wxT( "template" ) );
m_bm.m_search.AddPaths( fn.GetPath() ); m_bm.m_search.AddPaths( fn.GetPath() );
} }
//DBG( m_bm.m_search.Show( (std::string( __func__ ) + " SysSearch()").c_str() );)
} }
// Must be called before creating the main frame in order to // Must be called before creating the main frame in order to
@ -123,11 +119,12 @@ bool PGM_KICAD::OnPgmInit()
Kiway.SetTop( frame ); Kiway.SetTop( frame );
bool prjloaded = false; // true when the project is loaded wxString projToLoad;
if( App().argc > 1 ) if( App().argc > 1 )
frame->SetProjectFileName( App().argv[1] ); {
projToLoad = App().argv[1];
}
else if( GetFileHistory().GetCount() ) else if( GetFileHistory().GetCount() )
{ {
wxString last_pro = GetFileHistory().GetHistoryFile( 0 ); wxString last_pro = GetFileHistory().GetHistoryFile( 0 );
@ -135,37 +132,19 @@ bool PGM_KICAD::OnPgmInit()
if( !wxFileExists( last_pro ) ) if( !wxFileExists( last_pro ) )
{ {
GetFileHistory().RemoveFileFromHistory( 0 ); GetFileHistory().RemoveFileFromHistory( 0 );
wxFileName namelessProject( wxStandardPaths::Get().GetDocumentsDir(), NAMELESS_PROJECT,
ProjectFileExtension );
frame->SetProjectFileName( namelessProject.GetFullPath() );
} }
else else
{ {
// Try to open the last opened project, // Try to open the last opened project,
// if a project name is not given when starting Kicad // if a project name is not given when starting Kicad
frame->SetProjectFileName( last_pro ); projToLoad = last_pro;
wxCommandEvent cmd( 0, wxID_FILE1 );
frame->OnFileHistory( cmd );
prjloaded = true; // OnFileHistory() loads the project
} }
} }
else // there is no history
// Do not attempt to load a non-existent project file.
if( !projToLoad.empty() && wxFileExists( projToLoad ) )
{ {
wxFileName namelessProject( wxStandardPaths::Get().GetDocumentsDir(), NAMELESS_PROJECT, frame->LoadProject( projToLoad );
ProjectFileExtension );
frame->SetProjectFileName( namelessProject.GetFullPath() );
}
if( !prjloaded )
{
wxCommandEvent cmd( 0, wxID_ANY );
frame->OnLoadProject( cmd );
} }
frame->Show( true ); frame->Show( true );
@ -194,11 +173,9 @@ void PGM_KICAD::MacOpenFile( const wxString& aFileName )
KICAD_MANAGER_FRAME* frame = (KICAD_MANAGER_FRAME*) App().GetTopWindow(); KICAD_MANAGER_FRAME* frame = (KICAD_MANAGER_FRAME*) App().GetTopWindow();
frame->SetProjectFileName( aFileName ); if( !aFileName.empty && wxFileExists( aFileName ) )
frame->LoadProject( wxFileName( aFileName ) );
wxCommandEvent loadEvent( 0, wxID_ANY );
frame->OnLoadProject( loadEvent );
#endif #endif
} }

View File

@ -43,7 +43,7 @@ class LAUNCHER_PANEL;
class TREEPROJECTFILES; class TREEPROJECTFILES;
class TREE_PROJECT_FRAME; class TREE_PROJECT_FRAME;
// Enum to identify the type of files handled by Kicad manager // Identify the type of files handled by Kicad manager
// //
// When changing this enum please verify (and perhaps update) // When changing this enum please verify (and perhaps update)
// TREE_PROJECT_FRAME::GetFileExt(), // TREE_PROJECT_FRAME::GetFileExt(),
@ -131,8 +131,7 @@ enum id_kicad_frm {
/** /**
* Class KICAD_MANAGER_FRAME * The main KiCad project manager frame. It is not a KIWAY_PLAYER.
* is the main KiCad project manager frame. It is not a KIWAY_PLAYER.
*/ */
class KICAD_MANAGER_FRAME : public EDA_BASE_FRAME class KICAD_MANAGER_FRAME : public EDA_BASE_FRAME
{ {
@ -146,29 +145,26 @@ public:
void OnSize( wxSizeEvent& event ); void OnSize( wxSizeEvent& event );
/** /**
* Function OnChangeIconsOptions * Select the current icons options in menus (or toolbars) in Kicad
* Selects the current icons options in menus (or toolbars) in Kicad
* (the default for toolbars/menus is 26x26 pixels, and shows icons in menus). * (the default for toolbars/menus is 26x26 pixels, and shows icons in menus).
*/ */
void OnChangeIconsOptions( wxCommandEvent& event ) override; void OnChangeIconsOptions( wxCommandEvent& event ) override;
/** /**
* Function OnLoadProject * Load an exiting project (.pro) file.
* loads an exiting or creates a new project (.pro) file.
*/ */
void OnLoadProject( wxCommandEvent& event ); void OnLoadProject( wxCommandEvent& event );
/** /**
* Function OnCreateProjectFromTemplate
* Creates a new project folder, copy a template into this new folder. * Creates a new project folder, copy a template into this new folder.
* and open this new projrct as working project * and open this new projrct as working project
*/ */
void OnCreateProjectFromTemplate( wxCommandEvent& event ); void OnCreateProjectFromTemplate( wxCommandEvent& event );
void OnNewProject( wxCommandEvent& aEvent );
/** /**
* Function OnSaveProject * Save the project (.pro) file containing the top level configuration parameters.
* is the command event hendler to Save the project (.pro) file containing the top level
* configuration parameters.
*/ */
void OnSaveProject( wxCommandEvent& event ); void OnSaveProject( wxCommandEvent& event );
@ -199,21 +195,18 @@ public:
void RecreateBaseHToolbar(); void RecreateBaseHToolbar();
/** /**
* Function PrintMsg * Displays \a aText in the text panel.
* displays \a aText in the text panel.
* *
* @param aText The text to display. * @param aText The text to display.
*/ */
void PrintMsg( const wxString& aText ); void PrintMsg( const wxString& aText );
/** /**
* a minor helper function: * Prints the current working directory name and the projet name on the text panel.
* Prints the Current Working Dir name and the projet name on the text panel.
*/ */
void PrintPrjInfo(); void PrintPrjInfo();
/** /**
* a minor helper function:
* Erase the text panel. * Erase the text panel.
*/ */
void ClearMsg(); void ClearMsg();
@ -226,15 +219,17 @@ public:
void OnUpdatePreferredPdfBrowser( wxUpdateUIEvent& event ); void OnUpdatePreferredPdfBrowser( wxUpdateUIEvent& event );
void OnUpdateRequiresProject( wxUpdateUIEvent& event ); void OnUpdateRequiresProject( wxUpdateUIEvent& event );
void CreateNewProject( const wxString& aPrjFullFileName, bool aTemplateSelector ); void CreateNewProject( const wxFileName& aProjectFileName );
void LoadProject( const wxFileName& aProjectFileName );
void LoadSettings( wxConfigBase* aCfg ) override; void LoadSettings( wxConfigBase* aCfg ) override;
void SaveSettings( wxConfigBase* aCfg ) override; void SaveSettings( wxConfigBase* aCfg ) override;
/** /**
* Function Execute * Open another KiCad application and logs a message.
* opens another KiCad application and logs a message. *
* @param frame = owner frame. * @param frame = owner frame.
* @param execFile = name of the executable file. * @param execFile = name of the executable file.
* @param param = parameters to be passed to the executable. * @param param = parameters to be passed to the executable.
@ -249,7 +244,7 @@ public:
public: public:
TERMINATE_HANDLER( const wxString& appName ) : TERMINATE_HANDLER( const wxString& appName ) :
m_appName(appName) m_appName( appName )
{ {
} }
@ -260,7 +255,7 @@ public:
* Called by sending a event with id = ID_INIT_WATCHED_PATHS * Called by sending a event with id = ID_INIT_WATCHED_PATHS
* rebuild the list of wahtched paths * rebuild the list of wahtched paths
*/ */
void OnChangeWatchedPaths(wxCommandEvent& aEvent ); void OnChangeWatchedPaths( wxCommandEvent& aEvent );
void SetProjectFileName( const wxString& aFullProjectProFileName ); void SetProjectFileName( const wxString& aFullProjectProFileName );
@ -284,7 +279,6 @@ public:
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
private: private:
wxConfigBase* config() override; wxConfigBase* config() override;
const SEARCH_STACK& sys_search() override; const SEARCH_STACK& sys_search() override;

View File

@ -37,6 +37,7 @@
#include <wildcards_and_files_ext.h> #include <wildcards_and_files_ext.h>
#include <bitmaps.h> #include <bitmaps.h>
#include <executable_names.h> #include <executable_names.h>
#include <build_version.h>
#include "pgm_kicad.h" #include "pgm_kicad.h"
#include "tree_project_frame.h" #include "tree_project_frame.h"
@ -112,6 +113,8 @@ KICAD_MANAGER_FRAME::KICAD_MANAGER_FRAME( wxWindow* parent,
m_auimgr.GetPane( m_LeftWin ).BestSize(wxSize(m_leftWinWidth, -1) ); m_auimgr.GetPane( m_LeftWin ).BestSize(wxSize(m_leftWinWidth, -1) );
m_auimgr.Update(); m_auimgr.Update();
SetTitle( wxString( "KiCad " ) + GetBuildVersion() );
} }

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2009-2015 Wayne Stambaugh <stambaughw@verizon.net> * Copyright (C) 2009 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -44,7 +44,7 @@ BEGIN_EVENT_TABLE( KICAD_MANAGER_FRAME, EDA_BASE_FRAME )
EVT_CLOSE( KICAD_MANAGER_FRAME::OnCloseWindow ) EVT_CLOSE( KICAD_MANAGER_FRAME::OnCloseWindow )
// Toolbar events // Toolbar events
EVT_TOOL( ID_NEW_PROJECT, KICAD_MANAGER_FRAME::OnLoadProject ) EVT_TOOL( ID_NEW_PROJECT, KICAD_MANAGER_FRAME::OnNewProject )
EVT_TOOL( ID_NEW_PROJECT_FROM_TEMPLATE, KICAD_MANAGER_FRAME::OnCreateProjectFromTemplate ) EVT_TOOL( ID_NEW_PROJECT_FROM_TEMPLATE, KICAD_MANAGER_FRAME::OnCreateProjectFromTemplate )
EVT_TOOL( ID_LOAD_PROJECT, KICAD_MANAGER_FRAME::OnLoadProject ) EVT_TOOL( ID_LOAD_PROJECT, KICAD_MANAGER_FRAME::OnLoadProject )
@ -68,7 +68,8 @@ BEGIN_EVENT_TABLE( KICAD_MANAGER_FRAME, EDA_BASE_FRAME )
EVT_MENU( wxID_ABOUT, KICAD_MANAGER_FRAME::GetKicadAbout ) EVT_MENU( wxID_ABOUT, KICAD_MANAGER_FRAME::GetKicadAbout )
// Range menu events // Range menu events
EVT_MENU_RANGE( ID_LANGUAGE_CHOICE, ID_LANGUAGE_CHOICE_END, KICAD_MANAGER_FRAME::language_change ) EVT_MENU_RANGE( ID_LANGUAGE_CHOICE, ID_LANGUAGE_CHOICE_END,
KICAD_MANAGER_FRAME::language_change )
EVT_MENU_RANGE( wxID_FILE1, wxID_FILE9, KICAD_MANAGER_FRAME::OnFileHistory ) EVT_MENU_RANGE( wxID_FILE1, wxID_FILE9, KICAD_MANAGER_FRAME::OnFileHistory )

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2004-2015 Jean-Pierre Charras * Copyright (C) 2004-2015 Jean-Pierre Charras
* Copyright (C) 2004-2016 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2004-2017 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -31,6 +31,7 @@
#include <wx/dir.h> #include <wx/dir.h>
#include <wx/filename.h> #include <wx/filename.h>
#include <wx/stdpaths.h> #include <wx/stdpaths.h>
#include <wx/msgdlg.h>
#include <build_version.h> #include <build_version.h>
#include <config_params.h> #include <config_params.h>
@ -56,17 +57,180 @@
PARAM_CFG_ARRAY s_KicadManagerParams; PARAM_CFG_ARRAY s_KicadManagerParams;
void KICAD_MANAGER_FRAME::CreateNewProject( const wxString& aPrjFullFileName,
bool aTemplateSelector = false ) void KICAD_MANAGER_FRAME::LoadProject( const wxFileName& aProjectFileName )
{ {
wxFileName newProjectName = aPrjFullFileName; // The project file should be valid by the time we get here or something has gone wrong.
if( !aProjectFileName.Exists() )
return;
// Any open KIFACE's must be closed if they are not part of the new project.
// (We never want a KIWAY_PLAYER open on a KIWAY that isn't in the same project.)
// User is prompted here to close those KIWAY_PLAYERs:
if( !Kiway.PlayersClose( false ) )
return;
SetTitle( wxString( "KiCad " ) + GetBuildVersion() );
// Save the project file for the currently loaded project.
if( m_active_project )
Prj().ConfigLoad( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
m_active_project = true;
ClearMsg();
SetProjectFileName( aProjectFileName.GetFullPath() );
Prj().ConfigLoad( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
wxString title = GetTitle() + " " + aProjectFileName.GetFullPath();
if( !aProjectFileName.IsDirWritable() )
title += _( " [Read Only]" );
else
SetMruPath( Prj().GetProjectPath() ); // Only set MRU path if we have write access. Why?
SetTitle( title );
UpdateFileHistory( aProjectFileName.GetFullPath(), &PgmTop().GetFileHistory() );
m_LeftWin->ReCreateTreePrj();
// Rebuild the list of watched paths.
// however this is possible only when the main loop event handler is running,
// so we use it to run the rebuild function.
wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_INIT_WATCHED_PATHS );
wxPostEvent( this, cmd );
PrintPrjInfo();
}
void KICAD_MANAGER_FRAME::CreateNewProject( const wxFileName& aProjectFileName )
{
wxCHECK_RET( aProjectFileName.DirExists() && aProjectFileName.IsDirWritable(),
"Project folder must exist and be writable to create a new project." );
// Init project filename
SetProjectFileName( aProjectFileName.GetFullPath() );
// Ensure a "stub" for a schematic root sheet and a board exist.
// It will avoid messages from the schematic editor or the board editor to create a new file
// And forces the user to create main files under the right name for the project manager
wxFileName fn( aProjectFileName.GetFullPath() );
fn.SetExt( SchematicFileExtension );
// If a <project>.sch file does not exist, create a "stub" file ( minimal schematic file )
if( !fn.FileExists() )
{
wxFile file( fn.GetFullPath(), wxFile::write );
if( file.IsOpened() )
file.Write( wxT( "EESchema Schematic File Version 2\n"
"EELAYER 25 0\nEELAYER END\n$EndSCHEMATC\n" ) );
// wxFile dtor will close the file
}
// If a <project>.kicad_pcb or <project>.brd file does not exist,
// create a .kicad_pcb "stub" file
fn.SetExt( KiCadPcbFileExtension );
wxFileName leg_fn( fn );
leg_fn.SetExt( LegacyPcbFileExtension );
if( !fn.FileExists() && !leg_fn.FileExists() )
{
wxFile file( fn.GetFullPath(), wxFile::write );
if( file.IsOpened() )
file.Write( wxT( "(kicad_pcb (version 4) (host kicad \"dummy file\") )\n" ) );
// wxFile dtor will close the file
}
}
void KICAD_MANAGER_FRAME::OnLoadProject( wxCommandEvent& event )
{
wxString default_dir = GetMruPath();
wxFileDialog dlg( this, _( "Open Existing Project" ), default_dir, wxEmptyString,
ProjectFileWildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
if( dlg.ShowModal() == wxID_CANCEL )
return;
wxFileName pro( dlg.GetPath() );
pro.SetExt( ProjectFileExtension ); // enforce extension
if( !pro.IsAbsolute() )
pro.MakeAbsolute();
if( !pro.FileExists() )
return;
LoadProject( pro );
}
void KICAD_MANAGER_FRAME::OnNewProject( wxCommandEvent& aEvent )
{
wxString default_dir = GetMruPath();
wxFileDialog dlg( this, _( "Create New Project" ), default_dir, wxEmptyString,
ProjectFileWildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if( dlg.ShowModal() == wxID_CANCEL )
return;
wxFileName pro( dlg.GetPath() );
pro.SetExt( ProjectFileExtension ); // enforce extension
if( !pro.IsAbsolute() )
pro.MakeAbsolute();
// Check if the project directory is empty.
wxDir directory( pro.GetPath() );
if( directory.HasFiles() )
{
wxString msg = _( "The selected directory is not empty. We recommend you "
"create projects in their own clean directory.\n\nDo you "
"want to create a new empty directory for the project?" );
if( IsOK( this, msg ) )
{
// Append a new directory with the same name of the project file
// and try to create it
pro.AppendDir( pro.GetName() );
if( !wxMkdir( pro.GetPath() ) )
// There was a problem, undo
pro.RemoveLastDir();
}
}
CreateNewProject( pro.GetFullPath() );
LoadProject( pro );
}
void KICAD_MANAGER_FRAME::OnCreateProjectFromTemplate( wxCommandEvent& event )
{
wxString default_dir = wxFileName( Prj().GetProjectFullName() ).GetPathWithSep();
wxString title = _( "New Project Folder" );
wxDirDialog dlg( this, title, default_dir );
if( dlg.ShowModal() == wxID_CANCEL )
return;
// Builds the project .pro filename, from the new project folder name
wxFileName fn;
fn.AssignDir( dlg.GetPath() );
fn.SetName( dlg.GetPath().AfterLast( SEP() ) );
fn.SetExt( wxT( "pro" ) );
wxChar sep[2] = { SEP(), 0 }; // nul terminated separator wxChar string. wxChar sep[2] = { SEP(), 0 }; // nul terminated separator wxChar string.
ClearMsg(); ClearMsg();
// If we are creating a project from a template, make sure the template directory is sane
if( aTemplateSelector )
{
DIALOG_TEMPLATE_SELECTOR* ps = new DIALOG_TEMPLATE_SELECTOR( this ); DIALOG_TEMPLATE_SELECTOR* ps = new DIALOG_TEMPLATE_SELECTOR( this );
wxFileName templatePath; wxFileName templatePath;
@ -96,8 +260,8 @@ void KICAD_MANAGER_FRAME::CreateNewProject( const wxString& aPrjFullFileName,
if( !wxDirExists( templatePath.GetFullPath() ) ) if( !wxDirExists( templatePath.GetFullPath() ) )
{ {
templatePath = wxPathOnly( wxStandardPaths::Get().GetExecutablePath() ) + templatePath = wxPathOnly( wxStandardPaths::Get().GetExecutablePath() ) + sep +
sep + wxT( ".." ) + sep + wxT( "share" ) + sep + wxT( "template" ) + sep; wxT( ".." ) + sep + wxT( "share" ) + sep + wxT( "template" ) + sep;
} }
} }
#else #else
@ -108,8 +272,8 @@ void KICAD_MANAGER_FRAME::CreateNewProject( const wxString& aPrjFullFileName,
ps->AddTemplatesPage( _( "System Templates" ), templatePath ); ps->AddTemplatesPage( _( "System Templates" ), templatePath );
// Add a new tab for user templates // Add a new tab for user templates
wxFileName userPath = wxStandardPaths::Get().GetDocumentsDir() + wxFileName userPath = wxStandardPaths::Get().GetDocumentsDir() + sep + wxT( "kicad" ) +
sep + wxT( "kicad" ) + sep + wxT( "template" ) + sep; sep + wxT( "template" ) + sep;
ps->AddTemplatesPage( _( "User Templates" ), userPath ); ps->AddTemplatesPage( _( "User Templates" ), userPath );
@ -129,254 +293,92 @@ void KICAD_MANAGER_FRAME::CreateNewProject( const wxString& aPrjFullFileName,
} }
// Show the project template selector dialog // Show the project template selector dialog
int result = ps->ShowModal(); if( ps->ShowModal() != wxID_OK )
return;
if( ( result != wxID_OK ) || ( ps->GetSelectedTemplate() == NULL ) )
{
if( ps->GetSelectedTemplate() == NULL ) if( ps->GetSelectedTemplate() == NULL )
{ {
wxMessageBox( _( "No project template was selected. Cannot generate new " wxMessageBox( _( "No project template was selected. Cannot generate new project." ),
"project." ),
_( "Error" ), _( "Error" ),
wxOK | wxICON_ERROR, wxOK | wxICON_ERROR,
this ); this );
return;
} }
}
else // Make sure the user has write permissions to the base path.
wxFileName prjPath = fn;
while( !prjPath.DirExists() )
prjPath.RemoveLastDir();
if( !prjPath.IsDirWritable() )
{ {
wxString msg;
msg.Printf( _( "Cannot write to folder '%s'." ), prjPath.GetPath() );
wxMessageDialog msgDlg( this, msg, _( "Error!" ), wxICON_ERROR | wxOK | wxCENTER );
msgDlg.SetExtendedMessage( _( "Plese check your access permissions to this folder "
"and try again." ) );
msgDlg.ShowModal();
return;
}
// Make sure we are not overwriting anything in the destination folder.
std::vector< wxFileName > destFiles;
if( ps->GetSelectedTemplate()->GetDestinationFiles( fn, destFiles ) )
{
std::vector< wxFileName > overwrittenFiles;
for( auto file : destFiles )
{
if( file.FileExists() )
overwrittenFiles.push_back( file );
}
if( !overwrittenFiles.empty() )
{
wxString extendedMsg = _( "Overwriting files:" ) + "\n";
for( auto file : overwrittenFiles )
{
extendedMsg += "\n" + file.GetFullName();
}
wxMessageDialog owDlg( this,
_( "Are you sure you want to overwrite files in "
"the destination folder?" ),
_( "Warning!" ),
wxYES_NO | wxICON_QUESTION | wxCENTER );
owDlg.SetExtendedMessage( extendedMsg );
owDlg.SetYesNoLabels( _( "Overwrite" ), _( "Do Not Overwrite" ) );
if( owDlg.ShowModal() == wxID_NO )
return;
}
}
wxString errorMsg;
// The selected template widget contains the template we're attempting to use to // The selected template widget contains the template we're attempting to use to
// create a project // create a project
if( !ps->GetSelectedTemplate()->CreateProject( newProjectName ) ) if( !ps->GetSelectedTemplate()->CreateProject( fn, &errorMsg ) )
{ {
wxMessageBox( _( "Problem whilst creating new project from template!" ), wxMessageDialog createDlg( this,
_( "A problem occurred creating new project from template!" ),
_( "Template Error" ), _( "Template Error" ),
wxOK | wxICON_ERROR, wxOK | wxICON_ERROR );
this );
}
}
}
// Init project filename if( !errorMsg.empty() )
SetProjectFileName( newProjectName.GetFullPath() ); createDlg.SetExtendedMessage( errorMsg );
// Write settings to project file createDlg.ShowModal();
// was: wxGetApp().WriteProjectConfig( aPrjFullFileName, GeneralGroupName, s_KicadManagerParams );
Prj().ConfigSave( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
// Ensure a "stub" for a schematic root sheet and a board exist.
// It will avoid messages from the schematic editor or the board editor to create a new file
// And forces the user to create main files under the right name for the project manager
wxFileName fn( newProjectName.GetFullPath() );
fn.SetExt( SchematicFileExtension );
// If a <project>.sch file does not exist, create a "stub" file
// ( minimal schematic file )
if( !fn.FileExists() )
{
wxFile file( fn.GetFullPath(), wxFile::write );
if( file.IsOpened() )
file.Write( wxT( "EESchema Schematic File Version 2\n"
"EELAYER 25 0\nEELAYER END\n$EndSCHEMATC\n" ) );
// wxFile dtor will close the file
}
// If a <project>.kicad_pcb or <project>.brd file does not exist,
// create a .kicad_pcb "stub" file
fn.SetExt( KiCadPcbFileExtension );
wxFileName leg_fn( fn );
leg_fn.SetExt( LegacyPcbFileExtension );
if( !fn.FileExists() && !leg_fn.FileExists() )
{
wxFile file( fn.GetFullPath(), wxFile::write );
if( file.IsOpened() )
file.Write( wxT( "(kicad_pcb (version 4) (host kicad \"dummy file\") )\n" ) );
// wxFile dtor will close the file
}
// Enable the toolbar and menubar buttons and clear the help text.
m_active_project = true;
m_MessagesBox->Clear();
}
void KICAD_MANAGER_FRAME::OnLoadProject( wxCommandEvent& event )
{
// Any open KIFACE's must be closed if they are not part of the new project.
// (We never want a KIWAY_PLAYER open on a KIWAY that isn't in the same project.)
// User is prompted here to close those KIWAY_PLAYERs:
if( !Kiway.PlayersClose( false ) )
return;
// evt_id can be one of:
// ID_NEW_PROJECT, ID_NEW_PROJECT_FROM_TEMPLATE, ID_LOAD_PROJECT, and
// wxID_ANY from 3 different places.
int evt_id = event.GetId();
wxString title;
ClearMsg();
bool newProject = ( evt_id == ID_NEW_PROJECT ) ||
( evt_id == ID_NEW_PROJECT_FROM_TEMPLATE );
if( evt_id != wxID_ANY )
{
int style;
if( newProject )
{
title = _( "Create New Project" );
style = wxFD_SAVE | wxFD_OVERWRITE_PROMPT;
}
else
{
title = _( "Open Existing Project" );
style = wxFD_OPEN | wxFD_FILE_MUST_EXIST;
}
wxString default_dir = GetMruPath();
wxFileDialog dlg( this, title, default_dir, wxEmptyString,
ProjectFileWildcard, style );
if( dlg.ShowModal() == wxID_CANCEL )
return;
//DBG( printf( "%s: wxFileDialog::GetPath=%s\n", __func__, TO_UTF8( dlg.GetPath() ) );)
wxFileName pro( dlg.GetPath() );
pro.SetExt( ProjectFileExtension ); // enforce extension
if( !pro.IsAbsolute() )
pro.MakeAbsolute();
if( newProject )
{
// Check if the project directory is empty
wxDir directory( pro.GetPath() );
if( directory.HasFiles() )
{
wxString msg = _( "The selected directory is not empty. We recommend you "
"create projects in their own clean directory.\n\nDo you "
"want to create a new empty directory for the project?" );
if( IsOK( this, msg ) )
{
// Append a new directory with the same name of the project file
// and try to create it
pro.AppendDir( pro.GetName() );
if( !wxMkdir( pro.GetPath() ) )
// There was a problem, undo
pro.RemoveLastDir();
}
}
if( evt_id == ID_NEW_PROJECT )
{
CreateNewProject( pro.GetFullPath() );
}
else if( evt_id == ID_NEW_PROJECT_FROM_TEMPLATE )
{
// Launch the template selector dialog
CreateNewProject( pro.GetFullPath(), true );
}
}
SetProjectFileName( pro.GetFullPath() );
}
wxString prj_filename = GetProjectFileName();
wxString nameless_prj = NAMELESS_PROJECT wxT( ".pro" );
wxLogDebug( wxT( "%s: %s" ),
GetChars( wxFileName( prj_filename ).GetFullName() ),
GetChars( nameless_prj ) );
// Check if project file exists and if it is not noname.pro
if( !wxFileExists( prj_filename )
&& !wxFileName( prj_filename ).GetFullName().IsSameAs( nameless_prj ) )
{
wxString msg = wxString::Format( _(
"KiCad project file '%s' not found" ),
GetChars( prj_filename ) );
DisplayError( this, msg );
return; return;
} }
// Either this is the first time kicad has been run or one of the projects in the CreateNewProject( fn.GetFullPath() );
// history list is no longer valid. This prevents kicad from automatically creating LoadProject( fn );
// a noname.pro file in the same folder as the kicad binary.
if( wxFileName( prj_filename ).GetFullName().IsSameAs( nameless_prj ) && !newProject )
{
m_active_project = false;
m_MessagesBox->SetValue( _( "To proceed, you can use the File menu to start a new project." ) );
return;
}
else
{
m_active_project = true;
m_MessagesBox->Clear();
}
Prj().ConfigLoad( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
title = L"KiCad \u2014 " + prj_filename;
if( !wxFileName( prj_filename ).IsDirWritable() )
title += _( " [Read Only]" );
else
SetMruPath( Prj().GetProjectPath() ); // Only set MRU path if we have write access.
SetTitle( title );
if( !prj_filename.IsSameAs( nameless_prj ) )
UpdateFileHistory( prj_filename, &PgmTop().GetFileHistory() );
m_LeftWin->ReCreateTreePrj();
// Rebuild the list of watched paths.
// however this is possible only when the main loop event handler is running,
// so we use it to run the rebuild function.
wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_INIT_WATCHED_PATHS );
wxPostEvent( this, cmd );
PrintPrjInfo();
}
/* Creates a new project folder, copy a template into this new folder.
* and open this new project as working project
*/
void KICAD_MANAGER_FRAME::OnCreateProjectFromTemplate( wxCommandEvent& event )
{
wxString default_dir = wxFileName( Prj().GetProjectFullName() ).GetPathWithSep();
wxString title = _("New Project Folder");
wxDirDialog dlg( this, title, default_dir );
if( dlg.ShowModal() == wxID_CANCEL )
return;
// Builds the project .pro filename, from the new project folder name
wxFileName fn;
fn.AssignDir( dlg.GetPath() );
fn.SetName( dlg.GetPath().AfterLast( SEP() ) );
fn.SetExt( wxT( "pro" ) );
// Launch the template selector dialog, and copy files
CreateNewProject( fn.GetFullPath(), true );
// Initialize the project
event.SetId( wxID_ANY );
OnLoadProject( event );
} }
@ -385,8 +387,6 @@ void KICAD_MANAGER_FRAME::OnSaveProject( wxCommandEvent& event )
if( !wxIsWritable( GetProjectFileName() ) ) if( !wxIsWritable( GetProjectFileName() ) )
return; return;
// was: wxGetApp().WriteProjectConfig( m_ProjectFileName.GetFullPath(),
// GeneralGroupName, s_KicadManagerParams );
Prj().ConfigSave( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams ); Prj().ConfigSave( PgmTop().SysSearch(), GeneralGroupName, s_KicadManagerParams );
} }

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 Brian Sidebotham <brian.sidebotham@gmail.com> * Copyright (C) 2012 Brian Sidebotham <brian.sidebotham@gmail.com>
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -27,6 +27,7 @@
#include <wx/dir.h> #include <wx/dir.h>
#include <wx/txtstrm.h> #include <wx/txtstrm.h>
#include <wx/wfstream.h> #include <wx/wfstream.h>
#include <wx/log.h>
#include "project_template.h" #include "project_template.h"
@ -44,29 +45,29 @@ PROJECT_TEMPLATE::PROJECT_TEMPLATE( const wxString& aPath )
title = wxEmptyString; title = wxEmptyString;
// Test the project template requirements to make sure aPath is a valid // Test the project template requirements to make sure aPath is a valid template structure.
// template structure
if( !wxFileName::DirExists( templateBasePath.GetPath() ) ) if( !wxFileName::DirExists( templateBasePath.GetPath() ) )
{ {
// Error, the path doesn't exist! // Error, the path doesn't exist!
title = wxT( "Could open the template path! " + aPath ); title = _( "Could open the template path! " ) + aPath;
} }
else if( !wxFileName::DirExists( templateMetaPath.GetPath() ) ) else if( !wxFileName::DirExists( templateMetaPath.GetPath() ) )
{ {
// Error, the meta information directory doesn't exist! // Error, the meta information directory doesn't exist!
title = wxT( "Couldn't open the meta information directory for this template! " + title = _( "Couldn't open the meta information directory for this template! " ) +
templateMetaPath.GetPath() ); templateMetaPath.GetPath();
} }
else if( !wxFileName::FileExists( templateMetaHtmlFile.GetFullPath() ) ) else if( !wxFileName::FileExists( templateMetaHtmlFile.GetFullPath() ) )
{ {
// Error, the meta information directory doesn't contain the informational html file! // Error, the meta information directory doesn't contain the informational html file!
title = wxT( "Cound't find the meta html information file for this template!" ); title = _( "Cound't find the meta HTML information file for this template!" );
} }
// Try to load an icon // Try to load an icon
metaIcon = new wxBitmap( templateMetaIconFile.GetFullPath(), wxBITMAP_TYPE_PNG ); metaIcon = new wxBitmap( templateMetaIconFile.GetFullPath(), wxBITMAP_TYPE_PNG );
} }
std::vector<wxFileName> PROJECT_TEMPLATE::GetFileList() std::vector<wxFileName> PROJECT_TEMPLATE::GetFileList()
{ {
std::vector<wxFileName> files; std::vector<wxFileName> files;
@ -83,7 +84,7 @@ std::vector<wxFileName> PROJECT_TEMPLATE::GetFileList()
// Files that are in the meta directory must not be included // Files that are in the meta directory must not be included
if( !p.GetPath().StartsWith( templateMetaPath.GetPath() ) ) if( !p.GetPath().StartsWith( templateMetaPath.GetPath() ) )
files.push_back(allfiles[i]); files.push_back( allfiles[i] );
} }
return files; return files;
@ -114,7 +115,45 @@ wxBitmap* PROJECT_TEMPLATE::GetIcon()
} }
bool PROJECT_TEMPLATE::CreateProject( wxFileName& aNewProjectPath ) 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;
for( auto file : srcFiles )
{
if( file.GetExt() == wxT( "pro" ) )
{
basename = file.GetName();
break;
}
}
for( auto file : srcFiles )
{
wxFileName destFile = file;
// 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( templateBasePath.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 // CreateProject copy the files from template to the new project folder
// and rename files which have the same name as the template .pro file // and rename files which have the same name as the template .pro file
@ -122,8 +161,9 @@ bool PROJECT_TEMPLATE::CreateProject( wxFileName& aNewProjectPath )
std::vector<wxFileName> srcFiles = GetFileList(); std::vector<wxFileName> srcFiles = GetFileList();
// Find the template file name base. this is the name of the .pro templte file // Find the template file name base. this is the name of the .pro template file
wxString basename; wxString basename;
for( size_t i=0; i < srcFiles.size(); i++ ) for( size_t i=0; i < srcFiles.size(); i++ )
{ {
if( srcFiles[i].GetExt() == wxT( "pro" ) ) if( srcFiles[i].GetExt() == wxT( "pro" ) )
@ -152,7 +192,23 @@ bool PROJECT_TEMPLATE::CreateProject( wxFileName& aNewProjectPath )
// about error checking, if the path isn't created the file copy will fail anyway // about error checking, if the path isn't created the file copy will fail anyway
if( !wxFileName::DirExists( destpath ) ) if( !wxFileName::DirExists( destpath ) )
wxFileName::Mkdir( destpath, 0777, wxPATH_MKDIR_FULL ); {
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;
}
}
destination.SetPath( destpath ); destination.SetPath( destpath );
@ -161,8 +217,19 @@ bool PROJECT_TEMPLATE::CreateProject( wxFileName& aNewProjectPath )
if( !wxCopyFile( srcFile, dstFile ) ) if( !wxCopyFile( srcFile, dstFile ) )
{ {
if( aErrorMsg )
{
if( !aErrorMsg->empty() )
*aErrorMsg += "\n";
wxString msg;
msg.Printf( _( "Cannot copy file '%s'." ), dstFile );
*aErrorMsg += msg;
}
result = false; result = false;
break;
} }
} }

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 Brian Sidebotham <brian.sidebotham@gmail.com> * Copyright (C) 2012 Brian Sidebotham <brian.sidebotham@gmail.com>
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -112,38 +112,36 @@
class wxBitmap; class wxBitmap;
class wxFileName;
/** /**
* @brief A directory which contains information about the project template and does not get * A directory which contains information about the project template and does not get
* copied. This define is the default filename for this directory * copied. This define is the default filename for this directory
* *
*/ */
#define METADIR wxT("meta") #define METADIR wxT( "meta" )
/** /**
* @brief A required html formatted file which contains information about the project template. * A required html formatted file which contains information about the project template.
* This define is the default filename for this file * This define is the default filename for this file
* *
*/ */
#define METAFILE_INFO_HTML wxT("info.html") #define METAFILE_INFO_HTML wxT( "info.html" )
/** /**
* @brief An optional png icon, exactly 64px x 64px which is used in the template selector if * An optional png icon, exactly 64px x 64px which is used in the template selector if
* present. This define is the default filename for this file * present. This define is the default filename for this file
* *
*/ */
#define METAFILE_ICON wxT("icon.png") #define METAFILE_ICON wxT( "icon.png" )
/** /**
* @brief A class which provides project template functionality. * A class which provides project template functionality.
*
*
*
*/ */
class PROJECT_TEMPLATE { class PROJECT_TEMPLATE
private: {
protected: protected:
wxFileName templateBasePath; wxFileName templateBasePath;
wxFileName templateMetaPath; wxFileName templateMetaPath;
@ -155,7 +153,7 @@ protected:
public: public:
/** /**
* @brief Create a new project instance from \a aPath. \a aPath should be a directory that * Create a new project instance from \a aPath. \a aPath should be a directory that
* conforms to the project template requirements * conforms to the project template requirements
* *
* @param aPath Should be a directory containing the template * @param aPath Should be a directory containing the template
@ -163,45 +161,61 @@ public:
PROJECT_TEMPLATE( const wxString& aPath ); PROJECT_TEMPLATE( const wxString& aPath );
/** /**
* @brief Non-virtual destructor (so no dervied classes) * Non-virtual destructor (so no derived classes)
*/ */
~PROJECT_TEMPLATE(); ~PROJECT_TEMPLATE();
/** /**
* @brief Get the dir name of the project template * Get the dir name of the project template (i.e. the name of the last folder containing
* (i.e. the name of the last folder containing the template files) * the template files)
*
* @return the dir name of the template * @return the dir name of the template
*/ */
wxString GetPrjDirName(); wxString GetPrjDirName();
/** /**
* @brief Get the full Html filename for the project template * Get the full Html filename for the project template
*
* @return the html meta information file for this template * @return the html meta information file for this template
*/ */
wxFileName GetHtmlFile(); wxFileName GetHtmlFile();
/** /**
* @brief Copies and renames all template files to create a new project. * Copies and renames all template files to create a new project.
*
* @param aNewProjectPath The full path of the new project file to create * @param aNewProjectPath The full path of the new project file to create
* @param aErrorMsg is an optional string to place project creation error messages.
* @return true if the project creation was successful otherwise false.
*/ */
bool CreateProject( wxFileName& aNewProjectPath ); bool CreateProject( wxFileName& aNewProjectPath, wxString* aErrorMsg = nullptr );
/** /**
* @brief Get the 64px^2 icon for the project template * Get the 64px^2 icon for the project template
*
* @return an image file of 64px x 64px which is the templates icon * @return an image file of 64px x 64px which is the templates icon
*/ */
wxBitmap* GetIcon(); wxBitmap* GetIcon();
/** /**
* @brief Get the title of the project (extracted from the html title tag) * Get the title of the project (extracted from the html title tag)
*/ */
wxString* GetTitle(); wxString* GetTitle();
/** /**
* @brief Get a vector list of filenames for the template. The files are the source files, * Get a vector list of filenames for the template. The files are the source files,
* and have not yet been through any renaming * and have not yet been through any renaming
*/ */
std::vector<wxFileName> GetFileList(); std::vector<wxFileName> GetFileList();
/**
* Fetch the list of destination files to be copied when the new project is created.
*
* @param aNewProjectPath is the path to the project to be created.
* @param aDestFiles is a container to place the list of destination files to be created.
* @return the number of destination files added to the container.
*/
size_t GetDestinationFiles( const wxFileName& aNewProjectPath,
std::vector< wxFileName >& aDestFiles );
}; };
#endif #endif