summaryrefslogtreecommitdiff
path: root/pcbnew/files.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/files.cpp')
-rw-r--r--pcbnew/files.cpp869
1 files changed, 869 insertions, 0 deletions
diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp
new file mode 100644
index 0000000..e8465ba
--- /dev/null
+++ b/pcbnew/files.cpp
@@ -0,0 +1,869 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2011-2016 Wayne Stambaugh <stambaughw@verizon.net>
+ * Copyright (C) 2016 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
+ */
+
+/**
+ * @file pcbnew/files.cpp
+ * @brief Read and write board files.
+ */
+
+#include <fctsys.h>
+#include <class_drawpanel.h>
+#include <confirm.h>
+#include <kicad_string.h>
+#include <gestfich.h>
+#include <wxPcbStruct.h>
+#include <macros.h>
+#include <3d_viewer.h>
+#include <richio.h>
+#include <filter_reader.h>
+#include <pgm_base.h>
+#include <msgpanel.h>
+#include <fp_lib_table.h>
+#include <ratsnest_data.h>
+
+#include <pcbnew.h>
+#include <pcbnew_id.h>
+#include <io_mgr.h>
+#include <wildcards_and_files_ext.h>
+
+#include <class_board.h>
+#include <build_version.h> // LEGACY_BOARD_FILE_VERSION
+#include <module_editor_frame.h>
+#include <modview_frame.h>
+
+#include <wx/stdpaths.h>
+
+
+//#define USE_INSTRUMENTATION true
+#define USE_INSTRUMENTATION false
+
+
+static const wxChar backupSuffix[] = wxT( "-bak" );
+static const wxChar autosavePrefix[] = wxT( "_autosave-" );
+
+
+wxString PCB_EDIT_FRAME::GetAutoSaveFilePrefix()
+{
+ return wxString( autosavePrefix );
+}
+
+
+/**
+ * Function AskLoadBoardFileName
+ * puts up a wxFileDialog asking for a BOARD filename to open.
+ *
+ * @param aParent is a wxFrame passed to wxFileDialog.
+ * @param aCtl is where to put the OpenProjectFiles() control bits.
+ *
+ * @param aFileName on entry is a probable choice, on return is the chosen filename.
+ * @param aKicadFilesOnly true to list kiacad pcb files plugins only, false to list all plugins.
+ *
+ * @return bool - true if chosen, else false if user aborted.
+ */
+bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName, bool aKicadFilesOnly = false )
+{
+ // This is a subset of all PLUGINs which are trusted to be able to
+ // load a BOARD. User may occasionally use the wrong plugin to load a
+ // *.brd file (since both legacy and eagle use *.brd extension),
+ // but eventually *.kicad_pcb will be more common than legacy *.brd files.
+ static const struct
+ {
+ const wxString& filter;
+ IO_MGR::PCB_FILE_T pluginType;
+ } loaders[] =
+ {
+ { PcbFileWildcard, IO_MGR::KICAD },
+ { LegacyPcbFileWildcard, IO_MGR::LEGACY },
+ { EaglePcbFileWildcard, IO_MGR::EAGLE },
+ { PCadPcbFileWildcard, IO_MGR::PCAD },
+ };
+
+ wxFileName fileName( *aFileName );
+ wxString fileFilters;
+
+ unsigned pluginsCount = aKicadFilesOnly ? 2 : DIM( loaders );
+
+ for( unsigned i=0; i < pluginsCount; ++i )
+ {
+ if( i > 0 )
+ fileFilters += wxChar( '|' );
+
+ fileFilters += wxGetTranslation( loaders[i].filter );
+ }
+
+ wxString path;
+ wxString name;
+
+ if( fileName.FileExists() )
+ {
+ path = fileName.GetPath();
+ name = fileName.GetFullName();
+ }
+ else
+ {
+ path = wxStandardPaths::Get().GetDocumentsDir();
+ // leave name empty
+ }
+
+ wxFileDialog dlg( aParent, _( "Open Board File" ), path, name, fileFilters,
+ wxFD_OPEN | wxFD_FILE_MUST_EXIST );
+
+ if( dlg.ShowModal() != wxID_CANCEL )
+ {
+ int chosenFilter = dlg.GetFilterIndex();
+
+ // if Eagle, tell OpenProjectFiles() to use Eagle plugin. It's the only special
+ // case because of the duplicate use of the *.brd file extension. Other cases
+ // are clear because of unique file extensions.
+ *aCtl = chosenFilter == 2 ? KICTL_EAGLE_BRD : 0;
+ *aFileName = dlg.GetPath();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+
+/**
+ * Function AskSaveBoardFileName
+ * puts up a wxFileDialog asking for a BOARD filename to save.
+ *
+ * @param aParent is a wxFrame passed to wxFileDialog.
+ * @param aFileName on entry is a probable choice, on return is the
+ * chosen full filename (includes path).
+ *
+ * @return bool - true if chosen, else false if user aborted.
+ */
+bool AskSaveBoardFileName( wxWindow* aParent, wxString* aFileName )
+{
+ wxString wildcard = wxGetTranslation( PcbFileWildcard );
+ wxFileName fn = *aFileName;
+
+ fn.SetExt( KiCadPcbFileExtension );
+
+ wxFileDialog dlg( aParent,
+ _( "Save Board File As" ),
+ fn.GetPath(),
+ fn.GetFullName(),
+ wildcard,
+ wxFD_SAVE
+ /* wxFileDialog is not equipped to handle multiple wildcards and
+ wxFD_OVERWRITE_PROMPT both together.
+ | wxFD_OVERWRITE_PROMPT
+ */
+ );
+
+ if( dlg.ShowModal() != wxID_OK )
+ return false;
+
+ fn = dlg.GetPath();
+
+ // always enforce filename extension, user may not have entered it.
+ fn.SetExt( KiCadPcbFileExtension );
+
+ // Since the file overwrite test was removed from wxFileDialog because it doesn't work
+ // when multiple wildcards are defined, we have to check it ourselves to prevent an
+ // existing board file from silently being over written.
+ if( fn.FileExists() )
+ {
+ wxString ask = wxString::Format( _(
+ "The file '%s' already exists.\n\n"
+ "Do you want to overwrite it?" ),
+ GetChars( fn.GetFullPath() )
+ );
+
+ if( !IsOK( aParent, ask ) )
+ {
+ return false;
+ }
+ }
+
+ *aFileName = fn.GetFullPath();
+
+ return true;
+}
+
+
+void PCB_EDIT_FRAME::OnFileHistory( wxCommandEvent& event )
+{
+ wxString fn = GetFileFromHistory( event.GetId(), _( "Printed circuit board" ) );
+
+ if( !!fn )
+ {
+ int open_ctl = 0;
+
+ m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
+
+ if( !wxFileName::IsFileReadable( fn ) )
+ {
+ if( !AskLoadBoardFileName( this, &open_ctl, &fn ) )
+ return;
+ }
+
+ OpenProjectFiles( std::vector<wxString>( 1, fn ), open_ctl );
+ }
+}
+
+
+void PCB_EDIT_FRAME::Files_io( wxCommandEvent& event )
+{
+ int id = event.GetId();
+ Files_io_from_id( id );
+}
+
+void PCB_EDIT_FRAME::Files_io_from_id( int id )
+{
+ wxString msg;
+
+ // If an edition is in progress, stop it.
+ // For something else than save, get rid of current tool.
+ if( id == ID_SAVE_BOARD )
+ m_canvas->EndMouseCapture( -1, m_canvas->GetDefaultCursor() );
+ else
+ m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
+
+ switch( id )
+ {
+ case ID_LOAD_FILE:
+ {
+ // LoadOnePcbFile( GetBoard()->GetFileName(), append=false, aForceFileDialog=true );
+
+ int open_ctl;
+ wxString fileName = Prj().AbsolutePath( GetBoard()->GetFileName() );
+
+ if( !AskLoadBoardFileName( this, &open_ctl, &fileName ) )
+ return;
+
+ OpenProjectFiles( std::vector<wxString>( 1, fileName ), open_ctl );
+ }
+ break;
+
+ case ID_MENU_READ_BOARD_BACKUP_FILE:
+ case ID_MENU_RECOVER_BOARD_AUTOSAVE:
+ {
+ wxFileName currfn = Prj().AbsolutePath( GetBoard()->GetFileName() );
+ wxFileName fn = currfn;
+
+ if( id == ID_MENU_RECOVER_BOARD_AUTOSAVE )
+ {
+ wxString rec_name = wxString( autosavePrefix ) + fn.GetName();
+ fn.SetName( rec_name );
+ }
+ else
+ {
+ wxString backup_ext = fn.GetExt()+ backupSuffix;
+ fn.SetExt( backup_ext );
+ }
+
+ if( !fn.FileExists() )
+ {
+ msg.Printf( _( "Recovery file '%s' not found." ),
+ GetChars( fn.GetFullPath() ) );
+ DisplayInfoMessage( this, msg );
+ break;
+ }
+
+ msg.Printf( _( "OK to load recovery or backup file '%s'" ),
+ GetChars(fn.GetFullPath() ) );
+
+ if( !IsOK( this, msg ) )
+ break;
+
+ GetScreen()->ClrModify(); // do not prompt the user for changes
+
+ // LoadOnePcbFile( fn.GetFullPath(), aAppend=false, aForceFileDialog=false );
+ OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ) );
+
+ // Re-set the name since name or extension was changed
+ GetBoard()->SetFileName( currfn.GetFullPath() );
+ UpdateTitle();
+ }
+ break;
+
+ case ID_APPEND_FILE:
+ {
+ int open_ctl;
+ wxString fileName;
+
+ if( !AskLoadBoardFileName( this, &open_ctl, &fileName, true ) )
+ break;
+
+ AppendBoardFile( fileName, open_ctl );
+
+ m_canvas->Refresh();
+ }
+ break;
+
+ case ID_NEW_BOARD:
+ {
+ if( !Clear_Pcb( true ) )
+ break;
+
+ wxFileName fn( wxStandardPaths::Get().GetDocumentsDir(), wxT( "noname" ),
+ ProjectFileExtension );
+
+ Prj().SetProjectFullName( fn.GetFullPath() );
+
+ fn.SetExt( PcbFileExtension );
+
+ GetBoard()->SetFileName( fn.GetFullPath() );
+ UpdateTitle();
+ ReCreateLayerBox();
+ break;
+ }
+
+ case ID_SAVE_BOARD:
+ if( ! GetBoard()->GetFileName().IsEmpty() )
+ {
+ SavePcbFile( Prj().AbsolutePath( GetBoard()->GetFileName() ) );
+ break;
+ }
+ // Fall through
+ case ID_COPY_BOARD_AS:
+ case ID_SAVE_BOARD_AS:
+ {
+ wxString pro_dir = wxPathOnly( Prj().GetProjectFullName() );
+ wxFileName fn( pro_dir, _( "noname" ), KiCadPcbFileExtension );
+ wxString filename = fn.GetFullPath();
+
+ if( AskSaveBoardFileName( this, &filename ) )
+ {
+ if( id == ID_COPY_BOARD_AS )
+ SavePcbCopy( filename );
+ else
+ SavePcbFile( filename, NO_BACKUP_FILE );
+ }
+ }
+ break;
+
+ default:
+ DisplayError( this, wxT( "File_io Internal Error" ) );
+ break;
+ }
+}
+
+
+// The KIWAY_PLAYER::OpenProjectFiles() API knows nothing about plugins, so
+// determine how to load the BOARD here, with minor assistance from KICTL_EAGLE_BRD
+// bit flag.
+IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl )
+{
+ IO_MGR::PCB_FILE_T pluginType;
+
+ wxFileName fn = aFileName;
+
+ // Note: file extensions are expected to be in ower case.
+ // This is not always true, especially when importing files, so the string
+ // comparisons are case insensitive to try to find the suitable plugin.
+
+ if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::LEGACY ) ) == 0 )
+ {
+ // both legacy and eagle share a common file extension.
+ pluginType = ( aCtl & KICTL_EAGLE_BRD ) ? IO_MGR::EAGLE : IO_MGR::LEGACY;
+ }
+ else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::LEGACY ) + backupSuffix ) == 0 )
+ {
+ pluginType = IO_MGR::LEGACY;
+ }
+ else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::PCAD ) ) == 0 )
+ {
+ pluginType = IO_MGR::PCAD;
+ }
+ else
+ {
+ pluginType = IO_MGR::KICAD;
+ }
+
+ return pluginType;
+}
+
+
+bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
+{
+ // This is for python:
+ if( aFileSet.size() != 1 )
+ {
+ UTF8 msg = StrPrintf( "Pcbnew:%s() takes only a single filename", __func__ );
+ DisplayError( this, msg );
+ return false;
+ }
+
+ wxString fullFileName( aFileSet[0] );
+
+ // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
+ wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(),
+ wxT( "bug in single_top.cpp or project manager." ) );
+
+ if( !LockFile( fullFileName ) )
+ {
+ wxString msg = wxString::Format( _(
+ "PCB file '%s' is already open." ),
+ GetChars( fullFileName )
+ );
+ DisplayError( this, msg );
+ return false;
+ }
+
+ if( GetScreen()->IsModify() )
+ {
+ int response = YesNoCancelDialog( this, _(
+ "The current board has been modified. Do you wish to save the changes?" ),
+ wxEmptyString,
+ _( "Save and Load" ),
+ _( "Load Without Saving" )
+ );
+
+ if( response == wxID_CANCEL )
+ return false;
+ else if( response == wxID_YES )
+ SavePcbFile( GetBoard()->GetFileName(), CREATE_BACKUP_FILE );
+ else
+ {
+ // response == wxID_NO, fall thru
+ }
+ }
+
+ wxFileName pro = fullFileName;
+ pro.SetExt( ProjectFileExtension );
+
+ bool is_new = !wxFileName::IsFileReadable( fullFileName );
+
+ // If its a non-existent schematic and caller thinks it exists
+ if( is_new && !( aCtl & KICTL_CREATE ) )
+ {
+ // notify user that fullFileName does not exist, ask if user wants to create it.
+ wxString ask = wxString::Format( _(
+ "Board '%s' does not exist. Do you wish to create it?" ),
+ GetChars( fullFileName )
+ );
+ if( !IsOK( this, ask ) )
+ return false;
+ }
+
+ Clear_Pcb( false ); // pass false since we prompted above for a modified board
+
+ IO_MGR::PCB_FILE_T pluginType = plugin_type( fullFileName, aCtl );
+
+ bool converted = pluginType != IO_MGR::LEGACY && pluginType != IO_MGR::KICAD;
+
+ if( !converted )
+ {
+ // PROJECT::SetProjectFullName() is an impactful function. It should only be
+ // called under carefully considered circumstances.
+
+ // The calling code should know not to ask me here to change projects unless
+ // it knows what consequences that will have on other KIFACEs running and using
+ // this same PROJECT. It can be very harmful if that calling code is stupid.
+ Prj().SetProjectFullName( pro.GetFullPath() );
+
+ // load project settings before BOARD
+ LoadProjectSettings();
+ }
+
+ if( is_new )
+ {
+ OnModify();
+ }
+ else
+ {
+ BOARD* loadedBoard = 0; // it will be set to non-NULL if loaded OK
+
+ PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) );
+
+ try
+ {
+ PROPERTIES props;
+ char xbuf[30];
+ char ybuf[30];
+
+ // EAGLE_PLUGIN can use this info to center the BOARD, but it does not yet.
+ sprintf( xbuf, "%d", GetPageSizeIU().x );
+ sprintf( ybuf, "%d", GetPageSizeIU().y );
+
+ props["page_width"] = xbuf;
+ props["page_height"] = ybuf;
+
+#if USE_INSTRUMENTATION
+ // measure the time to load a BOARD.
+ unsigned startTime = GetRunningMicroSecs();
+#endif
+
+ loadedBoard = pi->Load( fullFileName, NULL, &props );
+
+#if USE_INSTRUMENTATION
+ unsigned stopTime = GetRunningMicroSecs();
+ printf( "PLUGIN::Load(): %u usecs\n", stopTime - startTime );
+#endif
+ }
+ catch( const IO_ERROR& ioe )
+ {
+ wxString msg = wxString::Format( _(
+ "Error loading board.\n%s" ),
+ GetChars( ioe.errorText )
+ );
+ DisplayError( this, msg );
+ return false;
+ }
+
+ SetBoard( loadedBoard );
+
+ // we should not ask PLUGINs to do these items:
+ loadedBoard->BuildListOfNets();
+ loadedBoard->SynchronizeNetsAndNetClasses();
+
+ SetStatusText( wxEmptyString );
+ BestZoom();
+
+ // update the layer names in the listbox
+ ReCreateLayerBox( false );
+
+ GetScreen()->ClrModify();
+
+ {
+ wxFileName fn = fullFileName;
+ CheckForAutoSaveFile( fullFileName, fn.GetExt() );
+ }
+
+ if( pluginType == IO_MGR::LEGACY &&
+ loadedBoard->GetFileFormatVersionAtLoad() < LEGACY_BOARD_FILE_VERSION )
+ {
+ DisplayInfoMessage( this,
+ _( "This file was created by an older version of Pcbnew.\n"
+ "It will be stored in the new file format when you save this file again." ) );
+ }
+ }
+
+ {
+ wxFileName fn = fullFileName;
+
+ if( converted )
+ fn.SetExt( PcbFileExtension );
+
+ wxString fname = fn.GetFullPath();
+
+ fname.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP );
+
+ GetBoard()->SetFileName( fname );
+ }
+
+ UpdateTitle();
+
+ if( !converted )
+ UpdateFileHistory( GetBoard()->GetFileName() );
+
+ // Rebuild the new pad list (for drc and ratsnet control ...)
+ GetBoard()->m_Status_Pcb = 0;
+
+ // Update current netclass:NETCLASS::Default alwaysxists
+ SetCurrentNetClass( NETCLASS::Default );
+
+ // Rebuild list of nets (full ratsnest rebuild)
+ {
+ wxBusyCursor dummy; // Displays an Hourglass while building connectivity
+ Compile_Ratsnest( NULL, true );
+ GetBoard()->GetRatsnest()->ProcessBoard();
+ }
+
+ // Update info shown by the horizontal toolbars
+ ReFillLayerWidget();
+ ReCreateLayerBox();
+
+ // upate the layer widget to match board visibility states, both layers and render columns.
+ syncLayerVisibilities();
+ syncLayerWidgetLayer();
+ syncRenderStates();
+
+ // Update the tracks / vias available sizes list:
+ ReCreateAuxiliaryToolbar();
+
+ // Update the RATSNEST items, which were not loaded at the time
+ // BOARD::SetVisibleElements() was called from within any PLUGIN.
+ // See case RATSNEST_VISIBLE: in BOARD::SetElementVisibility()
+ GetBoard()->SetVisibleElements( GetBoard()->GetVisibleElements() );
+
+ // Display the loaded board:
+ Zoom_Automatique( false );
+
+ SetMsgPanel( GetBoard() );
+
+ // Refresh the 3D view, if any
+ if( m_Draw3DFrame )
+ m_Draw3DFrame->NewDisplay();
+
+#if 0 && defined(DEBUG)
+ // Output the board object tree to stdout, but please run from command prompt:
+ GetBoard()->Show( 0, std::cout );
+#endif
+
+ // from EDA_APPL which was first loaded BOARD only:
+ {
+ /* For an obscure reason the focus is lost after loading a board file
+ * when starting up the process.
+ * (seems due to the recreation of the layer manager after loading the file)
+ * Give focus to main window and Drawpanel
+ * must be done for these 2 windows (for an obscure reason ...)
+ * Linux specific
+ * This is more a workaround than a fix.
+ */
+ SetFocus();
+ GetCanvas()->SetFocus();
+ }
+
+ return true;
+}
+
+
+static wxString create_backup_file( const wxString& aFileName )
+{
+ wxFileName fn = aFileName;
+ wxFileName backupFileName = aFileName;
+
+ backupFileName.SetExt( fn.GetExt() + backupSuffix );
+
+ // If an old backup file exists, delete it. If an old board file exists,
+ // rename it to the backup file name.
+ if( fn.FileExists() )
+ {
+ // Remove the old file xxx.000 if it exists.
+ if( backupFileName.FileExists() )
+ wxRemoveFile( backupFileName.GetFullPath() );
+
+ // Rename the current file from <xxx>.kicad_pcb to <xxx>.kicad_pcb-bak
+ if( !wxRenameFile( fn.GetFullPath(), backupFileName.GetFullPath() ) )
+ {
+ wxString msg = wxString::Format( _(
+ "Warning: unable to create backup file '%s'" ),
+ GetChars( backupFileName.GetFullPath() )
+ );
+ DisplayError( NULL, msg );
+ }
+ }
+ else
+ {
+ backupFileName.Clear();
+ }
+
+ return backupFileName.GetFullPath();
+}
+
+
+bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupFile )
+{
+ // please, keep it simple. prompting goes elsewhere.
+
+ wxFileName pcbFileName = aFileName;
+
+ if( pcbFileName.GetExt() == LegacyPcbFileExtension )
+ pcbFileName.SetExt( KiCadPcbFileExtension );
+
+ if( !IsWritable( pcbFileName ) )
+ {
+ wxString msg = wxString::Format( _(
+ "No access rights to write to file '%s'" ),
+ GetChars( pcbFileName.GetFullPath() )
+ );
+
+ DisplayError( this, msg );
+ return false;
+ }
+
+ wxString backupFileName;
+
+ // aCreateBackupFile == false is mainly used to write autosave files
+ // or new files in save as... command
+ if( aCreateBackupFile )
+ {
+ backupFileName = create_backup_file( aFileName );
+ }
+
+ GetBoard()->m_Status_Pcb &= ~CONNEXION_OK;
+
+ GetBoard()->SynchronizeNetsAndNetClasses();
+
+ // Select default Netclass before writing file.
+ // Useful to save default values in headers
+ SetCurrentNetClass( NETCLASS::Default );
+
+ ClearMsgPanel();
+
+ wxString upperTxt;
+ wxString lowerTxt;
+
+ try
+ {
+ PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD ) );
+
+ wxASSERT( pcbFileName.IsAbsolute() );
+
+ pi->Save( pcbFileName.GetFullPath(), GetBoard(), NULL );
+ }
+ catch( const IO_ERROR& ioe )
+ {
+ wxString msg = wxString::Format( _(
+ "Error saving board file '%s'.\n%s" ),
+ GetChars( pcbFileName.GetFullPath() ),
+ GetChars( ioe.errorText )
+ );
+ DisplayError( this, msg );
+
+ lowerTxt.Printf( _( "Failed to create '%s'" ), GetChars( pcbFileName.GetFullPath() ) );
+
+ AppendMsgPanel( upperTxt, lowerTxt, CYAN );
+
+ return false;
+ }
+
+ GetBoard()->SetFileName( pcbFileName.GetFullPath() );
+ UpdateTitle();
+
+ // Put the saved file in File History, unless aCreateBackupFile
+ // is false.
+ // aCreateBackupFile == false is mainly used to write autosave files
+ // and not need to have an autosave file in file history
+ if( aCreateBackupFile )
+ UpdateFileHistory( GetBoard()->GetFileName() );
+
+ // Delete auto save file on successful save.
+ wxFileName autoSaveFileName = pcbFileName;
+
+ autoSaveFileName.SetName( wxString( autosavePrefix ) + pcbFileName.GetName() );
+
+ if( autoSaveFileName.FileExists() )
+ wxRemoveFile( autoSaveFileName.GetFullPath() );
+
+ if( !!backupFileName )
+ upperTxt.Printf( _( "Backup file: '%s'" ), GetChars( backupFileName ) );
+
+ lowerTxt.Printf( _( "Wrote board file: '%s'" ), GetChars( pcbFileName.GetFullPath() ) );
+
+ AppendMsgPanel( upperTxt, lowerTxt, CYAN );
+
+ GetScreen()->ClrModify();
+ GetScreen()->ClrSave();
+ return true;
+}
+
+
+bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName )
+{
+ wxFileName pcbFileName = aFileName;
+
+ // Ensure the file ext is the right ext:
+ pcbFileName.SetExt( KiCadPcbFileExtension );
+
+ if( !IsWritable( pcbFileName ) )
+ {
+ wxString msg = wxString::Format( _(
+ "No access rights to write to file '%s'" ),
+ GetChars( pcbFileName.GetFullPath() )
+ );
+
+ DisplayError( this, msg );
+ return false;
+ }
+
+ GetBoard()->m_Status_Pcb &= ~CONNEXION_OK;
+ GetBoard()->SynchronizeNetsAndNetClasses();
+
+ // Select default Netclass before writing file.
+ // Useful to save default values in headers
+ SetCurrentNetClass( NETCLASS::Default );
+
+ try
+ {
+ PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD ) );
+
+ wxASSERT( pcbFileName.IsAbsolute() );
+
+ pi->Save( pcbFileName.GetFullPath(), GetBoard(), NULL );
+ }
+ catch( const IO_ERROR& ioe )
+ {
+ wxString msg = wxString::Format( _(
+ "Error saving board file '%s'.\n%s" ),
+ GetChars( pcbFileName.GetFullPath() ),
+ GetChars( ioe.errorText )
+ );
+ DisplayError( this, msg );
+
+ return false;
+ }
+
+ DisplayInfoMessage( this, wxString::Format( _( "Board copied to:\n'%s'" ),
+ GetChars( pcbFileName.GetFullPath() ) ) );
+
+ return true;
+}
+
+
+bool PCB_EDIT_FRAME::doAutoSave()
+{
+ wxFileName tmpFileName;
+
+ if( GetBoard()->GetFileName().IsEmpty() )
+ {
+ tmpFileName = wxFileName( wxStandardPaths::Get().GetDocumentsDir(), wxT( "noname" ),
+ KiCadPcbFileExtension );
+ GetBoard()->SetFileName( tmpFileName.GetFullPath() );
+ }
+ else
+ {
+ tmpFileName = Prj().AbsolutePath( GetBoard()->GetFileName() );
+ }
+
+ wxFileName autoSaveFileName = tmpFileName;
+
+ // Auto save file name is the board file name prepended with autosaveFilePrefix string.
+ autoSaveFileName.SetName( wxString( autosavePrefix ) + autoSaveFileName.GetName() );
+
+ if( !autoSaveFileName.IsOk() )
+ return false;
+
+ // If the board file path is not writable, try writing to a platform specific temp file
+ // path. If that path isn't writabe, give up.
+ if( !autoSaveFileName.IsDirWritable() )
+ {
+ autoSaveFileName.SetPath( wxFileName::GetTempDir() );
+
+ if( !autoSaveFileName.IsOk() || !autoSaveFileName.IsDirWritable() )
+ return false;
+ }
+
+ wxLogTrace( traceAutoSave, "Creating auto save file <" + autoSaveFileName.GetFullPath() + ">" );
+
+ if( SavePcbFile( autoSaveFileName.GetFullPath(), NO_BACKUP_FILE ) )
+ {
+ GetScreen()->SetModify();
+ GetBoard()->SetFileName( tmpFileName.GetFullPath() );
+ UpdateTitle();
+ m_autoSaveState = false;
+ return true;
+ }
+
+ GetBoard()->SetFileName( tmpFileName.GetFullPath() );
+
+ return false;
+}