KiCad PCB EDA Suite
pcbnew/files.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 2016-2018 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <fctsys.h>
27 #include <confirm.h>
28 #include <kicad_string.h>
29 #include <gestfich.h>
30 #include <pcb_edit_frame.h>
31 #include <macros.h>
33 #include <richio.h>
34 #include <filter_reader.h>
35 #include <pgm_base.h>
36 #include <msgpanel.h>
37 #include <fp_lib_table.h>
38 #include <ratsnest_data.h>
39 #include <kiway.h>
40 #include <kiway_player.h>
41 #include <trace_helpers.h>
42 #include <lockfile.cpp>
44 #include <pcbnew.h>
45 #include <pcbnew_id.h>
46 #include <io_mgr.h>
48 
49 #include <class_board.h>
50 #include <build_version.h> // LEGACY_BOARD_FILE_VERSION
51 
52 #include <wx/stdpaths.h>
53 #include <pcb_layer_widget.h>
54 #include <wx/wupdlock.h>
55 
56 
57 //#define USE_INSTRUMENTATION 1
58 #define USE_INSTRUMENTATION 0
59 
60 
73 bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName, bool aKicadFilesOnly )
74 {
75  // This is a subset of all PLUGINs which are trusted to be able to
76  // load a BOARD. User may occasionally use the wrong plugin to load a
77  // *.brd file (since both legacy and eagle use *.brd extension),
78  // but eventually *.kicad_pcb will be more common than legacy *.brd files.
79  static const struct
80  {
81  const wxString& filter;
82  IO_MGR::PCB_FILE_T pluginType;
83  } loaders[] =
84  {
85  { PcbFileWildcard(), IO_MGR::KICAD_SEXP }, // Current Kicad board files
86  { LegacyPcbFileWildcard(), IO_MGR::LEGACY }, // Old Kicad board files
87  { EaglePcbFileWildcard(), IO_MGR::EAGLE }, // Import board files
88  { PCadPcbFileWildcard(), IO_MGR::PCAD }, // Import board files
89  };
90 
91  wxFileName fileName( *aFileName );
92  wxString fileFilters;
93 
94  if( aKicadFilesOnly )
95  {
96  for( unsigned ii = 0; ii < 2; ++ii )
97  {
98  if( !fileFilters.IsEmpty() )
99  fileFilters += wxChar( '|' );
100 
101  fileFilters += wxGetTranslation( loaders[ii].filter );
102  }
103  }
104  else
105  {
106  for( unsigned ii = 2; ii < arrayDim( loaders ); ++ii )
107  {
108  if( !fileFilters.IsEmpty() )
109  fileFilters += wxChar( '|' );
110 
111  fileFilters += wxGetTranslation( loaders[ii].filter );
112  }
113  }
114 
115  wxString path;
116  wxString name;
117 
118  if( fileName.FileExists() )
119  {
120  path = fileName.GetPath();
121  name = fileName.GetFullName();
122  }
123  else
124  {
125  path = wxStandardPaths::Get().GetDocumentsDir();
126  // leave name empty
127  }
128 
129  wxFileDialog dlg( aParent,
130  aKicadFilesOnly ? _( "Open Board File" ) : _( "Import Non KiCad Board File" ),
131  path, name, fileFilters,
132  wxFD_OPEN | wxFD_FILE_MUST_EXIST );
133 
134  if( dlg.ShowModal() == wxID_OK )
135  {
136  // For import option, if Eagle (*.brd files), tell OpenProjectFiles() to use Eagle plugin.
137  // It's the only special case because of the duplicate use of the *.brd file extension.
138  // Other cases are clear because of unique file extensions.
139  *aCtl = aKicadFilesOnly ? 0 : KICTL_EAGLE_BRD;
140  *aFileName = dlg.GetPath();
141  return true;
142  }
143  else
144  return false;
145 }
146 
147 
158 bool AskSaveBoardFileName( wxWindow* aParent, wxString* aFileName )
159 {
160  wxString wildcard = PcbFileWildcard();
161  wxFileName fn = *aFileName;
162 
163  fn.SetExt( KiCadPcbFileExtension );
164 
165  wxFileDialog dlg( aParent,
166  _( "Save Board File As" ),
167  fn.GetPath(),
168  fn.GetFullName(),
169  wildcard,
170  wxFD_SAVE | wxFD_OVERWRITE_PROMPT
171  );
172 
173  if( dlg.ShowModal() != wxID_OK )
174  return false;
175 
176  fn = dlg.GetPath();
177 
178  // always enforce filename extension, user may not have entered it.
179  fn.SetExt( KiCadPcbFileExtension );
180 
181  *aFileName = fn.GetFullPath();
182 
183  return true;
184 }
185 
186 
187 void PCB_EDIT_FRAME::OnFileHistory( wxCommandEvent& event )
188 {
189  wxString fn = GetFileFromHistory( event.GetId(), _( "Printed circuit board" ) );
190 
191  if( !!fn )
192  {
193  int open_ctl = 0;
194 
195  if( !wxFileName::IsFileReadable( fn ) )
196  {
197  if( !AskLoadBoardFileName( this, &open_ctl, &fn, true ) )
198  return;
199  }
200 
201  OpenProjectFiles( std::vector<wxString>( 1, fn ), open_ctl );
202  }
203 }
204 
205 void PCB_EDIT_FRAME::OnClearFileHistory( wxCommandEvent& aEvent )
206 {
208 }
209 
210 
211 void PCB_EDIT_FRAME::Files_io( wxCommandEvent& event )
212 {
213  int id = event.GetId();
214  Files_io_from_id( id );
215 }
216 
217 
219 {
220  wxString msg;
221 
222  switch( id )
223  {
224  case ID_LOAD_FILE:
225  {
226  int open_ctl = 0;
227  wxString fileName = Prj().AbsolutePath( GetBoard()->GetFileName() );
228 
229  return AskLoadBoardFileName( this, &open_ctl, &fileName, true )
230  && OpenProjectFiles( std::vector<wxString>( 1, fileName ), open_ctl );
231  }
232 
234  {
235  int open_ctl = 1;
236  wxString fileName; // = Prj().AbsolutePath( GetBoard()->GetFileName() );
237 
238  return AskLoadBoardFileName( this, &open_ctl, &fileName, false )
239  && OpenProjectFiles( std::vector<wxString>( 1, fileName ), open_ctl );
240  }
241 
244  {
245  wxFileName currfn = Prj().AbsolutePath( GetBoard()->GetFileName() );
246  wxFileName fn = currfn;
247 
249  {
250  wxString rec_name = GetAutoSaveFilePrefix() + fn.GetName();
251  fn.SetName( rec_name );
252  }
253  else
254  {
255  wxString backup_ext = fn.GetExt() + GetBackupSuffix();
256  fn.SetExt( backup_ext );
257  }
258 
259  if( !fn.FileExists() )
260  {
261  msg.Printf( _( "Recovery file \"%s\" not found." ), fn.GetFullPath() );
262  DisplayInfoMessage( this, msg );
263  return false;
264  }
265 
266  msg.Printf( _( "OK to load recovery or backup file \"%s\"" ), fn.GetFullPath() );
267 
268  if( !IsOK( this, msg ) )
269  return false;
270 
271  GetScreen()->ClrModify(); // do not prompt the user for changes
272 
273  if( OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ) ) )
274  {
275  // Re-set the name since name or extension was changed
276  GetBoard()->SetFileName( currfn.GetFullPath() );
277  UpdateTitle();
278  return true;
279  }
280  return false;
281  }
282 
283  case ID_NEW_BOARD:
284  {
285  if( IsContentModified() )
286  {
287  wxFileName fileName = GetBoard()->GetFileName();
288  wxString saveMsg =
289  _( "Current board will be closed, save changes to \"%s\" before continuing?" );
290 
291  if( !HandleUnsavedChanges( this, wxString::Format( saveMsg, fileName.GetFullName() ),
292  [&]()->bool { return Files_io_from_id( ID_SAVE_BOARD ); } ) )
293  return false;
294  }
295  else if( !GetBoard()->IsEmpty() )
296  {
297  if( !IsOK( this, _( "Current Board will be closed. Continue?" ) ) )
298  return false;
299  }
300 
301  if( !Clear_Pcb( false ) )
302  return false;
303 
304  wxFileName fn( wxStandardPaths::Get().GetDocumentsDir(), wxT( "noname" ),
306 
307  Prj().SetProjectFullName( fn.GetFullPath() );
308 
309  onBoardLoaded();
310 
311  OnModify();
312  return true;
313  }
314 
315  case ID_SAVE_BOARD:
316  if( !GetBoard()->GetFileName().IsEmpty() )
317  return SavePcbFile( Prj().AbsolutePath( GetBoard()->GetFileName() ) );
318  // Fall through
319 
320  case ID_COPY_BOARD_AS:
321  case ID_SAVE_BOARD_AS:
322  {
323  wxString orig_name;
324  wxFileName::SplitPath( GetBoard()->GetFileName(),
325  nullptr, nullptr, &orig_name, nullptr );
326 
327  if( orig_name.IsEmpty() )
328  orig_name = _( "noname" );
329 
330  wxString pro_dir = wxPathOnly( Prj().GetProjectFullName() );
331  wxFileName fn( pro_dir, orig_name, KiCadPcbFileExtension );
332  wxString filename = fn.GetFullPath();
333 
334  if( AskSaveBoardFileName( this, &filename ) )
335  {
336  if( id == ID_COPY_BOARD_AS )
337  return SavePcbCopy( filename );
338  else
339  return SavePcbFile( filename, NO_BACKUP_FILE );
340  }
341  return false;
342  }
343 
344  default:
345  wxLogDebug( wxT( "File_io Internal Error" ) );
346  return false;
347  }
348 }
349 
350 
351 // The KIWAY_PLAYER::OpenProjectFiles() API knows nothing about plugins, so
352 // determine how to load the BOARD here, with minor assistance from KICTL_EAGLE_BRD
353 // bit flag.
354 IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl )
355 {
356  IO_MGR::PCB_FILE_T pluginType;
357 
358  wxFileName fn = aFileName;
359 
360  // Note: file extensions are expected to be in ower case.
361  // This is not always true, especially when importing files, so the string
362  // comparisons are case insensitive to try to find the suitable plugin.
363 
364  if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::LEGACY ) ) == 0 )
365  {
366  // both legacy and eagle share a common file extension.
367  pluginType = ( aCtl & KICTL_EAGLE_BRD ) ? IO_MGR::EAGLE : IO_MGR::LEGACY;
368  }
369  else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::PCAD ) ) == 0 )
370  {
371  pluginType = IO_MGR::PCAD;
372  }
373  else
374  {
375  pluginType = IO_MGR::KICAD_SEXP;
376  }
377 
378  return pluginType;
379 }
380 
381 
383 {
384  PCB_LAYER_COLLECTOR collector;
385 
386  collector.SetLayerId( Edge_Cuts );
387  collector.Collect( aBoard, GENERAL_COLLECTOR::AllBoardItems );
388 
389  int edgeWidth = -1;
390  bool mixed = false;
391 
392  for( int i = 0; i < collector.GetCount(); i++ )
393  {
394  if( collector[i]->Type() == PCB_LINE_T )
395  {
396  int itemWidth = static_cast<DRAWSEGMENT*>( collector[i] )->GetWidth();
397 
398  if( edgeWidth != -1 && edgeWidth != itemWidth )
399  {
400  mixed = true;
401  edgeWidth = std::max( edgeWidth, itemWidth );
402  }
403  else
404  {
405  edgeWidth = itemWidth;
406  }
407  }
408  }
409 
410  if( mixed )
411  {
412  // If they had different widths then we can't ensure that fills will be the same.
413  wxMessageBox( _( "If the zones on this board are refilled the Copper Edge Clearance\n"
414  "setting will be used (see Board Setup > Design Rules). This may\n"
415  "result in different fills from previous Kicad versions which used\n"
416  "the line thickness of the board boundary on the Edge Cuts layer." ),
417  _( "Edge Clearance Warning" ), wxOK|wxICON_WARNING, this );
418  }
419 
420  return std::max( 0, edgeWidth / 2 );
421 }
422 
423 
424 bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
425 {
426  // This is for python:
427  if( aFileSet.size() != 1 )
428  {
429  UTF8 msg = StrPrintf( "Pcbnew:%s() takes only a single filename", __func__ );
430  DisplayError( this, msg );
431  return false;
432  }
433 
434  wxString fullFileName( aFileSet[0] );
435 
436  // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
437  wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(), wxT( "Path is not absolute!" ) );
438 
439  std::unique_ptr<wxSingleInstanceChecker> lockFile = ::LockFile( fullFileName );
440 
441  if( !lockFile )
442  {
443  wxString msg = wxString::Format( _( "PCB file \"%s\" is already open." ), fullFileName );
444  DisplayError( this, msg );
445  return false;
446  }
447 
448  if( IsContentModified() )
449  {
450  if( !HandleUnsavedChanges( this, _( "The current PCB has been modified. Save changes?" ),
451  [&]()->bool { return SavePcbFile( GetBoard()->GetFileName(), CREATE_BACKUP_FILE ); } ) )
452  {
453  return false;
454  }
455  }
456 
457  // Release the lock file, until the new file is actually loaded
458  ReleaseFile();
459 
460  wxFileName pro = fullFileName;
461  pro.SetExt( ProjectFileExtension );
462 
463  bool is_new = !wxFileName::IsFileReadable( fullFileName );
464 
465  // If its a non-existent schematic and caller thinks it exists
466  if( is_new && !( aCtl & KICTL_CREATE ) )
467  {
468  // notify user that fullFileName does not exist, ask if user wants to create it.
469  wxString ask = wxString::Format( _( "PCB \"%s\" does not exist. Do you wish to create it?" ),
470  fullFileName );
471  if( !IsOK( this, ask ) )
472  return false;
473  }
474 
475  wxWindowUpdateLocker no_update( m_Layers ); // Avoid flicker when rebuilding m_Layers
476 
477  Clear_Pcb( false ); // pass false since we prompted above for a modified board
478 
479  IO_MGR::PCB_FILE_T pluginType = plugin_type( fullFileName, aCtl );
480 
481  bool converted = pluginType != IO_MGR::LEGACY && pluginType != IO_MGR::KICAD_SEXP;
482 
483  if( !converted )
484  {
485  // PROJECT::SetProjectFullName() is an impactful function. It should only be
486  // called under carefully considered circumstances.
487 
488  // The calling code should know not to ask me here to change projects unless
489  // it knows what consequences that will have on other KIFACEs running and using
490  // this same PROJECT. It can be very harmful if that calling code is stupid.
491  Prj().SetProjectFullName( pro.GetFullPath() );
492 
493  // load project settings before BOARD
495  }
496 
497  if( is_new )
498  {
499  OnModify();
500  }
501  else
502  {
503  BOARD* loadedBoard = 0; // it will be set to non-NULL if loaded OK
504 
505  PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) );
506 
507  // This will rename the file if there is an autosave and the user want to recover
508  CheckForAutoSaveFile( fullFileName );
509 
510  try
511  {
512  PROPERTIES props;
513  char xbuf[30];
514  char ybuf[30];
515 
516  // EAGLE_PLUGIN can use this info to center the BOARD, but it does not yet.
517  sprintf( xbuf, "%d", GetPageSizeIU().x );
518  sprintf( ybuf, "%d", GetPageSizeIU().y );
519 
520  props["page_width"] = xbuf;
521  props["page_height"] = ybuf;
522 
523 #if USE_INSTRUMENTATION
524  // measure the time to load a BOARD.
525  unsigned startTime = GetRunningMicroSecs();
526 #endif
527 
528  loadedBoard = pi->Load( fullFileName, NULL, &props );
529 
530 #if USE_INSTRUMENTATION
531  unsigned stopTime = GetRunningMicroSecs();
532  printf( "PLUGIN::Load(): %u usecs\n", stopTime - startTime );
533 #endif
534  }
535  catch( const IO_ERROR& ioe )
536  {
537  if( ioe.Problem() != wxT( "CANCEL" ) )
538  {
539  wxString msg = wxString::Format( _( "Error loading board file:\n%s" ), fullFileName );
540  DisplayErrorMessage( this, msg, ioe.What() );
541  }
542 
543  return false;
544  }
545 
546  BOARD_DESIGN_SETTINGS& bds = loadedBoard->m_designSettings;
547 
548  if( bds.m_CopperEdgeClearance == Millimeter2iu( LEGACY_COPPEREDGECLEARANCE ) )
549  {
550  // 5.1 boards stored some settings in the config so as not to bump the file version.
551  // These will have been loaded into the config-initialized board, so we copy them
552  // from there.
554 
555  bds.m_DRCSeverities = configBds.m_DRCSeverities;
556  bds.m_HoleToHoleMin = configBds.m_HoleToHoleMin;
560  std::copy( configBds.m_TextItalic, configBds.m_TextItalic + 4, bds.m_TextItalic );
561  std::copy( configBds.m_TextUpright, configBds.m_TextUpright + 4, bds.m_TextUpright );
564 
565  // Before we had a copper edge clearance setting, the edge line widths could be used
566  // as a kludge to control them. So if there's no setting then infer it from the
567  // edge widths.
568  if( bds.m_CopperEdgeClearance == Millimeter2iu( LEGACY_COPPEREDGECLEARANCE ) )
569  bds.SetCopperEdgeClearance( inferLegacyEdgeClearance( loadedBoard ) );
570  }
571 
572  // 6.0 TODO: some of the 5.1 settings still haven't moved because they're waiting on
573  // the new DRC architecture
575  bds.m_DRCSeverities = configBds.m_DRCSeverities;
576  bds.m_HoleToHoleMin = configBds.m_HoleToHoleMin;
577 
578  SetBoard( loadedBoard );
579 
580  // we should not ask PLUGINs to do these items:
581  loadedBoard->BuildListOfNets();
582  loadedBoard->SynchronizeNetsAndNetClasses();
584 
585  if( loadedBoard->IsModified() )
586  OnModify();
587  else
588  GetScreen()->ClrModify();
589 
590  if( pluginType == IO_MGR::LEGACY &&
591  loadedBoard->GetFileFormatVersionAtLoad() < LEGACY_BOARD_FILE_VERSION )
592  {
593  DisplayInfoMessage( this,
594  _( "This file was created by an older version of Pcbnew.\n"
595  "It will be stored in the new file format when you save this file again." ) );
596  }
597  }
598 
599  {
600  wxFileName fn = fullFileName;
601 
602  if( converted )
603  fn.SetExt( PcbFileExtension );
604 
605  wxString fname = fn.GetFullPath();
606 
607  fname.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP );
608 
609  GetBoard()->SetFileName( fname );
610  }
611 
612  // Lock the file newly opened:
613  m_file_checker.reset( lockFile.release() );
614 
615  if( !converted )
616  UpdateFileHistory( GetBoard()->GetFileName() );
617 
618  // Select netclass Default as current netclass (it always exists)
620 
621  // Rebuild list of nets (full ratsnest rebuild)
622  Compile_Ratsnest( true );
624 
625  onBoardLoaded();
626 
627  // Refresh the 3D view, if any
628  EDA_3D_VIEWER* draw3DFrame = Get3DViewerFrame();
629 
630  if( draw3DFrame )
631  draw3DFrame->NewDisplay();
632 
633 #if 0 && defined(DEBUG)
634  // Output the board object tree to stdout, but please run from command prompt:
635  GetBoard()->Show( 0, std::cout );
636 #endif
637 
638  // from EDA_APPL which was first loaded BOARD only:
639  {
640  /* For an obscure reason the focus is lost after loading a board file
641  * when starting up the process.
642  * (seems due to the recreation of the layer manager after loading the file)
643  * Give focus to main window and Drawpanel
644  * must be done for these 2 windows (for an obscure reason ...)
645  * Linux specific
646  * This is more a workaround than a fix.
647  */
648  SetFocus();
649  GetCanvas()->SetFocus();
650  }
651 
652  return true;
653 }
654 
655 
656 wxString PCB_EDIT_FRAME::createBackupFile( const wxString& aFileName )
657 {
658  wxFileName fn = aFileName;
659  wxFileName backupFileName = aFileName;
660 
661  backupFileName.SetExt( fn.GetExt() + GetBackupSuffix() );
662 
663  // If an old backup file exists, delete it. If an old board file exists,
664  // rename it to the backup file name.
665  if( fn.FileExists() )
666  {
667  // Remove the old file xxx.000 if it exists.
668  if( backupFileName.FileExists() )
669  wxRemoveFile( backupFileName.GetFullPath() );
670 
671  // Rename the current file from <xxx>.kicad_pcb to <xxx>.kicad_pcb-bak
672  if( !wxRenameFile( fn.GetFullPath(), backupFileName.GetFullPath() ) )
673  {
674  wxString msg = wxString::Format( _(
675  "Warning: unable to create backup file \"%s\"" ),
676  GetChars( backupFileName.GetFullPath() )
677  );
678  DisplayError( NULL, msg );
679  }
680  }
681  else
682  {
683  backupFileName.Clear();
684  }
685 
686  return backupFileName.GetFullPath();
687 }
688 
689 
690 bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupFile )
691 {
692  // please, keep it simple. prompting goes elsewhere.
693 
694  wxFileName pcbFileName = aFileName;
695 
696  if( pcbFileName.GetExt() == LegacyPcbFileExtension )
697  pcbFileName.SetExt( KiCadPcbFileExtension );
698 
699  if( !IsWritable( pcbFileName ) )
700  {
701  wxString msg = wxString::Format( _(
702  "No access rights to write to file \"%s\"" ),
703  GetChars( pcbFileName.GetFullPath() )
704  );
705 
706  DisplayError( this, msg );
707  return false;
708  }
709 
710  wxString backupFileName;
711 
712  if( aCreateBackupFile )
713  {
714  backupFileName = createBackupFile( aFileName );
715  }
716 
718 
719  // Select default Netclass before writing file.
720  // Useful to save default values in headers
722 
723  ClearMsgPanel();
724 
725  wxString upperTxt;
726  wxString lowerTxt;
727 
728  try
729  {
731 
732  wxASSERT( pcbFileName.IsAbsolute() );
733 
734  pi->Save( pcbFileName.GetFullPath(), GetBoard(), NULL );
735  }
736  catch( const IO_ERROR& ioe )
737  {
738  wxString msg = wxString::Format( _(
739  "Error saving board file \"%s\".\n%s" ),
740  GetChars( pcbFileName.GetFullPath() ),
741  GetChars( ioe.What() )
742  );
743  DisplayError( this, msg );
744 
745  lowerTxt.Printf( _( "Failed to create \"%s\"" ), GetChars( pcbFileName.GetFullPath() ) );
746 
747  AppendMsgPanel( upperTxt, lowerTxt, CYAN );
748 
749  return false;
750  }
751 
752  GetBoard()->SetFileName( pcbFileName.GetFullPath() );
753  UpdateTitle();
754 
755  // Put the saved file in File History, unless aCreateBackupFile
756  // is false.
757  // aCreateBackupFile == false is mainly used to write autosave files
758  // and not need to have an autosave file in file history
759  if( aCreateBackupFile )
760  UpdateFileHistory( GetBoard()->GetFileName() );
761 
762  // Delete auto save file on successful save.
763  wxFileName autoSaveFileName = pcbFileName;
764 
765  autoSaveFileName.SetName( GetAutoSaveFilePrefix() + pcbFileName.GetName() );
766 
767  if( autoSaveFileName.FileExists() )
768  wxRemoveFile( autoSaveFileName.GetFullPath() );
769 
770  if( !!backupFileName )
771  upperTxt.Printf( _( "Backup file: \"%s\"" ), GetChars( backupFileName ) );
772 
773  lowerTxt.Printf( _( "Wrote board file: \"%s\"" ), GetChars( pcbFileName.GetFullPath() ) );
774 
775  AppendMsgPanel( upperTxt, lowerTxt, CYAN );
776 
777  GetScreen()->ClrModify();
778  GetScreen()->ClrSave();
779  return true;
780 }
781 
782 
783 bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName )
784 {
785  wxFileName pcbFileName = aFileName;
786 
787  // Ensure the file ext is the right ext:
788  pcbFileName.SetExt( KiCadPcbFileExtension );
789 
790  if( !IsWritable( pcbFileName ) )
791  {
792  wxString msg = wxString::Format( _(
793  "No access rights to write to file \"%s\"" ),
794  GetChars( pcbFileName.GetFullPath() )
795  );
796 
797  DisplayError( this, msg );
798  return false;
799  }
800 
802 
803  // Select default Netclass before writing file.
804  // Useful to save default values in headers
806 
807  try
808  {
810 
811  wxASSERT( pcbFileName.IsAbsolute() );
812 
813  pi->Save( pcbFileName.GetFullPath(), GetBoard(), NULL );
814  }
815  catch( const IO_ERROR& ioe )
816  {
817  wxString msg = wxString::Format( _(
818  "Error saving board file \"%s\".\n%s" ),
819  GetChars( pcbFileName.GetFullPath() ),
820  GetChars( ioe.What() )
821  );
822  DisplayError( this, msg );
823 
824  return false;
825  }
826 
827  DisplayInfoMessage( this, wxString::Format( _( "Board copied to:\n\"%s\"" ),
828  GetChars( pcbFileName.GetFullPath() ) ) );
829 
830  return true;
831 }
832 
833 
835 {
836  wxFileName tmpFileName;
837 
838  if( GetBoard()->GetFileName().IsEmpty() )
839  {
840  tmpFileName = wxFileName( wxStandardPaths::Get().GetDocumentsDir(), wxT( "noname" ),
842  GetBoard()->SetFileName( tmpFileName.GetFullPath() );
843  }
844  else
845  {
846  tmpFileName = Prj().AbsolutePath( GetBoard()->GetFileName() );
847  }
848 
849  wxFileName autoSaveFileName = tmpFileName;
850 
851  // Auto save file name is the board file name prepended with autosaveFilePrefix string.
852  autoSaveFileName.SetName( GetAutoSaveFilePrefix() + autoSaveFileName.GetName() );
853 
854  if( !autoSaveFileName.IsOk() )
855  return false;
856 
857  // If the board file path is not writable, try writing to a platform specific temp file
858  // path. If that path isn't writabe, give up.
859  if( !autoSaveFileName.IsDirWritable() )
860  {
861  autoSaveFileName.SetPath( wxFileName::GetTempDir() );
862 
863  if( !autoSaveFileName.IsOk() || !autoSaveFileName.IsDirWritable() )
864  return false;
865  }
866 
867  wxLogTrace( traceAutoSave, "Creating auto save file <" + autoSaveFileName.GetFullPath() + ">" );
868 
869  if( SavePcbFile( autoSaveFileName.GetFullPath(), NO_BACKUP_FILE ) )
870  {
871  GetScreen()->SetModify();
872  GetBoard()->SetFileName( tmpFileName.GetFullPath() );
873  UpdateTitle();
874  m_autoSaveState = false;
875  return true;
876  }
877 
878  GetBoard()->SetFileName( tmpFileName.GetFullPath() );
879 
880  return false;
881 }
882 
883 
884 bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType )
885 {
886  switch( (IO_MGR::PCB_FILE_T) aFileType )
887  {
888  case IO_MGR::EAGLE:
889  if( OpenProjectFiles( std::vector<wxString>( 1, aFileName ), KICTL_EAGLE_BRD ) )
890  {
891  wxString projectpath = Kiway().Prj().GetProjectPath();
892  wxFileName newfilename;
893 
894  newfilename.SetPath( Prj().GetProjectPath() );
895  newfilename.SetName( Prj().GetProjectName() );
896  newfilename.SetExt( KiCadPcbFileExtension );
897 
898  GetBoard()->SetFileName( newfilename.GetFullPath() );
899  UpdateTitle();
900  OnModify();
901 
902  // Extract a footprint library from the design and add it to the fp-lib-table
903  wxString newLibPath;
904  ArchiveModulesOnBoard( true, newfilename.GetName(), &newLibPath );
905 
906  if( newLibPath.Length() > 0 )
907  {
908  FP_LIB_TABLE* prjlibtable = Prj().PcbFootprintLibs();
909  const wxString& project_env = PROJECT_VAR_NAME;
910  wxString rel_path, env_path;
911 
912  wxGetEnv( project_env, &env_path );
913 
914  wxString result( newLibPath );
915  rel_path = result.Replace( env_path,
916  wxString( "$(" + project_env + ")" ) ) ? result : "" ;
917 
918  if( !rel_path.IsEmpty() )
919  newLibPath = rel_path;
920 
921  FP_LIB_TABLE_ROW* row = new FP_LIB_TABLE_ROW( newfilename.GetName(),
922  newLibPath, wxT( "KiCad" ), wxEmptyString );
923  prjlibtable->InsertRow( row );
924  }
925 
926  if( !GetBoard()->GetFileName().IsEmpty() )
927  {
928  wxString tblName = Prj().FootprintLibTblName();
929 
930  try
931  {
932  Prj().PcbFootprintLibs()->Save( tblName );
933  }
934  catch( const IO_ERROR& ioe )
935  {
936  wxString msg = wxString::Format( _(
937  "Error occurred saving project specific footprint library "
938  "table:\n\n%s" ),
939  GetChars( ioe.What() ) );
940  wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
941  }
942  }
943 
944 
945  // Update module LIB_IDs to point to the just imported Eagle library
946  for( MODULE* module : GetBoard()->Modules() )
947  {
948  LIB_ID libId = module->GetFPID();
949 
950  if( libId.GetLibItemName().empty() )
951  continue;
952 
953  libId.SetLibNickname( newfilename.GetName() );
954  module->SetFPID( libId );
955  }
956 
957 
958  // Store net names for all pads, to create net remap information
959  std::unordered_map<D_PAD*, wxString> netMap;
960 
961  for( const auto& pad : GetBoard()->GetPads() )
962  {
963  NETINFO_ITEM* netinfo = pad->GetNet();
964 
965  if( netinfo->GetNet() > 0 && !netinfo->GetNetname().IsEmpty() )
966  netMap[pad] = netinfo->GetNetname();
967  }
968 
969  // Two stage netlist update:
970  // - first, assign valid timestamps to footprints (no reannotation)
971  // - second, perform schematic annotation and update footprint references
972  // based on timestamps
973  NETLIST netlist;
975  DoUpdatePCBFromNetlist( netlist, false );
977  DoUpdatePCBFromNetlist( netlist, true );
978 
979  std::unordered_map<wxString, wxString> netRemap;
980 
981  // Compare the old net names with the new net names and create a net map
982  for( const auto& pad : GetBoard()->GetPads() )
983  {
984  auto it = netMap.find( pad );
985 
986  if( it == netMap.end() )
987  continue;
988 
989  NETINFO_ITEM* netinfo = pad->GetNet();
990 
991  // Net name has changed, create a remap entry
992  if( netinfo->GetNet() > 0 && netMap[pad] != netinfo->GetNetname() )
993  netRemap[netMap[pad]] = netinfo->GetNetname();
994  }
995 
996  if( !netRemap.empty() )
997  fixEagleNets( netRemap );
998 
999  return true;
1000  }
1001 
1002  return false;
1003 
1004  default:
1005  return false;
1006  }
1007 
1008  return false;
1009 }
1010 
1011 
1012 bool PCB_EDIT_FRAME::fixEagleNets( const std::unordered_map<wxString, wxString>& aRemap )
1013 {
1014  bool result = true;
1015  BOARD* board = GetBoard();
1016 
1017  // perform netlist matching to prevent orphaned zones.
1018  for( auto zone : board->Zones() )
1019  {
1020  auto it = aRemap.find( zone->GetNet()->GetNetname() );
1021 
1022  if( it != aRemap.end() )
1023  {
1024  NETINFO_ITEM* net = board->FindNet( it->second );
1025 
1026  if( !net )
1027  {
1028  wxFAIL;
1029  result = false;
1030  continue;
1031  }
1032 
1033  zone->SetNet( net );
1034  }
1035  }
1036 
1037 
1038  // perform netlist matching to prevent orphaned tracks/vias.
1039  for( auto track : board->Tracks() )
1040  {
1041  auto it = aRemap.find( track->GetNet()->GetNetname() );
1042 
1043  if( it != aRemap.end() )
1044  {
1045  NETINFO_ITEM* net = board->FindNet( it->second );
1046 
1047  if( !net )
1048  {
1049  wxFAIL;
1050  result = false;
1051  continue;
1052  }
1053 
1054  track->SetNet( net );
1055  }
1056  }
1057 
1058  return result;
1059 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:239
void UpdateTitle()
Function UpdateTitle sets the main window title bar text.
UTF8 is an 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to...
Definition: utf8.h:73
NETINFO_ITEM * FindNet(int aNetcode) const
Function FindNet searches for a net with the given netcode.
void BuildListOfNets()
Definition: class_board.h:692
void OnModify() override
Function OnModify must be called after a board change to set the modified flag.
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition: confirm.cpp:201
void SetCopperEdgeClearance(int aDistance)
Function SetCopperEdgeClearance.
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_holder.h:56
#define WIN_STRING_DIR_SEP
Definition: gestfich.h:44
wxString EaglePcbFileWildcard()
static const KICAD_T AllBoardItems[]
A scan list for all editable board items.
Definition: collectors.h:267
int StrPrintf(std::string *aResult, const char *aFormat,...)
Function StrPrintf is like sprintf() but the output is appended to a std::string instead of to a char...
Definition: richio.cpp:74
bool importFile(const wxString &aFileName, int aFileType)
Load the given filename but sets the path to the current project path.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:252
This file is part of the common library TODO brief description.
IO_MGR::PCB_FILE_T plugin_type(const wxString &aFileName, int aCtl)
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
bool IsModified() const
Definition: base_struct.h:221
FP_LIB_TABLE_ROW.
Definition: fp_lib_table.h:42
void Compile_Ratsnest(bool aDisplayStatus)
Function Compile_Ratsnest Create the entire board ratsnest.
bool InsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Adds aRow if it does not already exist or if doReplace is true.
This file is part of the common library.
const std::string ProjectFileExtension
const std::string LegacyPcbFileExtension
bool SavePcbCopy(const wxString &aFileName)
Function SavePcbCopy writes the board data structures to a aFileName but unlike SavePcbFile,...
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
#define UNIX_STRING_DIR_SEP
Definition: gestfich.h:43
std::map< int, int > m_DRCSeverities
VTBL_ENTRY PROJECT & Prj() const
Function Prj returns the PROJECT associated with this KIWAY.
Definition: kiway.cpp:171
bool doAutoSave() override
Function doAutoSave performs auto save when the board has been modified and not saved within the auto...
#define KICTL_CREATE
caller thinks requested project files may not exist
Definition: kiway_player.h:79
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
Class that computes missing connections on a PCB.
void ResolveDRCExclusions()
Update markers to match recorded exclusions.
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:38
#define LEGACY_COPPEREDGECLEARANCE
wxString createBackupFile(const wxString &aFileName)
bool LoadProjectSettings()
Load the current project's file configuration settings which are pertinent to this PCB_EDIT_FRAME ins...
void ReleaseFile()
Release the current file marked in use.
void Collect(BOARD_ITEM *aBoard, const KICAD_T aScanList[])
Tests a BOARD_ITEM using this class's Inspector method, which does the collection.
Definition: collectors.cpp:597
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:512
#define KICTL_EAGLE_BRD
chosen *.brd file is Eagle according to user.
Definition: kiway_player.h:78
int inferLegacyEdgeClearance(BOARD *aBoard)
Use the existing edge_cut line thicknesses to infer the edge clearace.
Collect all BOARD_ITEM objects on a given layer.
Definition: collectors.h:650
const std::string KiCadPcbFileExtension
bool IsWritable(const wxFileName &aFileName)
Checks if aFileName can be written.
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
#define NO_BACKUP_FILE
bool SetCurrentNetClass(const wxString &aNetClassName)
Function SetCurrentNetClass Must be called after a netclass selection (or after a netclass parameter ...
PROPERTIES is a name/value tuple with unique names and optional values.
Definition: properties.h:34
const wxString & GetFileName() const
Definition: class_board.h:218
virtual const wxString Problem() const
what was the problem?
Definition: exceptions.cpp:49
void ClearFileHistory(FILE_HISTORY *aFileHistory=NULL)
Removes all files from the file history.
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
bool fixEagleNets(const std::unordered_map< wxString, wxString > &aRemap)
Rematch orphaned zones and vias to schematic nets.
std::unique_ptr< wxSingleInstanceChecker > m_file_checker
prevents opening same file multiple times.
void SetLayerId(PCB_LAYER_ID aLayerId)
Definition: collectors.h:660
VTBL_ENTRY const wxString AbsolutePath(const wxString &aFileName) const
Function AbsolutePath fixes up aFileName if it is relative to the project's directory to be an absolu...
Definition: project.cpp:413
This file contains miscellaneous commonly used macros and functions.
void UpdateFileHistory(const wxString &FullFileName, FILE_HISTORY *aFileHistory=NULL)
Update the list of recently opened files.
RELEASER releases a PLUGIN in the context of a potential thrown exception, through its destructor.
Definition: io_mgr.h:577
wxSize m_TextSize[LAYER_CLASS_COUNT]
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:100
void SynchronizeNetsAndNetClasses()
Function SynchronizeNetsAndNetClasses copies NETCLASS info to each NET, based on NET membership in a ...
Definition: netclass.cpp:155
void OnClearFileHistory(wxCommandEvent &aEvent)
wxString LegacyPcbFileWildcard()
int m_TextThickness[LAYER_CLASS_COUNT]
void CheckForAutoSaveFile(const wxFileName &aFileName)
Check if an auto save file exists for aFileName and takes the appropriate action depending on the use...
NETLIST stores all of information read from a netlist along with the flags used to update the NETLIST...
Definition: pcb_netlist.h:212
#define NULL
bool IsContentModified() override
Get if the current board has been modified but not saved.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
Function OpenProjectFiles (was LoadOnePcbFile) loads a KiCad board (.kicad_pcb) from aFileName.
static wxString GetBackupSuffix()
static const char Default[]
the name of the default NETCLASS
Definition: netclass.h:80
void onBoardLoaded()
Updates the state of the GUI after a new board is loaded or created.
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
bool AskLoadBoardFileName(wxWindow *aParent, int *aCtl, wxString *aFileName, bool aKicadFilesOnly)
Function AskLoadBoardFileName puts up a wxFileDialog asking for a BOARD filename to open.
Definition: colors.h:59
PCB_LAYER_WIDGET * m_Layers
Layer manager. It is the responsibility of the child frames to instantiate this.
bool m_TextItalic[LAYER_CLASS_COUNT]
Definition of file extensions used in Kicad.
const wxString & GetNetname() const
Function GetNetname.
Definition: netinfo.h:232
wxLogTrace helper definitions.
virtual void SetFocus() override
#define PcbFileExtension
void BuildConnectivity()
Builds or rebuilds the board connectivity database for the board, especially the list of connected it...
void SetFileName(const wxString &aFileName)
Definition: class_board.h:216
virtual void ClearMsgPanel()
Clear all messages from the message panel.
VTBL_ENTRY void SetProjectFullName(const wxString &aFullPathAndName)
Function SetProjectFullName sets the: 1) full directory, 2) basename, and 3) extension of the project...
Definition: project.cpp:64
int m_LineThickness[LAYER_CLASS_COUNT]
int GetFileFormatVersionAtLoad() const
Definition: class_board.h:273
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Definition: macros.h:108
VTBL_ENTRY FP_LIB_TABLE * PcbFootprintLibs(KIWAY &aKiway)
Return the table of footprint libraries.
Definition: project.cpp:427
int SetLibNickname(const UTF8 &aNickname)
Override the logical library name portion of the LIB_ID to aNickname.
Definition: lib_id.cpp:193
static wxString GetAutoSaveFilePrefix()
void AppendMsgPanel(const wxString &textUpper, const wxString &textLower, COLOR4D color, int pad=6)
Append a message to the message panel.
#define CREATE_BACKUP_FILE
NETINFO_ITEM handles the data for a net.
Definition: netinfo.h:65
void OnFileHistory(wxCommandEvent &event)
wxString GetFileFromHistory(int cmdId, const wxString &type, FILE_HISTORY *aFileHistory=NULL)
Fetches the file name from the file history list.
ZONE_CONTAINERS & Zones()
Definition: class_board.h:243
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:101
Legacy Pcbnew file formats prior to s-expression.
Definition: io_mgr.h:56
see class PGM_BASE
void SetBoard(BOARD *aBoard) override
Declaration of the eda_3d_viewer class.
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
void DoUpdatePCBFromNetlist(NETLIST &aNetlist, bool aUseTimestamps)
Function DoUpdatePCBFromNetlist An automated version of UpdatePCBFromNetlist which skips the UI dialo...
void ArchiveModulesOnBoard(bool aStoreInNewLib, const wxString &aLibName=wxEmptyString, wxString *aLibPath=NULL)
Function ArchiveModulesOnBoard Save modules in a library:
int GetNet() const
Function GetNet.
Definition: netinfo.h:224
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:163
void Files_io(wxCommandEvent &event)
Function Files_io.
#define _(s)
Definition: 3d_actions.cpp:33
unsigned GetRunningMicroSecs()
Function GetRunningMicroSecs An alternate way to calculate an elapset time (in microsecondes) to clas...
static PLUGIN * PluginFind(PCB_FILE_T aFileType)
Function PluginFind returns a PLUGIN which the caller can use to import, export, save,...
Definition: io_mgr.cpp:58
bool AskSaveBoardFileName(wxWindow *aParent, wxString *aFileName)
Function AskSaveBoardFileName puts up a wxFileDialog asking for a BOARD filename to save.
void ClrModify()
Definition: base_screen.h:225
wxString PcbFileWildcard()
void Save(const wxString &aFileName) const
Write this library table to aFileName in s-expression form.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
const wxSize GetPageSizeIU() const override
Works off of GetPageSettings() to return the size of the paper page in the internal units of this par...
bool Files_io_from_id(int aId)
Function Files_io_from_id Read and write board files.
Create and handle a window for the 3d viewer connected to a Kiway and a pcbboard.
Definition: eda_3d_viewer.h:65
BOARD_DESIGN_SETTINGS m_designSettings
Definition: class_board.h:196
BOARD * GetBoard() const
void SetModify()
Definition: base_screen.h:224
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
Message panel definition file.
PCB_FILE_T
Enum PCB_FILE_T is a set of file types that the IO_MGR knows about, and for which there has been a pl...
Definition: io_mgr.h:54
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:267
void NewDisplay(bool aForceImmediateRedraw=false)
Reload and refresh (rebuild) the 3D scene.
wxString PCadPcbFileWildcard()
EDA_3D_VIEWER * Get3DViewerFrame()
bool Clear_Pcb(bool aQuery, bool aFinal=false)
Function Clear_Pcb delete all and reinitialize the current board.
Definition: initpcb.cpp:39
bool SavePcbFile(const wxString &aFileName, bool aCreateBackupFile=CREATE_BACKUP_FILE)
Function SavePcbFile writes the board data structures to a aFileName Creates backup when requested an...
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:283
bool IsEmpty() const
Definition: class_board.h:264
void ClrSave()
Definition: base_screen.h:227
bool FetchNetlistFromSchematic(NETLIST &aNetlist, FETCH_NETLIST_MODE aMode)
TRACKS & Tracks()
Definition: class_board.h:220
bool m_TextUpright[LAYER_CLASS_COUNT]
VTBL_ENTRY const wxString FootprintLibTblName() const
Function FootprintLibTblName returns the path and filename of this project's fp-lib-table,...
Definition: project.cpp:120
bool LockFile(const wxString &aFileName)
Mark a schematic file as being in use.
bool empty() const
Definition: utf8.h:108
S-expression Pcbnew file format.
Definition: io_mgr.h:57
BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
static const wxString GetFileExtension(PCB_FILE_T aFileType)
Function GetFileExtension returns the file extension for aFileType.
Definition: io_mgr.cpp:109