From 11785b08feabc24f81ad9059f93f62c63d89a3bd Mon Sep 17 00:00:00 2001 From: Wayne Stambaugh Date: Tue, 18 May 2021 13:59:03 -0400 Subject: [PATCH] Eeschema: fix plot path in stand alone mode with no project loaded. Fixes https://gitlab.com/kicad/code/kicad/8308 --- eeschema/dialogs/dialog_plot_schematic.cpp | 165 +++++++++++++++++++-- eeschema/dialogs/dialog_plot_schematic.h | 29 +++- 2 files changed, 176 insertions(+), 18 deletions(-) diff --git a/eeschema/dialogs/dialog_plot_schematic.cpp b/eeschema/dialogs/dialog_plot_schematic.cpp index b41f705663..4efa236b25 100644 --- a/eeschema/dialogs/dialog_plot_schematic.cpp +++ b/eeschema/dialogs/dialog_plot_schematic.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,8 @@ #include #include +#include + // static members (static to remember last state): int DIALOG_PLOT_SCHEMATIC::m_pageSizeSelect = PAGE_SIZE_AUTO; @@ -194,14 +197,26 @@ void DIALOG_PLOT_SCHEMATIC::OnOutputDirectoryBrowseClicked( wxCommandEvent& even { // Build the absolute path of current output directory to preselect it in the file browser. wxString path = ExpandEnvVarSubstitutions( m_outputDirectoryName->GetValue(), &Prj() ); - path = Prj().AbsolutePath( path ); + + // When editing a schematic that is not part of a project in the stand alone mode, the + // project path is not defined so point to the users document path to save the plot files. + if( Prj().IsNullProject() ) + { + path = wxStandardPaths::Get().GetDocumentsDir(); + } + else + { + // Build the absolute path of current output directory to preselect it in the file browser. + path = ExpandEnvVarSubstitutions( m_outputDirectoryName->GetValue(), &Prj() ); + path = Prj().AbsolutePath( path ); + } wxDirDialog dirDialog( this, _( "Select Output Directory" ), path ); if( dirDialog.ShowModal() == wxID_CANCEL ) return; - wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() ); + wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() ); wxFileName fn( Prj().AbsolutePath( m_parent->Schematic().Root().GetFileName() ) ); wxString defaultPath = fn.GetPathWithSep(); @@ -409,25 +424,40 @@ wxFileName DIALOG_PLOT_SCHEMATIC::createPlotFileName( const wxString& aPlotFileN const wxString& aExtension, REPORTER* aReporter ) { - wxString path = ExpandEnvVarSubstitutions( m_outputDirectoryName->GetValue(), &Prj() ); - wxFileName outputDir = wxFileName::DirName( path ); - wxString plotFileName; + wxFileName retv; + wxFileName tmp; + + tmp.SetPath( getOutputPath() ); + retv.SetPath( tmp.GetPath() ); if( !aPlotFileName.IsEmpty() ) - plotFileName = Prj().AbsolutePath( aPlotFileName + wxT( "." ) + aExtension ); + retv.SetName( aPlotFileName ); else - plotFileName = Prj().AbsolutePath( _( "Schematic" ) + wxT( "." ) + aExtension ); + retv.SetName( _( "Schematic" ) ); - if( !EnsureFileDirectoryExists( &outputDir, plotFileName, aReporter ) ) + retv.SetExt( aExtension ); + + if( !EnsureFileDirectoryExists( &tmp, retv.GetFullName(), aReporter ) + || !tmp.IsDirWritable() ) { wxString msg = wxString::Format( _( "Could not write plot files to folder \"%s\"." ), - outputDir.GetPath() ); + tmp.GetPath() ); aReporter->Report( msg, RPT_SEVERITY_ERROR ); + retv.Clear(); + + SCHEMATIC_SETTINGS& settings = m_parent->Schematic().Settings(); + settings.m_PlotDirectoryName.Clear(); + + m_configChanged = true; + } + else + { + retv.SetPath( tmp.GetPath() ); } - wxFileName fn( plotFileName ); - fn.SetPath( outputDir.GetFullPath() ); - return fn; + wxLogTrace( tracePathsAndFiles, "Writing plot file '%s'.", retv.GetFullPath() ); + + return retv; } @@ -479,6 +509,9 @@ void DIALOG_PLOT_SCHEMATIC::createDxfFile( bool aPlotAll, bool aPlotDrawingSheet wxString ext = DXF_PLOTTER::GetDefaultFileExtension(); wxFileName plotFileName = createPlotFileName( fname, ext, &reporter ); + if( !plotFileName.IsOk() ) + return; + if( plotOneSheetDxf( plotFileName.GetFullPath(), screen, aRenderSettings, plot_offset, 1.0, aPlotDrawingSheet ) ) { @@ -644,6 +677,9 @@ void DIALOG_PLOT_SCHEMATIC::createHPGLFile( bool aPlotAll, bool aPlotFrameRef, wxString ext = HPGL_PLOTTER::GetDefaultFileExtension(); wxFileName plotFileName = createPlotFileName( fname, ext, &reporter ); + if( !plotFileName.IsOk() ) + return; + LOCALE_IO toggle; if( plotOneSheetHpgl( plotFileName.GetFullPath(), screen, plotPage, aRenderSettings, @@ -713,7 +749,7 @@ bool DIALOG_PLOT_SCHEMATIC::plotOneSheetHpgl( const wxString& aFileName, // Init : plotter->SetCreator( wxT( "Eeschema-HPGL" ) ); - if( ! plotter->OpenFile( aFileName ) ) + if( !plotter->OpenFile( aFileName ) ) { delete plotter; return false; @@ -801,6 +837,9 @@ void DIALOG_PLOT_SCHEMATIC::createPDFFile( bool aPlotAll, bool aPlotDrawingSheet wxString ext = PDF_PLOTTER::GetDefaultFileExtension(); plotFileName = createPlotFileName( fname, ext, &reporter ); + if( !plotFileName.IsOk() ) + return; + if( !plotter->OpenFile( plotFileName.GetFullPath() ) ) { msg.Printf( _( "Unable to create file \"%s\".\n" ), @@ -985,6 +1024,7 @@ void DIALOG_PLOT_SCHEMATIC::createPSFile( bool aPlotAll, bool aPlotFrameRef, try { wxString fname = m_parent->GetUniqueFilenameForCurrentSheet(); + // The sub sheet can be in a sub_hierarchy, but we plot the file in the // main project folder (or the folder specified by the caller), // so replace separators to create a unique filename: @@ -993,6 +1033,9 @@ void DIALOG_PLOT_SCHEMATIC::createPSFile( bool aPlotAll, bool aPlotFrameRef, wxString ext = PS_PLOTTER::GetDefaultFileExtension(); wxFileName plotFileName = createPlotFileName( fname, ext, &reporter ); + if( !plotFileName.IsOk() ) + return; + if( plotOneSheetPS( plotFileName.GetFullPath(), screen, aRenderSettings, plotPage, plot_offset, scale, aPlotFrameRef ) ) { @@ -1114,6 +1157,9 @@ void DIALOG_PLOT_SCHEMATIC::createSVGFile( bool aPrintAll, bool aPrintFrameRef, wxString ext = SVG_PLOTTER::GetDefaultFileExtension(); wxFileName plotFileName = createPlotFileName( fname, ext, &reporter ); + if( !plotFileName.IsOk() ) + return; + bool success = plotOneSheetSVG( plotFileName.GetFullPath(), screen, aRenderSettings, getModeColor() ? false : true, aPrintFrameRef ); @@ -1199,3 +1245,96 @@ bool DIALOG_PLOT_SCHEMATIC::plotOneSheetSVG( const wxString& aFileName, return true; } + + +wxString DIALOG_PLOT_SCHEMATIC::getOutputPath() +{ + wxString msg; + wxString extMsg; + wxFileName fn; + + extMsg.Printf( _( "Falling back to user path '%s'." ), + wxStandardPaths::Get().GetDocumentsDir() ); + + // Build the absolute path of current output directory to preselect it in the file browser. + wxString path = ExpandEnvVarSubstitutions( m_outputDirectoryName->GetValue(), &Prj() ); + + fn.SetPath( path ); + + // If the contents of the path edit control results in an absolute path, return it as is. + if( fn.IsAbsolute() ) + return path; + + // When editing a schematic that is not part of a project in the stand alone mode, the + // project path is not defined. + if( Prj().IsNullProject() ) + { + SCH_SCREEN* screen = m_parent->Schematic().RootScreen(); + + if( screen && !screen->GetFileName().IsEmpty() ) + { + fn = screen->GetFileName(); + msg.Printf( _( "Cannot normalize path '%s%s'." ), fn.GetPathWithSep(), path ); + fn.SetPath( fn.GetPathWithSep() + path ); + + // Normalize always returns true for a non-empty file name so clear the file name + // and extension so that only the path is normalized. + fn.SetName( wxEmptyString ); + fn.SetExt( wxEmptyString ); + + if( fn.Normalize() ) + { + path = fn.GetPath(); + } + else + { + wxMessageDialog dlg( this, msg, _( "Warning" ), + wxOK | wxCENTER | wxRESIZE_BORDER | wxICON_EXCLAMATION | + wxSTAY_ON_TOP ); + + dlg.SetExtendedMessage( extMsg ); + dlg.ShowModal(); + + path = wxStandardPaths::Get().GetDocumentsDir(); + } + } + else + { + msg = _( "No project or path defined for the current schematic." ); + + wxMessageDialog dlg( this, msg, _( "Warning" ), + wxOK | wxCENTER | wxRESIZE_BORDER | wxICON_EXCLAMATION | + wxSTAY_ON_TOP ); + dlg.SetExtendedMessage( extMsg ); + dlg.ShowModal(); + + // Always fall back to user's document path if no other absolute path can be normalized. + path = wxStandardPaths::Get().GetDocumentsDir(); + } + } + else + { + msg.Printf( _( "Cannot normalize path '%s%s'." ), Prj().GetProjectPath(), path ); + + // Build the absolute path of current output directory and the project path. + fn.SetPath( Prj().GetProjectPath() + path ); + + if( fn.Normalize() ) + { + path = fn.GetPath(); + } + else + { + wxMessageDialog dlg( this, msg, _( "Warning" ), + wxOK | wxCENTER | wxRESIZE_BORDER | wxICON_EXCLAMATION | + wxSTAY_ON_TOP ); + + dlg.SetExtendedMessage( extMsg ); + dlg.ShowModal(); + + path = wxStandardPaths::Get().GetDocumentsDir(); + } + } + + return path; +} diff --git a/eeschema/dialogs/dialog_plot_schematic.h b/eeschema/dialogs/dialog_plot_schematic.h index 95d276d1eb..dabe767c16 100644 --- a/eeschema/dialogs/dialog_plot_schematic.h +++ b/eeschema/dialogs/dialog_plot_schematic.h @@ -63,7 +63,7 @@ public: DIALOG_PLOT_SCHEMATIC( SCH_EDIT_FRAME* parent ); /** - * Return true if the project configutation was modified. + * Return true if the project configuration was modified. */ bool PrjConfigChanged() { return m_configChanged; } @@ -102,10 +102,11 @@ private: void setupPlotPagePDF( PLOTTER* aPlotter, SCH_SCREEN* aScreen ); /** - * Everything done, close the plot and restore the environment - * @param aPlotter the plotter to close and destroy - * @param aOldsheetpath the stored old sheet path for the current sheet before the plot started - */ + * Everything done, close the plot and restore the environment. + * + * @param aPlotter the plotter to close and destroy + * @param aOldsheetpath the stored old sheet path for the current sheet before the plot started + */ void restoreEnvironment( PDF_PLOTTER* aPlotter, SCH_SHEET_PATH& aOldsheetpath ); // DXF @@ -181,6 +182,24 @@ private: wxFileName createPlotFileName( const wxString& aPlotFileName, const wxString& aExtension, REPORTER* aReporter = nullptr ); + /** + * Determine the best absolute path to plot files given the contents of the path + * edit control. + * + * - If the path edit control results in an absolute path, use it as is. + * - If the path edit control is not an absolute path and the project file is valid, use + the project root path to normalize the contents of the path edit control. + * - If the path edit control is not an absolute path and the project file does not exist + * and the screen file name is valid, use the screen file name path. + * - If the path edit control is not an absolute path and the project file does not exist + * and the screen file name is empty, user the user's documents folder. + * - Fall back to the user's document path if any of the above conditions do not result + * in a valid absolute path. + * + * @return a valid path to write the plot files. + */ + wxString getOutputPath(); + SCH_EDIT_FRAME* m_parent; bool m_configChanged; // true if a project config param has changed PLOT_FORMAT m_plotFormat;