Eeschema: allow sheet file paths outside the project to be relative.
This commit is contained in:
parent
bd72f7a054
commit
9fcd847238
|
@ -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) 2009 Wayne Stambaugh <stambaughw@gmail.com>
|
* Copyright (C) 2009 Wayne Stambaugh <stambaughw@gmail.com>
|
||||||
* Copyright (C) 2014-2020 KiCad Developers, see CHANGELOG.txt for contributors.
|
* Copyright (C) 2014-2021 KiCad Developers, see CHANGELOG.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
|
||||||
|
@ -87,7 +87,7 @@ DIALOG_SHEET_PROPERTIES::DIALOG_SHEET_PROPERTIES( SCH_EDIT_FRAME* aParent, SCH_S
|
||||||
// wxFormBuilder doesn't include this event...
|
// wxFormBuilder doesn't include this event...
|
||||||
m_grid->Connect( wxEVT_GRID_CELL_CHANGING,
|
m_grid->Connect( wxEVT_GRID_CELL_CHANGING,
|
||||||
wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
|
wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
|
||||||
NULL, this );
|
nullptr, this );
|
||||||
|
|
||||||
finishDialogSettings();
|
finishDialogSettings();
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ DIALOG_SHEET_PROPERTIES::~DIALOG_SHEET_PROPERTIES()
|
||||||
|
|
||||||
m_grid->Disconnect( wxEVT_GRID_CELL_CHANGING,
|
m_grid->Disconnect( wxEVT_GRID_CELL_CHANGING,
|
||||||
wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
|
wxGridEventHandler( DIALOG_SHEET_PROPERTIES::OnGridCellChanging ),
|
||||||
NULL, this );
|
nullptr, this );
|
||||||
|
|
||||||
// Delete the GRID_TRICKS.
|
// Delete the GRID_TRICKS.
|
||||||
m_grid->PopEventHandler( true );
|
m_grid->PopEventHandler( true );
|
||||||
|
@ -128,7 +128,7 @@ bool DIALOG_SHEET_PROPERTIES::TransferDataToWindow()
|
||||||
if( field_copy.GetId() == SHEETFILENAME )
|
if( field_copy.GetId() == SHEETFILENAME )
|
||||||
{
|
{
|
||||||
wxString filename = field_copy.GetText();
|
wxString filename = field_copy.GetText();
|
||||||
filename.Replace( wxT("/"), wxT("\\") );
|
filename.Replace( wxT( "/" ), wxT( "\\" ) );
|
||||||
field_copy.SetText( filename );
|
field_copy.SetText( filename );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -240,23 +240,24 @@ bool DIALOG_SHEET_PROPERTIES::TransferDataFromWindow()
|
||||||
if( !wxDialog::TransferDataFromWindow() ) // Calls our Validate() method.
|
if( !wxDialog::TransferDataFromWindow() ) // Calls our Validate() method.
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
wxString newRelativeNativeFilename = m_fields->at( SHEETFILENAME ).GetText();
|
// Sheet file names can be relative or absolute.
|
||||||
|
wxString sheetFileName = m_fields->at( SHEETFILENAME ).GetText();
|
||||||
|
|
||||||
// Ensure filepath is not empty. (In normal use will be caught by grid validators,
|
// Ensure filepath is not empty. (In normal use will be caught by grid validators,
|
||||||
// but unedited data from existing files can be bad.)
|
// but unedited data from existing files can be bad.)
|
||||||
if( newRelativeNativeFilename.IsEmpty() )
|
if( sheetFileName.IsEmpty() )
|
||||||
{
|
{
|
||||||
wxMessageBox( _( "A sheet must have a valid file name." ) );
|
DisplayError( this, _( "A sheet must have a valid file name." ) );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the filename extension is OK. (In normaly use will be caught by grid
|
// Ensure the filename extension is OK. In normal use will be caught by grid validators,
|
||||||
// validators, but unedited data from existing files can be bad.)
|
// but unedited data from existing files can be bad.
|
||||||
wxFileName fn( newRelativeNativeFilename );
|
wxFileName fn( sheetFileName );
|
||||||
|
|
||||||
if( fn.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
|
if( fn.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
|
||||||
{
|
{
|
||||||
wxMessageBox( _( "Sheet file must have a '.kicad_sch' extension." ) );
|
DisplayError( this, _( "Sheet file must have a '.kicad_sch' extension." ) );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,8 +273,54 @@ bool DIALOG_SHEET_PROPERTIES::TransferDataFromWindow()
|
||||||
|
|
||||||
if( filename_changed || m_sheet->IsNew() )
|
if( filename_changed || m_sheet->IsNew() )
|
||||||
{
|
{
|
||||||
|
SCH_SCREEN* currentScreen = m_frame->GetCurrentSheet().LastScreen();
|
||||||
|
|
||||||
|
wxCHECK( currentScreen, false );
|
||||||
|
|
||||||
|
bool clearFileName = false;
|
||||||
|
|
||||||
|
// This can happen for the root sheet when opening Eeschema in the stand alone mode.
|
||||||
|
if( currentScreen->GetFileName().IsEmpty() )
|
||||||
|
{
|
||||||
|
clearFileName = true;
|
||||||
|
currentScreen->SetFileName( m_frame->Prj().AbsolutePath( wxT( "noname.kicad_sch" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
wxFileName tmp( fn );
|
||||||
|
wxFileName screenFileName = currentScreen->GetFileName();
|
||||||
|
|
||||||
|
if( fn.IsAbsolute() && fn.MakeRelativeTo( screenFileName.GetPath() ) )
|
||||||
|
{
|
||||||
|
wxMessageDialog makeRelDlg( this, _( "Use relative path for sheet file?" ),
|
||||||
|
_( "Sheet File Path" ),
|
||||||
|
wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
|
||||||
|
|
||||||
|
makeRelDlg.SetExtendedMessage(
|
||||||
|
_( "Using relative hierarchical sheet file name paths improves schematic\n"
|
||||||
|
"portability across systems and platforms. Using absolute paths can\n"
|
||||||
|
"result in portability issues." ) );
|
||||||
|
makeRelDlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Use Relative Path" ) ),
|
||||||
|
wxMessageDialog::ButtonLabel( _( "Use Absolute Path" ) ) );
|
||||||
|
|
||||||
|
if( makeRelDlg.ShowModal() == wxID_YES )
|
||||||
|
{
|
||||||
|
wxLogTrace( tracePathsAndFiles, "\n Converted absolute path: '%s'\n"
|
||||||
|
" to relative path: '%s'", tmp.GetPath(), fn.GetPath() );
|
||||||
|
m_fields->at( SHEETFILENAME ).SetText( fn.GetFullPath() );
|
||||||
|
newRelativeFilename = fn.GetFullPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( !onSheetFilenameChanged( newRelativeFilename ) )
|
if( !onSheetFilenameChanged( newRelativeFilename ) )
|
||||||
|
{
|
||||||
|
if( clearFileName )
|
||||||
|
currentScreen->SetFileName( wxEmptyString );
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( clearFileName )
|
||||||
|
currentScreen->SetFileName( wxEmptyString );
|
||||||
|
|
||||||
// One last validity check (and potential repair) just to be sure to be sure
|
// One last validity check (and potential repair) just to be sure to be sure
|
||||||
SCH_SHEET_LIST repairedList( &m_frame->Schematic().Root(), true );
|
SCH_SHEET_LIST repairedList( &m_frame->Schematic().Root(), true );
|
||||||
|
@ -346,39 +393,44 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
{
|
{
|
||||||
wxString msg;
|
wxString msg;
|
||||||
|
|
||||||
// Relative file names are relative to the path of the current sheet. This allows for
|
// Sheet file names are relative to the path of the current sheet. This allows for
|
||||||
// nesting of schematic files in subfolders.
|
// nesting of schematic files in subfolders. Screen file names are always absolute.
|
||||||
wxFileName fileName( aNewFilename );
|
wxFileName sheetFileName( aNewFilename );
|
||||||
|
|
||||||
if( fileName.GetExt().IsEmpty() )
|
if( sheetFileName.GetExt().IsEmpty() )
|
||||||
{
|
{
|
||||||
fileName.SetExt( KiCadSchematicFileExtension );
|
sheetFileName.SetExt( KiCadSchematicFileExtension );
|
||||||
}
|
}
|
||||||
else if( fileName.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
|
else if( sheetFileName.GetExt().CmpNoCase( KiCadSchematicFileExtension ) != 0 )
|
||||||
{
|
{
|
||||||
msg.Printf( _( "The file \"%s\" does not appear to be a valid schematic file." ),
|
msg.Printf( _( "The file \"%s\" does not appear to be a valid schematic file." ),
|
||||||
fileName.GetFullName() );
|
sheetFileName.GetFullName() );
|
||||||
wxMessageDialog badSchFileDialog( this, msg, _( "Invalid Schematic File" ),
|
wxMessageDialog badSchFileDialog( this, msg, _( "Invalid Schematic File" ),
|
||||||
wxOK | wxCENTRE | wxICON_EXCLAMATION );
|
wxOK | wxCENTRE | wxICON_EXCLAMATION );
|
||||||
badSchFileDialog.ShowModal();
|
badSchFileDialog.ShowModal();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !fileName.IsAbsolute() )
|
wxFileName screenFileName( sheetFileName );
|
||||||
|
wxFileName tmp( sheetFileName );
|
||||||
|
|
||||||
|
SCH_SCREEN* currentScreen = m_frame->GetCurrentSheet().LastScreen();
|
||||||
|
|
||||||
|
wxCHECK( currentScreen, false );
|
||||||
|
|
||||||
|
// SCH_SCREEN file names are always absolute.
|
||||||
|
wxFileName currentScreenFileName = currentScreen->GetFileName();
|
||||||
|
|
||||||
|
if( !screenFileName.Normalize( wxPATH_NORM_ALL, currentScreenFileName.GetPath() ) )
|
||||||
{
|
{
|
||||||
const SCH_SCREEN* currentScreen = m_frame->GetCurrentSheet().LastScreen();
|
msg.Printf( _( "Cannot normalize new sheet schematic file path:\n'%s'\n"
|
||||||
wxCHECK_MSG( currentScreen, false, "Invalid sheet path object." );
|
"against parent sheet schematic file path:\n`%s`." ),
|
||||||
|
sheetFileName.GetPath(), currentScreenFileName.GetPath() );
|
||||||
wxFileName currentSheetFileName = currentScreen->GetFileName();
|
DisplayError( this, msg );
|
||||||
|
return false;
|
||||||
if( !fileName.Normalize( wxPATH_NORM_ALL, currentSheetFileName.GetPath() ) )
|
|
||||||
{
|
|
||||||
wxFAIL_MSG( "Cannot normalize new sheet schematic file path." );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString newAbsoluteFilename = fileName.GetFullPath();
|
wxString newAbsoluteFilename = screenFileName.GetFullPath();
|
||||||
|
|
||||||
// Inside Eeschema, filenames are stored using unix notation
|
// Inside Eeschema, filenames are stored using unix notation
|
||||||
newAbsoluteFilename.Replace( wxT( "\\" ), wxT( "/" ) );
|
newAbsoluteFilename.Replace( wxT( "\\" ), wxT( "/" ) );
|
||||||
|
@ -388,7 +440,7 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
bool clearAnnotation = false;
|
bool clearAnnotation = false;
|
||||||
bool restoreSheet = false;
|
bool restoreSheet = false;
|
||||||
bool isExistingSheet = false;
|
bool isExistingSheet = false;
|
||||||
SCH_SCREEN* useScreen = NULL;
|
SCH_SCREEN* useScreen = nullptr;
|
||||||
|
|
||||||
// Search for a schematic file having the same filename already in use in the hierarchy
|
// Search for a schematic file having the same filename already in use in the hierarchy
|
||||||
// or on disk, in order to reuse it.
|
// or on disk, in order to reuse it.
|
||||||
|
@ -396,12 +448,11 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
{
|
{
|
||||||
loadFromFile = wxFileExists( newAbsoluteFilename );
|
loadFromFile = wxFileExists( newAbsoluteFilename );
|
||||||
|
|
||||||
wxLogTrace( tracePathsAndFiles, "Sheet requested file \"%s\", %s",
|
wxLogTrace( tracePathsAndFiles, "\n Sheet requested file \"%s\", %s",
|
||||||
newAbsoluteFilename,
|
newAbsoluteFilename, ( loadFromFile ) ? "found" : "not found" );
|
||||||
( loadFromFile ) ? "found" : "not found" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_sheet->GetScreen() == NULL ) // New just created sheet.
|
if( m_sheet->GetScreen() == nullptr ) // New just created sheet.
|
||||||
{
|
{
|
||||||
if( !m_frame->AllowCaseSensitiveFileNameClashes( newAbsoluteFilename ) )
|
if( !m_frame->AllowCaseSensitiveFileNameClashes( newAbsoluteFilename ) )
|
||||||
return false;
|
return false;
|
||||||
|
@ -411,7 +462,7 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
clearAnnotation = true;
|
clearAnnotation = true;
|
||||||
wxString existsMsg;
|
wxString existsMsg;
|
||||||
wxString linkMsg;
|
wxString linkMsg;
|
||||||
existsMsg.Printf( _( "\"%s\" already exists." ), fileName.GetFullName() );
|
existsMsg.Printf( _( "\"%s\" already exists." ), sheetFileName.GetFullName() );
|
||||||
linkMsg.Printf( _( "Link \"%s\" to this file?" ), newAbsoluteFilename );
|
linkMsg.Printf( _( "Link \"%s\" to this file?" ), newAbsoluteFilename );
|
||||||
msg.Printf( wxT( "%s\n\n%s" ), existsMsg, linkMsg );
|
msg.Printf( wxT( "%s\n\n%s" ), existsMsg, linkMsg );
|
||||||
|
|
||||||
|
@ -440,9 +491,9 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
replaceMsg.Printf( _( "Change \"%s\" link from \"%s\" to \"%s\"?" ),
|
replaceMsg.Printf( _( "Change \"%s\" link from \"%s\" to \"%s\"?" ),
|
||||||
newAbsoluteFilename,
|
newAbsoluteFilename,
|
||||||
m_sheet->GetFileName(),
|
m_sheet->GetFileName(),
|
||||||
fileName.GetFullName() );
|
sheetFileName.GetFullName() );
|
||||||
newMsg.Printf( _( "Create new file \"%s\" with contents of \"%s\"?" ),
|
newMsg.Printf( _( "Create new file \"%s\" with contents of \"%s\"?" ),
|
||||||
fileName.GetFullName(),
|
sheetFileName.GetFullName(),
|
||||||
m_sheet->GetFileName() );
|
m_sheet->GetFileName() );
|
||||||
noUndoMsg = _( "This action cannot be undone." );
|
noUndoMsg = _( "This action cannot be undone." );
|
||||||
|
|
||||||
|
@ -473,7 +524,7 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if( loadFromFile )
|
if( loadFromFile )
|
||||||
m_sheet->SetScreen( NULL );
|
m_sheet->SetScreen( nullptr );
|
||||||
}
|
}
|
||||||
else // Save to new file name.
|
else // Save to new file name.
|
||||||
{
|
{
|
||||||
|
@ -515,7 +566,6 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
|
|
||||||
msg.Printf( _( "Failed to save schematic \"%s\"" ), newAbsoluteFilename );
|
msg.Printf( _( "Failed to save schematic \"%s\"" ), newAbsoluteFilename );
|
||||||
m_frame->SetMsgPanel( wxEmptyString, msg );
|
m_frame->SetMsgPanel( wxEmptyString, msg );
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,13 +574,12 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
// the screen reference counting in complex hierarchies.
|
// the screen reference counting in complex hierarchies.
|
||||||
if( m_sheet->GetScreenCount() > 1 )
|
if( m_sheet->GetScreenCount() > 1 )
|
||||||
{
|
{
|
||||||
m_sheet->SetScreen( NULL );
|
m_sheet->SetScreen( nullptr );
|
||||||
loadFromFile = true;
|
loadFromFile = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxFileName nativeFileName( aNewFilename );
|
|
||||||
SCH_SHEET_PATH& currentSheet = m_frame->GetCurrentSheet();
|
SCH_SHEET_PATH& currentSheet = m_frame->GetCurrentSheet();
|
||||||
|
|
||||||
if( useScreen )
|
if( useScreen )
|
||||||
|
@ -538,7 +587,7 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
// Create a temporary sheet for recursion testing to prevent a possible recursion error.
|
// Create a temporary sheet for recursion testing to prevent a possible recursion error.
|
||||||
std::unique_ptr< SCH_SHEET> tmpSheet = std::make_unique<SCH_SHEET>();
|
std::unique_ptr< SCH_SHEET> tmpSheet = std::make_unique<SCH_SHEET>();
|
||||||
tmpSheet->GetFields()[SHEETNAME] = m_fields->at( SHEETNAME );
|
tmpSheet->GetFields()[SHEETNAME] = m_fields->at( SHEETNAME );
|
||||||
tmpSheet->GetFields()[SHEETFILENAME].SetText( nativeFileName.GetFullPath() );
|
tmpSheet->GetFields()[SHEETFILENAME].SetText( sheetFileName.GetFullPath() );
|
||||||
tmpSheet->SetScreen( useScreen );
|
tmpSheet->SetScreen( useScreen );
|
||||||
|
|
||||||
// No need to check for valid library IDs if we are using an existing screen.
|
// No need to check for valid library IDs if we are using an existing screen.
|
||||||
|
@ -564,7 +613,7 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !m_frame->LoadSheetFromFile( m_sheet, ¤tSheet, newAbsoluteFilename )
|
if( !m_frame->LoadSheetFromFile( m_sheet, ¤tSheet, newAbsoluteFilename )
|
||||||
|| m_frame->CheckSheetForRecursion( m_sheet, ¤tSheet ) )
|
|| m_frame->CheckSheetForRecursion( m_sheet, ¤tSheet ) )
|
||||||
{
|
{
|
||||||
if( restoreSheet )
|
if( restoreSheet )
|
||||||
currentSheet.LastScreen()->Append( m_sheet );
|
currentSheet.LastScreen()->Append( m_sheet );
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
* @brief Implementation of SCH_SCREEN and SCH_SCREENS classes.
|
* @brief Implementation of SCH_SCREEN and SCH_SCREENS classes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <wx/filefn.h>
|
||||||
|
|
||||||
#include <eda_rect.h>
|
#include <eda_rect.h>
|
||||||
#include <id.h>
|
#include <id.h>
|
||||||
#include <kicad_string.h>
|
#include <kicad_string.h>
|
||||||
|
@ -103,6 +105,14 @@ void SCH_SCREEN::clearLibSymbols()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SCH_SCREEN::SetFileName( const wxString& aFileName )
|
||||||
|
{
|
||||||
|
wxASSERT( aFileName.IsEmpty() || wxIsAbsolutePath( aFileName ) );
|
||||||
|
|
||||||
|
m_fileName = aFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SCH_SCREEN::IncRefCount()
|
void SCH_SCREEN::IncRefCount()
|
||||||
{
|
{
|
||||||
m_refCount++;
|
m_refCount++;
|
||||||
|
|
|
@ -128,7 +128,15 @@ public:
|
||||||
const PAGE_INFO& GetPageSettings() const { return m_paper; }
|
const PAGE_INFO& GetPageSettings() const { return m_paper; }
|
||||||
void SetPageSettings( const PAGE_INFO& aPageSettings ) { m_paper = aPageSettings; }
|
void SetPageSettings( const PAGE_INFO& aPageSettings ) { m_paper = aPageSettings; }
|
||||||
|
|
||||||
void SetFileName( const wxString& aFileName ) { m_fileName = aFileName; }
|
/**
|
||||||
|
* Set the file name for this screen to \a aFileName.
|
||||||
|
*
|
||||||
|
* @note Screen file names must be absolute or empty. Absolute file names do not have to
|
||||||
|
* exist yet in the case of a new schematic file but file names cannot be relative.
|
||||||
|
*
|
||||||
|
* @param aFileName is the absolute file name and path of the screen.
|
||||||
|
*/
|
||||||
|
void SetFileName( const wxString& aFileName );
|
||||||
|
|
||||||
const wxString& GetFileName() const { return m_fileName; }
|
const wxString& GetFileName() const { return m_fileName; }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue