KiCad PCB EDA Suite
kicad_plugin.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) 2012 CERN
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <advanced_config.h>
26 #include <base_units.h>
27 #include <board.h>
28 #include <board_design_settings.h>
29 #include <boost/ptr_container/ptr_map.hpp>
30 #include <confirm.h>
31 #include <convert_basic_shapes_to_polygon.h> // for enum RECT_CHAMFER_POSITIONS definition
32 #include <core/arraydim.h>
33 #include <pcb_dimension.h>
34 #include <footprint.h>
35 #include <fp_shape.h>
36 #include <kicad_string.h>
37 #include <kiface_i.h>
38 #include <locale_io.h>
39 #include <macros.h>
40 #include <pad.h>
41 #include <pcb_group.h>
42 #include <pcb_shape.h>
43 #include <pcb_target.h>
44 #include <pcb_text.h>
45 #include <pcbnew_settings.h>
48 #include <trace_helpers.h>
49 #include <pcb_track.h>
52 #include <wx/dir.h>
53 #include <wx/log.h>
54 #include <wx_filename.h>
55 #include <zone.h>
56 #include <zones.h>
57 
58 using namespace PCB_KEYS_T;
59 
60 
70 {
72  std::unique_ptr<FOOTPRINT> m_footprint;
73 
74 public:
75  FP_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName );
76 
77  const WX_FILENAME& GetFileName() const { return m_filename; }
78  const FOOTPRINT* GetFootprint() const { return m_footprint.get(); }
79 };
80 
81 
82 FP_CACHE_ITEM::FP_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
83  m_filename( aFileName ),
84  m_footprint( aFootprint )
85 { }
86 
87 
88 typedef boost::ptr_map< wxString, FP_CACHE_ITEM > FOOTPRINT_MAP;
89 
90 
91 class FP_CACHE
92 {
93  PCB_IO* m_owner; // Plugin object that owns the cache.
94  wxFileName m_lib_path; // The path of the library.
95  wxString m_lib_raw_path; // For quick comparisons.
96  FOOTPRINT_MAP m_footprints; // Map of footprint filename to FOOTPRINT*.
97 
98  bool m_cache_dirty; // Stored separately because it's expensive to check
99  // m_cache_timestamp against all the files.
100  long long m_cache_timestamp; // A hash of the timestamps for all the footprint
101  // files.
102 
103 public:
104  FP_CACHE( PCB_IO* aOwner, const wxString& aLibraryPath );
105 
106  wxString GetPath() const { return m_lib_raw_path; }
107 
108  bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
109 
110  bool Exists() const { return m_lib_path.IsOk() && m_lib_path.DirExists(); }
111 
113 
114  // Most all functions in this class throw IO_ERROR exceptions. There are no
115  // error codes nor user interface calls from here, nor in any PLUGIN.
116  // Catch these exceptions higher up please.
117 
123  void Save( FOOTPRINT* aFootprint = nullptr );
124 
125  void Load();
126 
127  void Remove( const wxString& aFootprintName );
128 
134  static long long GetTimestamp( const wxString& aLibPath );
135 
139  bool IsModified();
140 
152  bool IsPath( const wxString& aPath ) const;
153 };
154 
155 
156 FP_CACHE::FP_CACHE( PCB_IO* aOwner, const wxString& aLibraryPath )
157 {
158  m_owner = aOwner;
159  m_lib_raw_path = aLibraryPath;
160  m_lib_path.SetPath( aLibraryPath );
161  m_cache_timestamp = 0;
162  m_cache_dirty = true;
163 }
164 
165 
166 void FP_CACHE::Save( FOOTPRINT* aFootprint )
167 {
168  m_cache_timestamp = 0;
169 
170  if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() )
171  {
172  THROW_IO_ERROR( wxString::Format( _( "Cannot create footprint library '%s'." ),
173  m_lib_raw_path ) );
174  }
175 
176  if( !m_lib_path.IsDirWritable() )
177  {
178  THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' is read only." ),
179  m_lib_raw_path ) );
180  }
181 
182  for( FOOTPRINT_MAP::iterator it = m_footprints.begin(); it != m_footprints.end(); ++it )
183  {
184  if( aFootprint && aFootprint != it->second->GetFootprint() )
185  continue;
186 
187  WX_FILENAME fn = it->second->GetFileName();
188 
189  wxString tempFileName =
190 #ifdef USE_TMP_FILE
191  wxFileName::CreateTempFileName( fn.GetPath() );
192 #else
193  fn.GetFullPath();
194 #endif
195  // Allow file output stream to go out of scope to close the file stream before
196  // renaming the file.
197  {
198  wxLogTrace( traceKicadPcbPlugin, wxT( "Creating temporary library file '%s'." ),
199  tempFileName );
200 
201  FILE_OUTPUTFORMATTER formatter( tempFileName );
202 
203  m_owner->SetOutputFormatter( &formatter );
204  m_owner->Format( (BOARD_ITEM*) it->second->GetFootprint() );
205  }
206 
207 #ifdef USE_TMP_FILE
208  wxRemove( fn.GetFullPath() ); // it is not an error if this does not exist
209 
210  // Even on Linux you can see an _intermittent_ error when calling wxRename(),
211  // and it is fully inexplicable. See if this dodges the error.
212  wxMilliSleep( 250L );
213 
214  if( !wxRenameFile( tempFileName, fn.GetFullPath() ) )
215  {
216  wxString msg = wxString::Format( _( "Cannot rename temporary file '%s' to '%s'" ),
217  tempFileName,
218  fn.GetFullPath() );
219  THROW_IO_ERROR( msg );
220  }
221 #endif
223  }
224 
225  m_cache_timestamp += m_lib_path.GetModificationTime().GetValue().GetValue();
226 
227  // If we've saved the full cache, we clear the dirty flag.
228  if( !aFootprint )
229  m_cache_dirty = false;
230 }
231 
232 
234 {
235  m_cache_dirty = false;
236  m_cache_timestamp = 0;
237 
238  wxDir dir( m_lib_raw_path );
239 
240  if( !dir.IsOpened() )
241  {
242  wxString msg = wxString::Format( _( "Footprint library '%s' not found." ),
243  m_lib_raw_path );
244  THROW_IO_ERROR( msg );
245  }
246 
247  wxString fullName;
248  wxString fileSpec = wxT( "*." ) + KiCadFootprintFileExtension;
249 
250  // wxFileName construction is egregiously slow. Construct it once and just swap out
251  // the filename thereafter.
252  WX_FILENAME fn( m_lib_raw_path, wxT( "dummyName" ) );
253 
254  if( dir.GetFirst( &fullName, fileSpec ) )
255  {
256  wxString cacheError;
257 
258  do
259  {
260  fn.SetFullName( fullName );
261 
262  // Queue I/O errors so only files that fail to parse don't get loaded.
263  try
264  {
265  FILE_LINE_READER reader( fn.GetFullPath() );
266 
267  m_owner->m_parser->SetLineReader( &reader );
268 
269  FOOTPRINT* footprint = (FOOTPRINT*) m_owner->m_parser->Parse();
270  wxString fpName = fn.GetName();
271 
272  footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
273  m_footprints.insert( fpName, new FP_CACHE_ITEM( footprint, fn ) );
274  }
275  catch( const IO_ERROR& ioe )
276  {
277  if( !cacheError.IsEmpty() )
278  cacheError += "\n\n";
279 
280  cacheError += ioe.What();
281  }
282  } while( dir.GetNext( &fullName ) );
283 
285 
286  if( !cacheError.IsEmpty() )
287  THROW_IO_ERROR( cacheError );
288  }
289 }
290 
291 
292 void FP_CACHE::Remove( const wxString& aFootprintName )
293 {
294  FOOTPRINT_MAP::const_iterator it = m_footprints.find( aFootprintName );
295 
296  if( it == m_footprints.end() )
297  {
298  wxString msg = wxString::Format( _( "Library '%s' has no footprint '%s'." ),
300  aFootprintName );
301  THROW_IO_ERROR( msg );
302  }
303 
304  // Remove the footprint from the cache and delete the footprint file from the library.
305  wxString fullPath = it->second->GetFileName().GetFullPath();
306  m_footprints.erase( aFootprintName );
307  wxRemoveFile( fullPath );
308 }
309 
310 
311 bool FP_CACHE::IsPath( const wxString& aPath ) const
312 {
313  return aPath == m_lib_raw_path;
314 }
315 
316 
318 {
320 
321  return m_cache_dirty;
322 }
323 
324 
325 long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
326 {
327  wxString fileSpec = wxT( "*." ) + KiCadFootprintFileExtension;
328 
329  return TimestampDir( aLibPath, fileSpec );
330 }
331 
332 
333 void PCB_IO::Save( const wxString& aFileName, BOARD* aBoard, const PROPERTIES* aProperties )
334 {
335  LOCALE_IO toggle; // toggles on, then off, the C locale.
336 
337  wxString sanityResult = aBoard->GroupsSanityCheck();
338 
339  if( sanityResult != wxEmptyString )
340  {
341  KIDIALOG dlg( nullptr, wxString::Format(
342  _( "Please report this bug. Error validating group structure: %s"
343  "\n\nSave anyway?" ), sanityResult ),
344  _( "Internal group data structure corrupt" ),
345  wxOK | wxCANCEL | wxICON_ERROR );
346  dlg.SetOKLabel( _( "Save Anyway" ) );
347 
348  if( dlg.ShowModal() == wxID_CANCEL )
349  return;
350  }
351 
352  init( aProperties );
353 
354  m_board = aBoard; // after init()
355 
356  // Prepare net mapping that assures that net codes saved in a file are consecutive integers
357  m_mapping->SetBoard( aBoard );
358 
359  FILE_OUTPUTFORMATTER formatter( aFileName );
360 
361  m_out = &formatter; // no ownership
362 
363  m_out->Print( 0, "(kicad_pcb (version %d) (generator pcbnew)\n", SEXPR_BOARD_FILE_VERSION );
364 
365  Format( aBoard, 1 );
366 
367  m_out->Print( 0, ")\n" );
368 
369  m_out = nullptr;
370 }
371 
372 
373 BOARD_ITEM* PCB_IO::Parse( const wxString& aClipboardSourceInput )
374 {
375  std::string input = TO_UTF8( aClipboardSourceInput );
376 
377  STRING_LINE_READER reader( input, wxT( "clipboard" ) );
378 
379  m_parser->SetLineReader( &reader );
380 
381  try
382  {
383  return m_parser->Parse();
384  }
385  catch( const PARSE_ERROR& parse_error )
386  {
387  if( m_parser->IsTooRecent() )
388  throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() );
389  else
390  throw;
391  }
392 }
393 
394 
395 void PCB_IO::Format( const BOARD_ITEM* aItem, int aNestLevel ) const
396 {
397  LOCALE_IO toggle; // public API function, perform anything convenient for caller
398 
399  switch( aItem->Type() )
400  {
401  case PCB_T:
402  format( static_cast<const BOARD*>( aItem ), aNestLevel );
403  break;
404 
405  case PCB_DIM_ALIGNED_T:
406  case PCB_DIM_CENTER_T:
408  case PCB_DIM_LEADER_T:
409  format( static_cast<const PCB_DIMENSION_BASE*>( aItem ), aNestLevel );
410  break;
411 
412  case PCB_SHAPE_T:
413  format( static_cast<const PCB_SHAPE*>( aItem ), aNestLevel );
414  break;
415 
416  case PCB_FP_SHAPE_T:
417  format( static_cast<const FP_SHAPE*>( aItem ), aNestLevel );
418  break;
419 
420  case PCB_TARGET_T:
421  format( static_cast<const PCB_TARGET*>( aItem ), aNestLevel );
422  break;
423 
424  case PCB_FOOTPRINT_T:
425  format( static_cast<const FOOTPRINT*>( aItem ), aNestLevel );
426  break;
427 
428  case PCB_PAD_T:
429  format( static_cast<const PAD*>( aItem ), aNestLevel );
430  break;
431 
432  case PCB_TEXT_T:
433  format( static_cast<const PCB_TEXT*>( aItem ), aNestLevel );
434  break;
435 
436  case PCB_FP_TEXT_T:
437  format( static_cast<const FP_TEXT*>( aItem ), aNestLevel );
438  break;
439 
440  case PCB_GROUP_T:
441  format( static_cast<const PCB_GROUP*>( aItem ), aNestLevel );
442  break;
443 
444  case PCB_TRACE_T:
445  case PCB_ARC_T:
446  case PCB_VIA_T:
447  format( static_cast<const PCB_TRACK*>( aItem ), aNestLevel );
448  break;
449 
450  case PCB_FP_ZONE_T:
451  case PCB_ZONE_T:
452  format( static_cast<const ZONE*>( aItem ), aNestLevel );
453  break;
454 
455  default:
456  wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
457  }
458 }
459 
460 
461 void PCB_IO::formatLayer( const BOARD_ITEM* aItem ) const
462 {
463  PCB_LAYER_ID layer = aItem->GetLayer();
464 
465  m_out->Print( 0, " (layer %s)", m_out->Quotew( LSET::Name( layer ) ).c_str() );
466 }
467 
468 
469 void PCB_IO::formatSetup( const BOARD* aBoard, int aNestLevel ) const
470 {
471  // Setup
472  m_out->Print( aNestLevel, "(setup\n" );
473 
474  // Save the board physical stackup structure
475  const BOARD_STACKUP& stackup = aBoard->GetDesignSettings().GetStackupDescriptor();
476 
477  if( aBoard->GetDesignSettings().m_HasStackup )
478  stackup.FormatBoardStackup( m_out, aBoard, aNestLevel+1 );
479 
480  BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
481 
482  m_out->Print( aNestLevel+1, "(pad_to_mask_clearance %s)\n",
483  FormatInternalUnits( dsnSettings.m_SolderMaskMargin ).c_str() );
484 
485  if( dsnSettings.m_SolderMaskMinWidth )
486  m_out->Print( aNestLevel+1, "(solder_mask_min_width %s)\n",
487  FormatInternalUnits( dsnSettings.m_SolderMaskMinWidth ).c_str() );
488 
489  if( dsnSettings.m_SolderPasteMargin != 0 )
490  m_out->Print( aNestLevel+1, "(pad_to_paste_clearance %s)\n",
491  FormatInternalUnits( dsnSettings.m_SolderPasteMargin ).c_str() );
492 
493  if( dsnSettings.m_SolderPasteMarginRatio != 0 )
494  m_out->Print( aNestLevel+1, "(pad_to_paste_clearance_ratio %s)\n",
495  Double2Str( dsnSettings.m_SolderPasteMarginRatio ).c_str() );
496 
497  if( dsnSettings.m_AuxOrigin != wxPoint( 0, 0 ) )
498  m_out->Print( aNestLevel+1, "(aux_axis_origin %s %s)\n",
499  FormatInternalUnits( dsnSettings.m_AuxOrigin.x ).c_str(),
500  FormatInternalUnits( dsnSettings.m_AuxOrigin.y ).c_str() );
501 
502  if( dsnSettings.m_GridOrigin != wxPoint( 0, 0 ) )
503  m_out->Print( aNestLevel+1, "(grid_origin %s %s)\n",
504  FormatInternalUnits( dsnSettings.m_GridOrigin.x ).c_str(),
505  FormatInternalUnits( dsnSettings.m_GridOrigin.y ).c_str() );
506 
507  aBoard->GetPlotOptions().Format( m_out, aNestLevel+1 );
508 
509  m_out->Print( aNestLevel, ")\n\n" );
510 }
511 
512 
513 void PCB_IO::formatGeneral( const BOARD* aBoard, int aNestLevel ) const
514 {
515  const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
516 
517  m_out->Print( 0, "\n" );
518  m_out->Print( aNestLevel, "(general\n" );
519  m_out->Print( aNestLevel+1, "(thickness %s)\n",
520  FormatInternalUnits( dsnSettings.GetBoardThickness() ).c_str() );
521 
522  m_out->Print( aNestLevel, ")\n\n" );
523 
524  aBoard->GetPageSettings().Format( m_out, aNestLevel, m_ctl );
525  aBoard->GetTitleBlock().Format( m_out, aNestLevel, m_ctl );
526 }
527 
528 
529 void PCB_IO::formatBoardLayers( const BOARD* aBoard, int aNestLevel ) const
530 {
531  m_out->Print( aNestLevel, "(layers\n" );
532 
533  // Save only the used copper layers from front to back.
534 
535  for( LSEQ cu = aBoard->GetEnabledLayers().CuStack(); cu; ++cu )
536  {
537  PCB_LAYER_ID layer = *cu;
538 
539  m_out->Print( aNestLevel+1, "(%d %s %s", layer,
540  m_out->Quotew( LSET::Name( layer ) ).c_str(),
541  LAYER::ShowType( aBoard->GetLayerType( layer ) ) );
542 
543  if( LSET::Name( layer ) != m_board->GetLayerName( layer ) )
544  m_out->Print( 0, " %s", m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
545 
546  m_out->Print( 0, ")\n" );
547  }
548 
549  // Save used non-copper layers in the order they are defined.
550  // desired sequence for non Cu BOARD layers.
551  static const PCB_LAYER_ID non_cu[] =
552  {
553  B_Adhes, // 32
554  F_Adhes,
555  B_Paste,
556  F_Paste,
557  B_SilkS,
558  F_SilkS,
559  B_Mask,
560  F_Mask,
561  Dwgs_User,
562  Cmts_User,
563  Eco1_User,
564  Eco2_User,
565  Edge_Cuts,
566  Margin,
567  B_CrtYd,
568  F_CrtYd,
569  B_Fab,
570  F_Fab,
571  User_1,
572  User_2,
573  User_3,
574  User_4,
575  User_5,
576  User_6,
577  User_7,
578  User_8,
579  User_9
580  };
581 
582  for( LSEQ seq = aBoard->GetEnabledLayers().Seq( non_cu, arrayDim( non_cu ) ); seq; ++seq )
583  {
584  PCB_LAYER_ID layer = *seq;
585 
586  m_out->Print( aNestLevel+1, "(%d %s user", layer,
587  m_out->Quotew( LSET::Name( layer ) ).c_str() );
588 
589  if( m_board->GetLayerName( layer ) != LSET::Name( layer ) )
590  m_out->Print( 0, " %s", m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
591 
592  m_out->Print( 0, ")\n" );
593  }
594 
595  m_out->Print( aNestLevel, ")\n\n" );
596 }
597 
598 
599 void PCB_IO::formatNetInformation( const BOARD* aBoard, int aNestLevel ) const
600 {
601  for( NETINFO_ITEM* net : *m_mapping )
602  {
603  if( net == nullptr ) // Skip not actually existing nets (orphan nets)
604  continue;
605 
606  m_out->Print( aNestLevel, "(net %d %s)\n",
607  m_mapping->Translate( net->GetNetCode() ),
608  m_out->Quotew( net->GetNetname() ).c_str() );
609  }
610 
611  m_out->Print( 0, "\n" );
612 }
613 
614 
615 void PCB_IO::formatProperties( const BOARD* aBoard, int aNestLevel ) const
616 {
617  for( const std::pair<const wxString, wxString>& prop : aBoard->GetProperties() )
618  {
619  m_out->Print( aNestLevel, "(property %s %s)\n",
620  m_out->Quotew( prop.first ).c_str(),
621  m_out->Quotew( prop.second ).c_str() );
622  }
623 
624  if( !aBoard->GetProperties().empty() )
625  m_out->Print( 0, "\n" );
626 }
627 
628 
629 void PCB_IO::formatHeader( const BOARD* aBoard, int aNestLevel ) const
630 {
631  formatGeneral( aBoard, aNestLevel );
632 
633  // Layers list.
634  formatBoardLayers( aBoard, aNestLevel );
635 
636  // Setup
637  formatSetup( aBoard, aNestLevel );
638 
639  // Properties
640  formatProperties( aBoard, aNestLevel );
641 
642  // Save net codes and names
643  formatNetInformation( aBoard, aNestLevel );
644 }
645 
646 
647 void PCB_IO::format( const BOARD* aBoard, int aNestLevel ) const
648 {
649  std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_footprints( aBoard->Footprints().begin(),
650  aBoard->Footprints().end() );
651  std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_drawings( aBoard->Drawings().begin(),
652  aBoard->Drawings().end() );
653  std::set<PCB_TRACK*, PCB_TRACK::cmp_tracks> sorted_tracks( aBoard->Tracks().begin(),
654  aBoard->Tracks().end() );
655  std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aBoard->Zones().begin(),
656  aBoard->Zones().end() );
657  std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_groups( aBoard->Groups().begin(),
658  aBoard->Groups().end() );
659  formatHeader( aBoard, aNestLevel );
660 
661  // Save the footprints.
662  for( BOARD_ITEM* footprint : sorted_footprints )
663  {
664  Format( footprint, aNestLevel );
665  m_out->Print( 0, "\n" );
666  }
667 
668  // Save the graphical items on the board (not owned by a footprint)
669  for( BOARD_ITEM* item : sorted_drawings )
670  Format( item, aNestLevel );
671 
672  if( sorted_drawings.size() )
673  m_out->Print( 0, "\n" );
674 
675  // Do not save PCB_MARKERs, they can be regenerated easily.
676 
677  // Save the tracks and vias.
678  for( PCB_TRACK* track : sorted_tracks )
679  Format( track, aNestLevel );
680 
681  if( sorted_tracks.size() )
682  m_out->Print( 0, "\n" );
683 
684  // Save the polygon (which are the newer technology) zones.
685  for( auto zone : sorted_zones )
686  Format( zone, aNestLevel );
687 
688  // Save the groups
689  for( BOARD_ITEM* group : sorted_groups )
690  Format( group, aNestLevel );
691 }
692 
693 
694 void PCB_IO::format( const PCB_DIMENSION_BASE* aDimension, int aNestLevel ) const
695 {
696  const PCB_DIM_ALIGNED* aligned = dynamic_cast<const PCB_DIM_ALIGNED*>( aDimension );
697  const PCB_DIM_ORTHOGONAL* ortho = dynamic_cast<const PCB_DIM_ORTHOGONAL*>( aDimension );
698  const PCB_DIM_CENTER* center = dynamic_cast<const PCB_DIM_CENTER*>( aDimension );
699  const PCB_DIM_LEADER* leader = dynamic_cast<const PCB_DIM_LEADER*>( aDimension );
700 
701  m_out->Print( aNestLevel, "(dimension" );
702 
703  if( aDimension->IsLocked() )
704  m_out->Print( 0, " locked" );
705 
706  if( aDimension->Type() == PCB_DIM_ALIGNED_T )
707  m_out->Print( 0, " (type aligned)" );
708  else if( aDimension->Type() == PCB_DIM_LEADER_T )
709  m_out->Print( 0, " (type leader)" );
710  else if( aDimension->Type() == PCB_DIM_CENTER_T )
711  m_out->Print( 0, " (type center)" );
712  else if( aDimension->Type() == PCB_DIM_ORTHOGONAL_T )
713  m_out->Print( 0, " (type orthogonal)" );
714  else
715  wxFAIL_MSG( wxT( "Cannot format unknown dimension type!" ) );
716 
717  formatLayer( aDimension );
718 
719  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aDimension->m_Uuid.AsString() ) );
720 
721  m_out->Print( 0, "\n" );
722 
723  m_out->Print( aNestLevel+1, "(pts (xy %s %s) (xy %s %s))\n",
724  FormatInternalUnits( aDimension->GetStart().x ).c_str(),
725  FormatInternalUnits( aDimension->GetStart().y ).c_str(),
726  FormatInternalUnits( aDimension->GetEnd().x ).c_str(),
727  FormatInternalUnits( aDimension->GetEnd().y ).c_str() );
728 
729  if( aligned )
730  {
731  m_out->Print( aNestLevel+1, "(height %s)\n",
732  FormatInternalUnits( aligned->GetHeight() ).c_str() );
733  }
734 
735  if( ortho )
736  {
737  m_out->Print( aNestLevel+1, "(orientation %d)\n",
738  static_cast<int>( ortho->GetOrientation() ) );
739  }
740 
741  if( !center )
742  {
743  Format( &aDimension->Text(), aNestLevel + 1 );
744 
745  m_out->Print( aNestLevel + 1, "(format" );
746 
747  if( !aDimension->GetPrefix().IsEmpty() )
748  m_out->Print( 0, " (prefix %s)", m_out->Quotew( aDimension->GetPrefix() ).c_str() );
749 
750  if( !aDimension->GetSuffix().IsEmpty() )
751  m_out->Print( 0, " (suffix %s)", m_out->Quotew( aDimension->GetSuffix() ).c_str() );
752 
753  m_out->Print( 0, " (units %d) (units_format %d) (precision %d)",
754  static_cast<int>( aDimension->GetUnitsMode() ),
755  static_cast<int>( aDimension->GetUnitsFormat() ), aDimension->GetPrecision() );
756 
757  if( aDimension->GetOverrideTextEnabled() )
758  m_out->Print( 0, " (override_value %s)",
759  m_out->Quotew( aDimension->GetOverrideText() ).c_str() );
760 
761  if( aDimension->GetSuppressZeroes() )
762  m_out->Print( 0, " suppress_zeroes" );
763 
764  m_out->Print( 0, ")\n" );
765  }
766 
767  m_out->Print( aNestLevel+1, "(style (thickness %s) (arrow_length %s) (text_position_mode %d)",
768  FormatInternalUnits( aDimension->GetLineThickness() ).c_str(),
769  FormatInternalUnits( aDimension->GetArrowLength() ).c_str(),
770  static_cast<int>( aDimension->GetTextPositionMode() ) );
771 
772  if( aligned )
773  {
774  m_out->Print( 0, " (extension_height %s)",
775  FormatInternalUnits( aligned->GetExtensionHeight() ).c_str() );
776  }
777 
778  if( leader )
779  m_out->Print( 0, " (text_frame %d)", static_cast<int>( leader->GetTextFrame() ) );
780 
781  m_out->Print( 0, " (extension_offset %s)",
782  FormatInternalUnits( aDimension->GetExtensionOffset() ).c_str() );
783 
784  if( aDimension->GetKeepTextAligned() )
785  m_out->Print( 0, " keep_text_aligned" );
786 
787  m_out->Print( 0, ")\n" );
788 
789  m_out->Print( aNestLevel, ")\n" );
790 }
791 
792 
793 void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const
794 {
795  std::string locked = aShape->IsLocked() ? " locked" : "";
796 
797  switch( aShape->GetShape() )
798  {
799  case SHAPE_T::SEGMENT:
800  m_out->Print( aNestLevel, "(gr_line%s (start %s) (end %s)",
801  locked.c_str(),
802  FormatInternalUnits( aShape->GetStart() ).c_str(),
803  FormatInternalUnits( aShape->GetEnd() ).c_str() );
804 
805  if( aShape->GetAngle() != 0.0 )
806  m_out->Print( 0, " (angle %s)", FormatAngle( aShape->GetAngle() ).c_str() );
807 
808  break;
809 
810  case SHAPE_T::RECT:
811  m_out->Print( aNestLevel, "(gr_rect%s (start %s) (end %s)",
812  locked.c_str(),
813  FormatInternalUnits( aShape->GetStart() ).c_str(),
814  FormatInternalUnits( aShape->GetEnd() ).c_str() );
815  break;
816 
817  case SHAPE_T::CIRCLE:
818  m_out->Print( aNestLevel, "(gr_circle%s (center %s) (end %s)",
819  locked.c_str(),
820  FormatInternalUnits( aShape->GetStart() ).c_str(),
821  FormatInternalUnits( aShape->GetEnd() ).c_str() );
822  break;
823 
824  case SHAPE_T::ARC:
825  m_out->Print( aNestLevel, "(gr_arc%s (start %s) (end %s) (angle %s)",
826  locked.c_str(),
827  FormatInternalUnits( aShape->GetStart() ).c_str(),
828  FormatInternalUnits( aShape->GetEnd() ).c_str(),
829  FormatAngle( aShape->GetAngle() ).c_str() );
830  break;
831 
832  case SHAPE_T::POLY:
833  if( aShape->IsPolyShapeValid() )
834  {
835  const SHAPE_POLY_SET& poly = aShape->GetPolyShape();
836  const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
837 
838  m_out->Print( aNestLevel, "(gr_poly%s\n", locked.c_str() );
839  m_out->Print( aNestLevel+1, "(pts\n" );
840 
841  bool need_newline = false;
842 
843  for( int ii = 0; ii < outline.PointCount(); ++ii )
844  {
845  int nestLevel = ii == 0 ? aNestLevel + 2 : 0;
846 
847  if( ii && ( !( ii % 4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) )
848  {
849  // newline every 4 pts.
850  nestLevel = aNestLevel + 2;
851  m_out->Print( 0, "\n" );
852  need_newline = false;
853  }
854 
855  int ind = outline.ArcIndex( ii );
856 
857  if( ind < 0 )
858  {
859  m_out->Print( nestLevel, "%s(xy %s)",
860  nestLevel ? "" : " ",
861  FormatInternalUnits( outline.CPoint( ii ) ).c_str() );
862  need_newline = true;
863  }
864  else
865  {
866  const SHAPE_ARC& arc = outline.Arc( ind );
867  m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s))",
868  nestLevel ? "" : " ",
869  FormatInternalUnits( arc.GetP0() ).c_str(),
870  FormatInternalUnits( arc.GetArcMid() ).c_str(),
871  FormatInternalUnits( arc.GetP1() ).c_str() );
872  need_newline = true;
873 
874  do
875  {
876  ++ii;
877  } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
878 
879  --ii;
880  }
881  }
882 
883  if( need_newline )
884  m_out->Print( 0, "\n" );
885 
886  m_out->Print( aNestLevel+1, ")" );
887  }
888  else
889  {
890  wxFAIL_MSG( wxT( "Cannot format invalid polygon." ) );
891  return;
892  }
893 
894  break;
895 
896  case SHAPE_T::BEZIER:
897  m_out->Print( aNestLevel, "(gr_curve%s (pts (xy %s) (xy %s) (xy %s) (xy %s))",
898  locked.c_str(),
899  FormatInternalUnits( aShape->GetStart() ).c_str(),
900  FormatInternalUnits( aShape->GetBezierC1() ).c_str(),
901  FormatInternalUnits( aShape->GetBezierC2() ).c_str(),
902  FormatInternalUnits( aShape->GetEnd() ).c_str() );
903  break;
904 
905  default:
906  wxFAIL_MSG( "PCB_IO::format cannot format unknown PCB_SHAPE shape:"
907  + SHAPE_T_asString( aShape->GetShape()) );
908  return;
909  };
910 
911  formatLayer( aShape );
912 
913  m_out->Print( 0, " (width %s)", FormatInternalUnits( aShape->GetWidth() ).c_str() );
914 
915  // The filled flag represents if a solid fill is present on circles, rectangles and polygons
916  if( ( aShape->GetShape() == SHAPE_T::POLY )
917  || ( aShape->GetShape() == SHAPE_T::RECT )
918  || ( aShape->GetShape() == SHAPE_T::CIRCLE ) )
919  {
920  if( aShape->IsFilled() )
921  m_out->Print( 0, " (fill solid)" );
922  else
923  m_out->Print( 0, " (fill none)" );
924  }
925 
926  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aShape->m_Uuid.AsString() ) );
927 
928  m_out->Print( 0, ")\n" );
929 }
930 
931 
932 void PCB_IO::format( const FP_SHAPE* aFPShape, int aNestLevel ) const
933 {
934  std::string locked = aFPShape->IsLocked() ? " locked" : "";
935 
936  switch( aFPShape->GetShape() )
937  {
938  case SHAPE_T::SEGMENT:
939  m_out->Print( aNestLevel, "(fp_line%s (start %s) (end %s)",
940  locked.c_str(),
941  FormatInternalUnits( aFPShape->GetStart0() ).c_str(),
942  FormatInternalUnits( aFPShape->GetEnd0() ).c_str() );
943  break;
944 
945  case SHAPE_T::RECT:
946  m_out->Print( aNestLevel, "(fp_rect%s (start %s) (end %s)",
947  locked.c_str(),
948  FormatInternalUnits( aFPShape->GetStart0() ).c_str(),
949  FormatInternalUnits( aFPShape->GetEnd0() ).c_str() );
950  break;
951 
952  case SHAPE_T::CIRCLE:
953  m_out->Print( aNestLevel, "(fp_circle%s (center %s) (end %s)",
954  locked.c_str(),
955  FormatInternalUnits( aFPShape->GetStart0() ).c_str(),
956  FormatInternalUnits( aFPShape->GetEnd0() ).c_str() );
957  break;
958 
959  case SHAPE_T::ARC:
960  m_out->Print( aNestLevel, "(fp_arc%s (start %s) (end %s) (angle %s)",
961  locked.c_str(),
962  FormatInternalUnits( aFPShape->GetStart0() ).c_str(),
963  FormatInternalUnits( aFPShape->GetEnd0() ).c_str(),
964  FormatAngle( aFPShape->GetAngle() ).c_str() );
965  break;
966 
967  case SHAPE_T::POLY:
968  if( aFPShape->IsPolyShapeValid() )
969  {
970  const SHAPE_POLY_SET& poly = aFPShape->GetPolyShape();
971  const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
972 
973  m_out->Print( aNestLevel, "(fp_poly%s (pts",
974  locked.c_str() );
975 
976  for( int ii = 0; ii < outline.PointCount(); ++ii )
977  {
978  int nestLevel = 0;
979 
980  if( ii && ( !( ii%4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) )
981  {
982  // newline every 4 pts.
983  nestLevel = aNestLevel + 1;
984  m_out->Print( 0, "\n" );
985  }
986 
987  int ind = outline.ArcIndex( ii );
988 
989  if( ind < 0 )
990  {
991  m_out->Print( nestLevel, "%s(xy %s)",
992  nestLevel ? "" : " ", FormatInternalUnits( outline.CPoint( ii ) ).c_str() );
993  }
994  else
995  {
996  auto& arc = outline.Arc( ind );
997  m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s))",
998  nestLevel ? "" : " ",
999  FormatInternalUnits( arc.GetP0() ).c_str(),
1000  FormatInternalUnits( arc.GetArcMid() ).c_str(),
1001  FormatInternalUnits( arc.GetP1() ).c_str() );
1002 
1003  do
1004  {
1005  ++ii;
1006  } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
1007 
1008  --ii;
1009  }
1010  }
1011 
1012  m_out->Print( 0, ")" );
1013  }
1014  else
1015  {
1016  wxFAIL_MSG( wxT( "Cannot format invalid polygon." ) );
1017  return;
1018  }
1019  break;
1020 
1021  case SHAPE_T::BEZIER:
1022  m_out->Print( aNestLevel, "(fp_curve%s (pts (xy %s) (xy %s) (xy %s) (xy %s))",
1023  locked.c_str(),
1024  FormatInternalUnits( aFPShape->GetStart0() ).c_str(),
1025  FormatInternalUnits( aFPShape->GetBezierC1_0() ).c_str(),
1026  FormatInternalUnits( aFPShape->GetBezierC2_0() ).c_str(),
1027  FormatInternalUnits( aFPShape->GetEnd0() ).c_str() );
1028  break;
1029 
1030  default:
1031  wxFAIL_MSG( "PCB_IO::format cannot format unknown FP_SHAPE shape:"
1032  + SHAPE_T_asString( aFPShape->GetShape()) );
1033  return;
1034  };
1035 
1036  formatLayer( aFPShape );
1037 
1038  m_out->Print( 0, " (width %s)", FormatInternalUnits( aFPShape->GetWidth() ).c_str() );
1039 
1040  // The filled flag represents if a solid fill is present on circles, rectangles and polygons
1041  if( ( aFPShape->GetShape() == SHAPE_T::POLY )
1042  || ( aFPShape->GetShape() == SHAPE_T::RECT )
1043  || ( aFPShape->GetShape() == SHAPE_T::CIRCLE ) )
1044  {
1045  if( aFPShape->IsFilled() )
1046  m_out->Print( 0, " (fill solid)" );
1047  else
1048  m_out->Print( 0, " (fill none)" );
1049  }
1050 
1051  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aFPShape->m_Uuid.AsString() ) );
1052 
1053  m_out->Print( 0, ")\n" );
1054 }
1055 
1056 
1057 void PCB_IO::format( const PCB_TARGET* aTarget, int aNestLevel ) const
1058 {
1059  m_out->Print( aNestLevel, "(target %s (at %s) (size %s)",
1060  ( aTarget->GetShape() ) ? "x" : "plus",
1061  FormatInternalUnits( aTarget->GetPosition() ).c_str(),
1062  FormatInternalUnits( aTarget->GetSize() ).c_str() );
1063 
1064  if( aTarget->GetWidth() != 0 )
1065  m_out->Print( 0, " (width %s)", FormatInternalUnits( aTarget->GetWidth() ).c_str() );
1066 
1067  formatLayer( aTarget );
1068 
1069  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTarget->m_Uuid.AsString() ) );
1070 
1071  m_out->Print( 0, ")\n" );
1072 }
1073 
1074 
1075 void PCB_IO::format( const FOOTPRINT* aFootprint, int aNestLevel ) const
1076 {
1077  if( !( m_ctl & CTL_OMIT_INITIAL_COMMENTS ) )
1078  {
1079  const wxArrayString* initial_comments = aFootprint->GetInitialComments();
1080 
1081  if( initial_comments )
1082  {
1083  for( unsigned i = 0; i < initial_comments->GetCount(); ++i )
1084  m_out->Print( aNestLevel, "%s\n", TO_UTF8( (*initial_comments)[i] ) );
1085 
1086  m_out->Print( 0, "\n" ); // improve readability?
1087  }
1088  }
1089 
1090  if( m_ctl & CTL_OMIT_LIBNAME )
1091  m_out->Print( aNestLevel, "(footprint %s",
1092  m_out->Quotes( aFootprint->GetFPID().GetLibItemName() ).c_str() );
1093  else
1094  m_out->Print( aNestLevel, "(footprint %s",
1095  m_out->Quotes( aFootprint->GetFPID().Format() ).c_str() );
1096 
1097  if( !( m_ctl & CTL_OMIT_FOOTPRINT_VERSION ) )
1098  m_out->Print( 0, " (version %d) (generator pcbnew)", SEXPR_BOARD_FILE_VERSION );
1099 
1100  if( aFootprint->IsLocked() )
1101  m_out->Print( 0, " locked" );
1102 
1103  if( aFootprint->IsPlaced() )
1104  m_out->Print( 0, " placed" );
1105 
1106  formatLayer( aFootprint );
1107 
1108  m_out->Print( 0, "\n" );
1109  m_out->Print( aNestLevel+1, "(tedit %lX)", (unsigned long)aFootprint->GetLastEditTime() );
1110 
1111  if( !( m_ctl & CTL_OMIT_TSTAMPS ) )
1112  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aFootprint->m_Uuid.AsString() ) );
1113 
1114  m_out->Print( 0, "\n" );
1115 
1116  if( !( m_ctl & CTL_OMIT_AT ) )
1117  {
1118  m_out->Print( aNestLevel+1, "(at %s",
1119  FormatInternalUnits( aFootprint->GetPosition() ).c_str() );
1120 
1121  if( aFootprint->GetOrientation() != 0.0 )
1122  m_out->Print( 0, " %s", FormatAngle( aFootprint->GetOrientation() ).c_str() );
1123 
1124  m_out->Print( 0, ")\n" );
1125  }
1126 
1127  if( !aFootprint->GetDescription().IsEmpty() )
1128  m_out->Print( aNestLevel+1, "(descr %s)\n",
1129  m_out->Quotew( aFootprint->GetDescription() ).c_str() );
1130 
1131  if( !aFootprint->GetKeywords().IsEmpty() )
1132  m_out->Print( aNestLevel+1, "(tags %s)\n",
1133  m_out->Quotew( aFootprint->GetKeywords() ).c_str() );
1134 
1135  const std::map<wxString, wxString>& props = aFootprint->GetProperties();
1136 
1137  for( const std::pair<const wxString, wxString>& prop : props )
1138  {
1139  m_out->Print( aNestLevel+1, "(property %s %s)\n",
1140  m_out->Quotew( prop.first ).c_str(),
1141  m_out->Quotew( prop.second ).c_str() );
1142  }
1143 
1144  if( !( m_ctl & CTL_OMIT_PATH ) && !aFootprint->GetPath().empty() )
1145  m_out->Print( aNestLevel+1, "(path %s)\n",
1146  m_out->Quotew( aFootprint->GetPath().AsString() ).c_str() );
1147 
1148  if( aFootprint->GetPlacementCost90() != 0 )
1149  m_out->Print( aNestLevel+1, "(autoplace_cost90 %d)\n", aFootprint->GetPlacementCost90() );
1150 
1151  if( aFootprint->GetPlacementCost180() != 0 )
1152  m_out->Print( aNestLevel+1, "(autoplace_cost180 %d)\n", aFootprint->GetPlacementCost180() );
1153 
1154  if( aFootprint->GetLocalSolderMaskMargin() != 0 )
1155  m_out->Print( aNestLevel+1, "(solder_mask_margin %s)\n",
1156  FormatInternalUnits( aFootprint->GetLocalSolderMaskMargin() ).c_str() );
1157 
1158  if( aFootprint->GetLocalSolderPasteMargin() != 0 )
1159  m_out->Print( aNestLevel+1, "(solder_paste_margin %s)\n",
1160  FormatInternalUnits( aFootprint->GetLocalSolderPasteMargin() ).c_str() );
1161 
1162  if( aFootprint->GetLocalSolderPasteMarginRatio() != 0 )
1163  m_out->Print( aNestLevel+1, "(solder_paste_ratio %s)\n",
1164  Double2Str( aFootprint->GetLocalSolderPasteMarginRatio() ).c_str() );
1165 
1166  if( aFootprint->GetLocalClearance() != 0 )
1167  m_out->Print( aNestLevel+1, "(clearance %s)\n",
1168  FormatInternalUnits( aFootprint->GetLocalClearance() ).c_str() );
1169 
1170  if( aFootprint->GetZoneConnection() != ZONE_CONNECTION::INHERITED )
1171  m_out->Print( aNestLevel+1, "(zone_connect %d)\n",
1172  static_cast<int>( aFootprint->GetZoneConnection() ) );
1173 
1174  if( aFootprint->GetThermalWidth() != 0 )
1175  m_out->Print( aNestLevel+1, "(thermal_width %s)\n",
1176  FormatInternalUnits( aFootprint->GetThermalWidth() ).c_str() );
1177 
1178  if( aFootprint->GetThermalGap() != 0 )
1179  m_out->Print( aNestLevel+1, "(thermal_gap %s)\n",
1180  FormatInternalUnits( aFootprint->GetThermalGap() ).c_str() );
1181 
1182  // Attributes
1183  if( aFootprint->GetAttributes() )
1184  {
1185  m_out->Print( aNestLevel+1, "(attr" );
1186 
1187  if( aFootprint->GetAttributes() & FP_SMD )
1188  m_out->Print( 0, " smd" );
1189 
1190  if( aFootprint->GetAttributes() & FP_THROUGH_HOLE )
1191  m_out->Print( 0, " through_hole" );
1192 
1193  if( aFootprint->GetAttributes() & FP_BOARD_ONLY )
1194  m_out->Print( 0, " board_only" );
1195 
1196  if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
1197  m_out->Print( 0, " exclude_from_pos_files" );
1198 
1199  if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM )
1200  m_out->Print( 0, " exclude_from_bom" );
1201 
1202  m_out->Print( 0, ")\n" );
1203  }
1204 
1205  Format( (BOARD_ITEM*) &aFootprint->Reference(), aNestLevel + 1 );
1206  Format( (BOARD_ITEM*) &aFootprint->Value(), aNestLevel + 1 );
1207 
1208  std::set<PAD*, FOOTPRINT::cmp_pads> sorted_pads( aFootprint->Pads().begin(),
1209  aFootprint->Pads().end() );
1210  std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> sorted_drawings(
1211  aFootprint->GraphicalItems().begin(),
1212  aFootprint->GraphicalItems().end() );
1213  std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aFootprint->Zones().begin(),
1214  aFootprint->Zones().end() );
1215  std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_groups( aFootprint->Groups().begin(),
1216  aFootprint->Groups().end() );
1217 
1218  // Save drawing elements.
1219 
1220  for( BOARD_ITEM* gr : sorted_drawings )
1221  Format( gr, aNestLevel+1 );
1222 
1223  // Save pads.
1224  for( PAD* pad : sorted_pads )
1225  Format( pad, aNestLevel+1 );
1226 
1227  // Save zones.
1228  for( BOARD_ITEM* zone : sorted_zones )
1229  Format( zone, aNestLevel + 1 );
1230 
1231  // Save groups.
1232  for( BOARD_ITEM* group : sorted_groups )
1233  Format( group, aNestLevel + 1 );
1234 
1235  // Save 3D info.
1236  auto bs3D = aFootprint->Models().begin();
1237  auto es3D = aFootprint->Models().end();
1238 
1239  while( bs3D != es3D )
1240  {
1241  if( !bs3D->m_Filename.IsEmpty() )
1242  {
1243  m_out->Print( aNestLevel+1, "(model %s%s\n",
1244  m_out->Quotew( bs3D->m_Filename ).c_str(),
1245  bs3D->m_Show ? "" : " hide" );
1246 
1247  if( bs3D->m_Opacity != 1.0 )
1248  m_out->Print( aNestLevel+2, "(opacity %0.4f)", bs3D->m_Opacity );
1249 
1250  m_out->Print( aNestLevel+2, "(offset (xyz %s %s %s))\n",
1251  Double2Str( bs3D->m_Offset.x ).c_str(),
1252  Double2Str( bs3D->m_Offset.y ).c_str(),
1253  Double2Str( bs3D->m_Offset.z ).c_str() );
1254 
1255  m_out->Print( aNestLevel+2, "(scale (xyz %s %s %s))\n",
1256  Double2Str( bs3D->m_Scale.x ).c_str(),
1257  Double2Str( bs3D->m_Scale.y ).c_str(),
1258  Double2Str( bs3D->m_Scale.z ).c_str() );
1259 
1260  m_out->Print( aNestLevel+2, "(rotate (xyz %s %s %s))\n",
1261  Double2Str( bs3D->m_Rotation.x ).c_str(),
1262  Double2Str( bs3D->m_Rotation.y ).c_str(),
1263  Double2Str( bs3D->m_Rotation.z ).c_str() );
1264 
1265  m_out->Print( aNestLevel+1, ")\n" );
1266  }
1267 
1268  ++bs3D;
1269  }
1270 
1271  m_out->Print( aNestLevel, ")\n" );
1272 }
1273 
1274 
1275 void PCB_IO::formatLayers( LSET aLayerMask, int aNestLevel ) const
1276 {
1277  std::string output;
1278 
1279  if( aNestLevel == 0 )
1280  output += ' ';
1281 
1282  output += "(layers";
1283 
1284  static const LSET cu_all( LSET::AllCuMask() );
1285  static const LSET fr_bk( 2, B_Cu, F_Cu );
1286  static const LSET adhes( 2, B_Adhes, F_Adhes );
1287  static const LSET paste( 2, B_Paste, F_Paste );
1288  static const LSET silks( 2, B_SilkS, F_SilkS );
1289  static const LSET mask( 2, B_Mask, F_Mask );
1290  static const LSET crt_yd( 2, B_CrtYd, F_CrtYd );
1291  static const LSET fab( 2, B_Fab, F_Fab );
1292 
1293  LSET cu_mask = cu_all;
1294 
1295  // output copper layers first, then non copper
1296 
1297  if( ( aLayerMask & cu_mask ) == cu_mask )
1298  {
1299  output += " *.Cu";
1300  aLayerMask &= ~cu_all; // clear bits, so they are not output again below
1301  }
1302  else if( ( aLayerMask & cu_mask ) == fr_bk )
1303  {
1304  output += " F&B.Cu";
1305  aLayerMask &= ~fr_bk;
1306  }
1307 
1308  if( ( aLayerMask & adhes ) == adhes )
1309  {
1310  output += " *.Adhes";
1311  aLayerMask &= ~adhes;
1312  }
1313 
1314  if( ( aLayerMask & paste ) == paste )
1315  {
1316  output += " *.Paste";
1317  aLayerMask &= ~paste;
1318  }
1319 
1320  if( ( aLayerMask & silks ) == silks )
1321  {
1322  output += " *.SilkS";
1323  aLayerMask &= ~silks;
1324  }
1325 
1326  if( ( aLayerMask & mask ) == mask )
1327  {
1328  output += " *.Mask";
1329  aLayerMask &= ~mask;
1330  }
1331 
1332  if( ( aLayerMask & crt_yd ) == crt_yd )
1333  {
1334  output += " *.CrtYd";
1335  aLayerMask &= ~crt_yd;
1336  }
1337 
1338  if( ( aLayerMask & fab ) == fab )
1339  {
1340  output += " *.Fab";
1341  aLayerMask &= ~fab;
1342  }
1343 
1344  // output any individual layers not handled in wildcard combos above
1345  wxString layerName;
1346 
1347  for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
1348  {
1349  if( aLayerMask[layer] )
1350  {
1351  layerName = LSET::Name( PCB_LAYER_ID( layer ) );
1352  output += ' ';
1353  output += m_out->Quotew( layerName );
1354  }
1355  }
1356 
1357  m_out->Print( aNestLevel, "%s)", output.c_str() );
1358 }
1359 
1360 
1361 void PCB_IO::format( const PAD* aPad, int aNestLevel ) const
1362 {
1363  const char* shape;
1364 
1365  switch( aPad->GetShape() )
1366  {
1367  case PAD_SHAPE::CIRCLE: shape = "circle"; break;
1368  case PAD_SHAPE::RECT: shape = "rect"; break;
1369  case PAD_SHAPE::OVAL: shape = "oval"; break;
1370  case PAD_SHAPE::TRAPEZOID: shape = "trapezoid"; break;
1372  case PAD_SHAPE::ROUNDRECT: shape = "roundrect"; break;
1373  case PAD_SHAPE::CUSTOM: shape = "custom"; break;
1374 
1375  default:
1376  THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"), aPad->GetShape() ) );
1377  }
1378 
1379  const char* type;
1380 
1381  switch( aPad->GetAttribute() )
1382  {
1383  case PAD_ATTRIB::PTH: type = "thru_hole"; break;
1384  case PAD_ATTRIB::SMD: type = "smd"; break;
1385  case PAD_ATTRIB::CONN: type = "connect"; break;
1386  case PAD_ATTRIB::NPTH: type = "np_thru_hole"; break;
1387 
1388  default:
1389  THROW_IO_ERROR( wxString::Format( "unknown pad attribute: %d", aPad->GetAttribute() ) );
1390  }
1391 
1392  const char* property = nullptr;
1393 
1394  switch( aPad->GetProperty() )
1395  {
1396  case PAD_PROP::NONE: break; // could be "none"
1397  case PAD_PROP::BGA: property = "PAD_PROP::BGA"; break;
1398  case PAD_PROP::FIDUCIAL_GLBL: property = "pad_prop_fiducial_glob"; break;
1399  case PAD_PROP::FIDUCIAL_LOCAL: property = "pad_prop_fiducial_loc"; break;
1400  case PAD_PROP::TESTPOINT: property = "PAD_PROP::TESTPOINT"; break;
1401  case PAD_PROP::HEATSINK: property = "PAD_PROP::HEATSINK"; break;
1402  case PAD_PROP::CASTELLATED: property = "PAD_PROP::CASTELLATED"; break;
1403 
1404  default:
1405  THROW_IO_ERROR( wxString::Format( "unknown pad property: %d", aPad->GetProperty() ) );
1406  }
1407 
1408  m_out->Print( aNestLevel, "(pad %s %s %s",
1409  m_out->Quotew( aPad->GetName() ).c_str(),
1410  type,
1411  shape );
1412 
1413  if( aPad->IsLocked() )
1414  m_out->Print( 0, " locked" );
1415 
1416  m_out->Print( 0, " (at %s", FormatInternalUnits( aPad->GetPos0() ).c_str() );
1417 
1418  if( aPad->GetOrientation() != 0.0 )
1419  m_out->Print( 0, " %s", FormatAngle( aPad->GetOrientation() ).c_str() );
1420 
1421  m_out->Print( 0, ")" );
1422 
1423  m_out->Print( 0, " (size %s)", FormatInternalUnits( aPad->GetSize() ).c_str() );
1424 
1425  if( (aPad->GetDelta().GetWidth()) != 0 || (aPad->GetDelta().GetHeight() != 0 ) )
1426  m_out->Print( 0, " (rect_delta %s)", FormatInternalUnits( aPad->GetDelta() ).c_str() );
1427 
1428  wxSize sz = aPad->GetDrillSize();
1429  wxPoint shapeoffset = aPad->GetOffset();
1430 
1431  if( (sz.GetWidth() > 0) || (sz.GetHeight() > 0) ||
1432  (shapeoffset.x != 0) || (shapeoffset.y != 0) )
1433  {
1434  m_out->Print( 0, " (drill" );
1435 
1436  if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
1437  m_out->Print( 0, " oval" );
1438 
1439  if( sz.GetWidth() > 0 )
1440  m_out->Print( 0, " %s", FormatInternalUnits( sz.GetWidth() ).c_str() );
1441 
1442  if( sz.GetHeight() > 0 && sz.GetWidth() != sz.GetHeight() )
1443  m_out->Print( 0, " %s", FormatInternalUnits( sz.GetHeight() ).c_str() );
1444 
1445  if( (shapeoffset.x != 0) || (shapeoffset.y != 0) )
1446  m_out->Print( 0, " (offset %s)", FormatInternalUnits( aPad->GetOffset() ).c_str() );
1447 
1448  m_out->Print( 0, ")" );
1449  }
1450 
1451  // Add pad property, if exists.
1452  if( property )
1453  m_out->Print( 0, " (property %s)", property );
1454 
1455  formatLayers( aPad->GetLayerSet() );
1456 
1457  if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
1458  {
1459  if( aPad->GetRemoveUnconnected() )
1460  {
1461  m_out->Print( 0, " (remove_unused_layers)" );
1462 
1463  if( aPad->GetKeepTopBottom() )
1464  m_out->Print( 0, " (keep_end_layers)" );
1465  }
1466  }
1467 
1468  // Output the radius ratio for rounded and chamfered rect pads
1469  if( aPad->GetShape() == PAD_SHAPE::ROUNDRECT || aPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT)
1470  {
1471  m_out->Print( 0, " (roundrect_rratio %s)",
1472  Double2Str( aPad->GetRoundRectRadiusRatio() ).c_str() );
1473  }
1474 
1475  // Output the chamfer corners for chamfered rect pads
1476  if( aPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT)
1477  {
1478  m_out->Print( 0, "\n" );
1479 
1480  m_out->Print( aNestLevel+1, "(chamfer_ratio %s)",
1481  Double2Str( aPad->GetChamferRectRatio() ).c_str() );
1482 
1483  m_out->Print( 0, " (chamfer" );
1484 
1485  if( ( aPad->GetChamferPositions() & RECT_CHAMFER_TOP_LEFT ) )
1486  m_out->Print( 0, " top_left" );
1487 
1488  if( ( aPad->GetChamferPositions() & RECT_CHAMFER_TOP_RIGHT ) )
1489  m_out->Print( 0, " top_right" );
1490 
1491  if( ( aPad->GetChamferPositions() & RECT_CHAMFER_BOTTOM_LEFT ) )
1492  m_out->Print( 0, " bottom_left" );
1493 
1495  m_out->Print( 0, " bottom_right" );
1496 
1497  m_out->Print( 0, ")" );
1498  }
1499 
1500  std::string output;
1501 
1502  // Unconnected pad is default net so don't save it.
1503  if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aPad->GetNetCode() != NETINFO_LIST::UNCONNECTED )
1504  {
1505  StrPrintf( &output, " (net %d %s)", m_mapping->Translate( aPad->GetNetCode() ),
1506  m_out->Quotew( aPad->GetNetname() ).c_str() );
1507  }
1508 
1509  // Pin functions and types are closely related to nets, so if CTL_OMIT_NETS is set, omit
1510  // them as well (for instance when saved from library editor).
1511  if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
1512  {
1513  if( !aPad->GetPinFunction().IsEmpty() )
1514  {
1515  StrPrintf( &output, " (pinfunction %s)",
1516  m_out->Quotew( aPad->GetPinFunction() ).c_str() );
1517  }
1518 
1519  if( !aPad->GetPinType().IsEmpty() )
1520  {
1521  StrPrintf( &output, " (pintype %s)",
1522  m_out->Quotew( aPad->GetPinType() ).c_str() );
1523  }
1524  }
1525 
1526  if( aPad->GetPadToDieLength() != 0 )
1527  {
1528  StrPrintf( &output, " (die_length %s)",
1529  FormatInternalUnits( aPad->GetPadToDieLength() ).c_str() );
1530  }
1531 
1532  if( aPad->GetLocalSolderMaskMargin() != 0 )
1533  {
1534  StrPrintf( &output, " (solder_mask_margin %s)",
1535  FormatInternalUnits( aPad->GetLocalSolderMaskMargin() ).c_str() );
1536  }
1537 
1538  if( aPad->GetLocalSolderPasteMargin() != 0 )
1539  {
1540  StrPrintf( &output, " (solder_paste_margin %s)",
1541  FormatInternalUnits( aPad->GetLocalSolderPasteMargin() ).c_str() );
1542  }
1543 
1544  if( aPad->GetLocalSolderPasteMarginRatio() != 0 )
1545  {
1546  StrPrintf( &output, " (solder_paste_margin_ratio %s)",
1547  Double2Str( aPad->GetLocalSolderPasteMarginRatio() ).c_str() );
1548  }
1549 
1550  if( aPad->GetLocalClearance() != 0 )
1551  {
1552  StrPrintf( &output, " (clearance %s)",
1553  FormatInternalUnits( aPad->GetLocalClearance() ).c_str() );
1554  }
1555 
1557  {
1558  StrPrintf( &output, " (zone_connect %d)",
1559  static_cast<int>( aPad->GetEffectiveZoneConnection() ) );
1560  }
1561 
1562  if( aPad->GetThermalSpokeWidth() != 0 )
1563  {
1564  StrPrintf( &output, " (thermal_width %s)",
1565  FormatInternalUnits( aPad->GetThermalSpokeWidth() ).c_str() );
1566  }
1567 
1568  if( aPad->GetThermalGap() != 0 )
1569  {
1570  StrPrintf( &output, " (thermal_gap %s)",
1571  FormatInternalUnits( aPad->GetThermalGap() ).c_str() );
1572  }
1573 
1574  if( output.size() )
1575  {
1576  m_out->Print( 0, "\n" );
1577  m_out->Print( aNestLevel+1, "%s", output.c_str()+1 ); // +1 skips 1st space on 1st element
1578  }
1579 
1580  if( aPad->GetShape() == PAD_SHAPE::CUSTOM )
1581  {
1582  m_out->Print( 0, "\n");
1583  m_out->Print( aNestLevel+1, "(options" );
1584 
1586  m_out->Print( 0, " (clearance convexhull)" );
1587  #if 1 // Set to 1 to output the default option
1588  else
1589  m_out->Print( 0, " (clearance outline)" );
1590  #endif
1591 
1592  // Output the anchor pad shape (circle/rect)
1593  if( aPad->GetAnchorPadShape() == PAD_SHAPE::RECT )
1594  shape = "rect";
1595  else
1596  shape = "circle";
1597 
1598  m_out->Print( 0, " (anchor %s)", shape );
1599 
1600  m_out->Print( 0, ")"); // end of (options ...
1601 
1602  // Output graphic primitive of the pad shape
1603  m_out->Print( 0, "\n");
1604  m_out->Print( aNestLevel+1, "(primitives" );
1605 
1606  int nested_level = aNestLevel+2;
1607 
1608  // Output all basic shapes
1609  for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives() )
1610  {
1611  m_out->Print( 0, "\n");
1612 
1613  switch( primitive->GetShape() )
1614  {
1615  case SHAPE_T::SEGMENT:
1616  m_out->Print( nested_level, "(gr_line (start %s) (end %s)",
1617  FormatInternalUnits( primitive->GetStart() ).c_str(),
1618  FormatInternalUnits( primitive->GetEnd() ).c_str() );
1619  break;
1620 
1621  case SHAPE_T::RECT:
1622  m_out->Print( nested_level, "(gr_rect (start %s) (end %s)",
1623  FormatInternalUnits( primitive->GetStart() ).c_str(),
1624  FormatInternalUnits( primitive->GetEnd() ).c_str() );
1625  break;
1626 
1627  case SHAPE_T::ARC:
1628  m_out->Print( nested_level, "(gr_arc (start %s) (end %s) (angle %s)",
1629  FormatInternalUnits( primitive->GetStart() ).c_str(),
1630  FormatInternalUnits( primitive->GetEnd() ).c_str(),
1631  FormatAngle( primitive->GetAngle() ).c_str() );
1632  break;
1633 
1634  case SHAPE_T::CIRCLE:
1635  m_out->Print( nested_level, "(gr_circle (center %s) (end %s)",
1636  FormatInternalUnits( primitive->GetStart() ).c_str(),
1637  FormatInternalUnits( primitive->GetEnd() ).c_str() );
1638  break;
1639 
1640  case SHAPE_T::BEZIER:
1641  m_out->Print( nested_level, "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
1642  FormatInternalUnits( primitive->GetStart() ).c_str(),
1643  FormatInternalUnits( primitive->GetBezierC1() ).c_str(),
1644  FormatInternalUnits( primitive->GetBezierC2() ).c_str(),
1645  FormatInternalUnits( primitive->GetEnd() ).c_str() );
1646  break;
1647 
1648  case SHAPE_T::POLY:
1649  if( primitive->GetPolyShape().COutline( 0 ).CPoints().size() < 2 )
1650  break; // Malformed polygon.
1651 
1652  {
1653  m_out->Print( nested_level, "(gr_poly (pts\n");
1654 
1655  // Write the polygon corners coordinates:
1656  int newLine = 0;
1657 
1658  for( const VECTOR2I &pt : primitive->GetPolyShape().COutline( 0 ).CPoints() )
1659  {
1660  if( newLine == 0 )
1661  m_out->Print( nested_level+1, "(xy %s)",
1662  FormatInternalUnits( (wxPoint) pt ).c_str() );
1663  else
1664  m_out->Print( 0, " (xy %s)",
1665  FormatInternalUnits( (wxPoint) pt ).c_str() );
1666 
1667  if( ++newLine > 4 || !ADVANCED_CFG::GetCfg().m_CompactSave )
1668  {
1669  newLine = 0;
1670  m_out->Print( 0, "\n" );
1671  }
1672  }
1673 
1674  m_out->Print( newLine ? 0 : nested_level, ")" );
1675  }
1676  break;
1677 
1678  default:
1679  break;
1680  }
1681 
1682  m_out->Print( 0, " (width %s)",
1683  FormatInternalUnits( primitive->GetWidth() ).c_str() );
1684 
1685  if( primitive->IsFilled() )
1686  m_out->Print( 0, " (fill yes)" );
1687 
1688  m_out->Print( 0, ")" );
1689  }
1690 
1691  m_out->Print( 0, "\n");
1692  m_out->Print( aNestLevel+1, ")" ); // end of (basic_shapes
1693  }
1694 
1695  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aPad->m_Uuid.AsString() ) );
1696 
1697  m_out->Print( 0, ")\n" );
1698 }
1699 
1700 
1701 void PCB_IO::format( const PCB_TEXT* aText, int aNestLevel ) const
1702 {
1703  m_out->Print( aNestLevel, "(gr_text %s (at %s",
1704  m_out->Quotew( aText->GetText() ).c_str(),
1705  FormatInternalUnits( aText->GetTextPos() ).c_str() );
1706 
1707  if( aText->GetTextAngle() != 0.0 )
1708  m_out->Print( 0, " %s", FormatAngle( aText->GetTextAngle() ).c_str() );
1709 
1710  m_out->Print( 0, ")" );
1711 
1712  formatLayer( aText );
1713 
1714  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aText->m_Uuid.AsString() ) );
1715 
1716  m_out->Print( 0, "\n" );
1717 
1718  // PCB_TEXTS are never hidden, so always omit "hide" attribute
1719  aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
1720 
1721  m_out->Print( aNestLevel, ")\n" );
1722 }
1723 
1724 
1725 void PCB_IO::format( const PCB_GROUP* aGroup, int aNestLevel ) const
1726 {
1727  // Don't write empty groups
1728  if( aGroup->GetItems().empty() )
1729  return;
1730 
1731  m_out->Print( aNestLevel, "(group %s%s (id %s)\n",
1732  m_out->Quotew( aGroup->GetName() ).c_str(),
1733  aGroup->IsLocked() ? " locked" : "",
1734  TO_UTF8( aGroup->m_Uuid.AsString() ) );
1735 
1736  m_out->Print( aNestLevel + 1, "(members\n" );
1737 
1738  wxArrayString memberIds;
1739 
1740  for( BOARD_ITEM* member : aGroup->GetItems() )
1741  memberIds.Add( member->m_Uuid.AsString() );
1742 
1743  memberIds.Sort();
1744 
1745  for( const wxString& memberId : memberIds )
1746  m_out->Print( aNestLevel + 2, "%s\n", TO_UTF8( memberId ) );
1747 
1748  m_out->Print( aNestLevel + 1, ")\n" ); // Close `members` token.
1749  m_out->Print( aNestLevel, ")\n" ); // Close `group` token.
1750 }
1751 
1752 
1753 void PCB_IO::format( const FP_TEXT* aText, int aNestLevel ) const
1754 {
1755  std::string type;
1756 
1757  switch( aText->GetType() )
1758  {
1759  case FP_TEXT::TEXT_is_REFERENCE: type = "reference"; break;
1760  case FP_TEXT::TEXT_is_VALUE: type = "value"; break;
1761  case FP_TEXT::TEXT_is_DIVERS: type = "user";
1762  }
1763 
1764  m_out->Print( aNestLevel, "(fp_text %s %s (at %s",
1765  type.c_str(),
1766  m_out->Quotew( aText->GetText() ).c_str(),
1767  FormatInternalUnits( aText->GetPos0() ).c_str() );
1768 
1769  // Due to Pcbnew history, fp_text angle is saved as an absolute on screen angle,
1770  // but internally the angle is held relative to its parent footprint. parent
1771  // may be NULL when saving a footprint outside a BOARD.
1772  double orient = aText->GetTextAngle();
1773  FOOTPRINT* parent = (FOOTPRINT*) aText->GetParent();
1774 
1775  if( parent )
1776  {
1777  // GetTextAngle() is always in -360..+360 range because of
1778  // FP_TEXT::SetTextAngle(), but summing that angle with an
1779  // additional board angle could kick sum up >= 360 or <= -360, so to have
1780  // consistent results, normalize again for the BOARD save. A footprint
1781  // save does not use this code path since parent is NULL.
1782 #if 0
1783  // This one could be considered reasonable if you like positive angles
1784  // in your board text.
1785  orient = NormalizeAnglePos( orient + parent->GetOrientation() );
1786 #else
1787  // Choose compatibility for now, even though this is only a 720 degree clamp
1788  // with two possible values for every angle.
1789  orient = NormalizeAngle360Min( orient + parent->GetOrientation() );
1790 #endif
1791  }
1792 
1793  if( orient != 0.0 )
1794  m_out->Print( 0, " %s", FormatAngle( orient ).c_str() );
1795 
1796  if( !aText->IsKeepUpright() )
1797  m_out->Print( 0, " unlocked" );
1798 
1799  m_out->Print( 0, ")" );
1800  formatLayer( aText );
1801 
1802  if( !aText->IsVisible() )
1803  m_out->Print( 0, " hide" );
1804 
1805  m_out->Print( 0, "\n" );
1806 
1807  aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
1808 
1809  m_out->Print( aNestLevel + 1, "(tstamp %s)\n", TO_UTF8( aText->m_Uuid.AsString() ) );
1810 
1811  m_out->Print( aNestLevel, ")\n" );
1812 }
1813 
1814 
1815 void PCB_IO::format( const PCB_TRACK* aTrack, int aNestLevel ) const
1816 {
1817  if( aTrack->Type() == PCB_VIA_T )
1818  {
1819  PCB_LAYER_ID layer1, layer2;
1820 
1821  const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
1822  BOARD* board = (BOARD*) via->GetParent();
1823 
1824  wxCHECK_RET( board != nullptr, wxT( "Via " ) +
1825  via->GetSelectMenuText( EDA_UNITS::MILLIMETRES ) + wxT( " has no parent." ) );
1826 
1827  m_out->Print( aNestLevel, "(via" );
1828 
1829  via->LayerPair( &layer1, &layer2 );
1830 
1831  switch( via->GetViaType() )
1832  {
1833  case VIATYPE::THROUGH: // Default shape not saved.
1834  break;
1835 
1836  case VIATYPE::BLIND_BURIED:
1837  m_out->Print( 0, " blind" );
1838  break;
1839 
1840  case VIATYPE::MICROVIA:
1841  m_out->Print( 0, " micro" );
1842  break;
1843 
1844  default:
1845  THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), via->GetViaType() ) );
1846  }
1847 
1848  if( via->IsLocked() )
1849  m_out->Print( 0, " locked" );
1850 
1851  m_out->Print( 0, " (at %s) (size %s)",
1852  FormatInternalUnits( aTrack->GetStart() ).c_str(),
1853  FormatInternalUnits( aTrack->GetWidth() ).c_str() );
1854 
1855  // Old boards were using UNDEFINED_DRILL_DIAMETER value in file for via drill when
1856  // via drill was the netclass value.
1857  // recent boards always set the via drill to the actual value, but now we need to
1858  // always store the drill value, because netclass value is not stored in the board file.
1859  // Otherwise the drill value of some (old) vias can be unknown
1860  if( via->GetDrill() != UNDEFINED_DRILL_DIAMETER )
1861  m_out->Print( 0, " (drill %s)", FormatInternalUnits( via->GetDrill() ).c_str() );
1862  else // Probably old board!
1863  m_out->Print( 0, " (drill %s)", FormatInternalUnits( via->GetDrillValue() ).c_str() );
1864 
1865  m_out->Print( 0, " (layers %s %s)",
1866  m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
1867  m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
1868 
1869  if( via->GetRemoveUnconnected() )
1870  {
1871  m_out->Print( 0, " (remove_unused_layers)" );
1872 
1873  if( via->GetKeepTopBottom() )
1874  m_out->Print( 0, " (keep_end_layers)" );
1875  }
1876 
1877  if( via->GetIsFree() )
1878  m_out->Print( 0, " (free)" );
1879  }
1880  else if( aTrack->Type() == PCB_ARC_T )
1881  {
1882  const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
1883  std::string locked = arc->IsLocked() ? " locked" : "";
1884 
1885  m_out->Print( aNestLevel, "(arc%s (start %s) (mid %s) (end %s) (width %s)",
1886  locked.c_str(),
1887  FormatInternalUnits( arc->GetStart() ).c_str(),
1888  FormatInternalUnits( arc->GetMid() ).c_str(),
1889  FormatInternalUnits( arc->GetEnd() ).c_str(),
1890  FormatInternalUnits( arc->GetWidth() ).c_str() );
1891 
1892  m_out->Print( 0, " (layer %s)", m_out->Quotew( LSET::Name( arc->GetLayer() ) ).c_str() );
1893  }
1894  else
1895  {
1896  std::string locked = aTrack->IsLocked() ? " locked" : "";
1897 
1898  m_out->Print( aNestLevel, "(segment%s (start %s) (end %s) (width %s)",
1899  locked.c_str(),
1900  FormatInternalUnits( aTrack->GetStart() ).c_str(),
1901  FormatInternalUnits( aTrack->GetEnd() ).c_str(),
1902  FormatInternalUnits( aTrack->GetWidth() ).c_str() );
1903 
1904  m_out->Print( 0, " (layer %s)", m_out->Quotew( LSET::Name( aTrack->GetLayer() ) ).c_str() );
1905  }
1906 
1907  m_out->Print( 0, " (net %d)", m_mapping->Translate( aTrack->GetNetCode() ) );
1908 
1909  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTrack->m_Uuid.AsString() ) );
1910 
1911  m_out->Print( 0, ")\n" );
1912 }
1913 
1914 
1915 void PCB_IO::format( const ZONE* aZone, int aNestLevel ) const
1916 {
1917  std::string locked = aZone->IsLocked() ? " locked" : "";
1918 
1919  // Save the NET info; For keepout zones, net code and net name are irrelevant
1920  // so be sure a dummy value is stored, just for ZONE compatibility
1921  // (perhaps netcode and netname should be not stored)
1922  m_out->Print( aNestLevel, "(zone%s (net %d) (net_name %s)",
1923  locked.c_str(),
1924  aZone->GetIsRuleArea() ? 0 : m_mapping->Translate( aZone->GetNetCode() ),
1925  m_out->Quotew( aZone->GetIsRuleArea() ? wxT("") : aZone->GetNetname() ).c_str() );
1926 
1927  // If a zone exists on multiple layers, format accordingly
1928  if( aZone->GetLayerSet().count() > 1 )
1929  {
1930  formatLayers( aZone->GetLayerSet() );
1931  }
1932  else
1933  {
1934  formatLayer( aZone );
1935  }
1936 
1937  m_out->Print( 0, " (tstamp %s)", TO_UTF8( aZone->m_Uuid.AsString() ) );
1938 
1939  if( !aZone->GetZoneName().empty() )
1940  m_out->Print( 0, " (name %s)", m_out->Quotew( aZone->GetZoneName() ).c_str() );
1941 
1942  // Save the outline aux info
1943  std::string hatch;
1944 
1945  switch( aZone->GetHatchStyle() )
1946  {
1947  default:
1948  case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: hatch = "none"; break;
1949  case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: hatch = "edge"; break;
1950  case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: hatch = "full"; break;
1951  }
1952 
1953  m_out->Print( 0, " (hatch %s %s)\n", hatch.c_str(),
1954  FormatInternalUnits( aZone->GetBorderHatchPitch() ).c_str() );
1955 
1956  if( aZone->GetPriority() > 0 )
1957  m_out->Print( aNestLevel+1, "(priority %d)\n", aZone->GetPriority() );
1958 
1959  m_out->Print( aNestLevel+1, "(connect_pads" );
1960 
1961  switch( aZone->GetPadConnection() )
1962  {
1963  default:
1964  case ZONE_CONNECTION::THERMAL: // Default option not saved or loaded.
1965  break;
1966 
1968  m_out->Print( 0, " thru_hole_only" );
1969  break;
1970 
1971  case ZONE_CONNECTION::FULL:
1972  m_out->Print( 0, " yes" );
1973  break;
1974 
1975  case ZONE_CONNECTION::NONE:
1976  m_out->Print( 0, " no" );
1977  break;
1978  }
1979 
1980  m_out->Print( 0, " (clearance %s))\n",
1981  FormatInternalUnits( aZone->GetLocalClearance() ).c_str() );
1982 
1983  m_out->Print( aNestLevel+1, "(min_thickness %s)",
1984  FormatInternalUnits( aZone->GetMinThickness() ).c_str() );
1985 
1986  // write it only if V 6.O version option is used (i.e. do not write if the "legacy"
1987  // algorithm is used)
1988  if( !aZone->GetFilledPolysUseThickness() )
1989  m_out->Print( 0, " (filled_areas_thickness no)" );
1990 
1991  m_out->Print( 0, "\n" );
1992 
1993  if( aZone->GetIsRuleArea() )
1994  {
1995  m_out->Print( aNestLevel + 1,
1996  "(keepout (tracks %s) (vias %s) (pads %s ) (copperpour %s) "
1997  "(footprints %s))\n",
1998  aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
1999  aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
2000  aZone->GetDoNotAllowPads() ? "not_allowed" : "allowed",
2001  aZone->GetDoNotAllowCopperPour() ? "not_allowed" : "allowed",
2002  aZone->GetDoNotAllowFootprints() ? "not_allowed" : "allowed" );
2003  }
2004 
2005  m_out->Print( aNestLevel + 1, "(fill" );
2006 
2007  // Default is not filled.
2008  if( aZone->IsFilled() )
2009  m_out->Print( 0, " yes" );
2010 
2011  // Default is polygon filled.
2012  if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
2013  m_out->Print( 0, " (mode hatch)" );
2014 
2015  m_out->Print( 0, " (thermal_gap %s) (thermal_bridge_width %s)",
2016  FormatInternalUnits( aZone->GetThermalReliefGap() ).c_str(),
2017  FormatInternalUnits( aZone->GetThermalReliefSpokeWidth() ).c_str() );
2018 
2020  {
2021  m_out->Print( 0, " (smoothing" );
2022 
2023  switch( aZone->GetCornerSmoothingType() )
2024  {
2026  m_out->Print( 0, " chamfer" );
2027  break;
2028 
2030  m_out->Print( 0, " fillet" );
2031  break;
2032 
2033  default:
2034  THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
2035  aZone->GetCornerSmoothingType() ) );
2036  }
2037  m_out->Print( 0, ")" );
2038 
2039  if( aZone->GetCornerRadius() != 0 )
2040  m_out->Print( 0, " (radius %s)",
2041  FormatInternalUnits( aZone->GetCornerRadius() ).c_str() );
2042  }
2043 
2045  {
2046  m_out->Print( 0, " (island_removal_mode %d) (island_area_min %s)",
2047  static_cast<int>( aZone->GetIslandRemovalMode() ),
2048  FormatInternalUnits( aZone->GetMinIslandArea() / IU_PER_MM ).c_str() );
2049  }
2050 
2051  if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
2052  {
2053  m_out->Print( 0, "\n" );
2054  m_out->Print( aNestLevel+2, "(hatch_thickness %s) (hatch_gap %s) (hatch_orientation %s)",
2055  FormatInternalUnits( aZone->GetHatchThickness() ).c_str(),
2056  FormatInternalUnits( aZone->GetHatchGap() ).c_str(),
2057  Double2Str( aZone->GetHatchOrientation() ).c_str() );
2058 
2059  if( aZone->GetHatchSmoothingLevel() > 0 )
2060  {
2061  m_out->Print( 0, "\n" );
2062  m_out->Print( aNestLevel+2, "(hatch_smoothing_level %d) (hatch_smoothing_value %s)",
2063  aZone->GetHatchSmoothingLevel(),
2064  Double2Str( aZone->GetHatchSmoothingValue() ).c_str() );
2065  }
2066 
2067  m_out->Print( 0, "\n" );
2068  m_out->Print( aNestLevel+2, "(hatch_border_algorithm %s) (hatch_min_hole_area %s)",
2069  aZone->GetHatchBorderAlgorithm() ? "hatch_thickness" : "min_thickness",
2070  Double2Str( aZone->GetHatchHoleMinArea() ).c_str() );
2071  }
2072 
2073  m_out->Print( 0, ")\n" );
2074 
2075  if( aZone->GetNumCorners() )
2076  {
2077  SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0);
2078 
2079  for( auto& chain : poly )
2080  {
2081  m_out->Print( aNestLevel + 1, "(polygon\n" );
2082  m_out->Print( aNestLevel + 2, "(pts" );
2083 
2084  bool need_newline = true;
2085 
2086  for( int ii = 0; ii < chain.PointCount(); ++ii )
2087  {
2088  int nestLevel = 0;
2089 
2090  if( !( ii % 4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) // newline every 4 pts
2091  {
2092  nestLevel = aNestLevel + 3;
2093  m_out->Print( 0, "\n" );
2094  need_newline = false;
2095  }
2096 
2097  int ind = chain.ArcIndex( ii );
2098 
2099  if( ind < 0 )
2100  {
2101  m_out->Print( nestLevel, "%s(xy %s)",
2102  nestLevel ? "" : " ",
2103  FormatInternalUnits( chain.CPoint( ii ) ).c_str() );
2104  need_newline = true;
2105  }
2106  else
2107  {
2108  auto& arc = chain.Arc( ind );
2109  m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s))",
2110  nestLevel ? "" : " ",
2111  FormatInternalUnits( arc.GetP0() ).c_str(),
2112  FormatInternalUnits( arc.GetArcMid() ).c_str(),
2113  FormatInternalUnits( arc.GetP1() ).c_str() );
2114  need_newline = true;
2115 
2116  do
2117  {
2118  ++ii;
2119  } while( ii < chain.PointCount() && chain.ArcIndex( ii ) == ind );
2120 
2121  --ii;
2122  }
2123  }
2124 
2125  if( need_newline )
2126  m_out->Print( 0, "\n" );
2127 
2128  m_out->Print( aNestLevel + 2, ")\n" );
2129  m_out->Print( aNestLevel + 1, ")\n" );
2130 
2131  }
2132  }
2133 
2134  // Save the PolysList (filled areas)
2135  for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
2136  {
2137  const SHAPE_POLY_SET& fv = aZone->GetFilledPolysList( layer );
2138 
2139  for( int ii = 0; ii < fv.OutlineCount(); ++ii )
2140  {
2141  m_out->Print( aNestLevel + 1, "(filled_polygon\n" );
2142  m_out->Print( aNestLevel + 2, "(layer %s)\n",
2143  m_out->Quotew( LSET::Name( layer ) ).c_str() );
2144 
2145  if( aZone->IsIsland( layer, ii ) )
2146  m_out->Print( aNestLevel + 2, "(island)\n" );
2147 
2148  m_out->Print( aNestLevel + 2, "(pts" );
2149 
2150  const SHAPE_LINE_CHAIN& chain = fv.COutline( ii );
2151 
2152  bool need_newline = true;
2153 
2154  for( int jj = 0; jj < chain.PointCount(); ++jj )
2155  {
2156  int nestLevel = 0;
2157 
2158  if( !( jj%4 ) || !ADVANCED_CFG::GetCfg().m_CompactSave ) // newline every 4 pts
2159  {
2160  nestLevel = aNestLevel + 3;
2161  m_out->Print( 0, "\n" );
2162  need_newline = false;
2163  }
2164 
2165  int ind = chain.ArcIndex( jj );
2166 
2167  if( ind < 0 )
2168  {
2169  m_out->Print( nestLevel, "%s(xy %s)",
2170  nestLevel ? "" : " ",
2171  FormatInternalUnits( chain.CPoint( jj ) ).c_str() );
2172  need_newline = true;
2173  }
2174  else
2175  {
2176  auto& arc = chain.Arc( ind );
2177  m_out->Print( aNestLevel, "%s(arc (start %s) (mid %s) (end %s))",
2178  nestLevel ? "" : " ",
2179  FormatInternalUnits( arc.GetP0() ).c_str(),
2180  FormatInternalUnits( arc.GetArcMid() ).c_str(),
2181  FormatInternalUnits( arc.GetP1() ).c_str() );
2182  need_newline = true;
2183 
2184  do
2185  {
2186  ++jj;
2187  } while( jj < chain.PointCount() && chain.ArcIndex( jj ) == ind );
2188 
2189  --jj;
2190  }
2191  }
2192 
2193  if( need_newline )
2194  m_out->Print( 0, "\n" );
2195 
2196  m_out->Print( aNestLevel+2, ")\n" );
2197  m_out->Print( aNestLevel+1, ")\n" );
2198  }
2199 
2200  // Save the filling segments list
2201  const auto& segs = aZone->FillSegments( layer );
2202 
2203  if( segs.size() )
2204  {
2205  m_out->Print( aNestLevel + 1, "(fill_segments\n" );
2206  m_out->Print( aNestLevel + 2, "(layer %s)\n",
2207  TO_UTF8( BOARD::GetStandardLayerName( layer ) ) );
2208 
2209  for( ZONE_SEGMENT_FILL::const_iterator it = segs.begin(); it != segs.end(); ++it )
2210  {
2211  m_out->Print( aNestLevel + 2, "(pts (xy %s) (xy %s))\n",
2212  FormatInternalUnits( wxPoint( it->A ) ).c_str(),
2213  FormatInternalUnits( wxPoint( it->B ) ).c_str() );
2214  }
2215 
2216  m_out->Print( aNestLevel + 1, ")\n" );
2217  }
2218  }
2219 
2220  m_out->Print( aNestLevel, ")\n" );
2221 }
2222 
2223 
2224 PCB_IO::PCB_IO( int aControlFlags ) :
2225  m_cache( nullptr ),
2226  m_ctl( aControlFlags ),
2227  m_parser( new PCB_PARSER() ),
2228  m_mapping( new NETINFO_MAPPING() )
2229 {
2230  init( nullptr );
2231  m_out = &m_sf;
2232 }
2233 
2234 
2236 {
2237  delete m_cache;
2238  delete m_parser;
2239  delete m_mapping;
2240 }
2241 
2242 
2243 BOARD* PCB_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties,
2244  PROJECT* aProject, PROGRESS_REPORTER* aProgressReporter )
2245 {
2246  FILE_LINE_READER reader( aFileName );
2247 
2248  unsigned lineCount = 0;
2249 
2250  if( aProgressReporter )
2251  {
2252  aProgressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
2253 
2254  if( !aProgressReporter->KeepRefreshing() )
2255  THROW_IO_ERROR( _( "Open cancelled by user." ) );
2256 
2257  while( reader.ReadLine() )
2258  lineCount++;
2259 
2260  reader.Rewind();
2261  }
2262 
2263  BOARD* board = DoLoad( reader, aAppendToMe, aProperties, aProgressReporter, lineCount );
2264 
2265  // Give the filename to the board if it's new
2266  if( !aAppendToMe )
2267  board->SetFileName( aFileName );
2268 
2269  return board;
2270 }
2271 
2272 
2273 BOARD* PCB_IO::DoLoad( LINE_READER& aReader, BOARD* aAppendToMe, const PROPERTIES* aProperties,
2274  PROGRESS_REPORTER* aProgressReporter, unsigned aLineCount)
2275 {
2276  init( aProperties );
2277 
2278  m_parser->SetLineReader( &aReader );
2279  m_parser->SetBoard( aAppendToMe );
2280  m_parser->SetProgressReporter( aProgressReporter, &aReader, aLineCount );
2281 
2282  BOARD* board;
2283 
2284  try
2285  {
2286  board = dynamic_cast<BOARD*>( m_parser->Parse() );
2287  }
2288  catch( const FUTURE_FORMAT_ERROR& )
2289  {
2290  // Don't wrap a FUTURE_FORMAT_ERROR in another
2291  throw;
2292  }
2293  catch( const PARSE_ERROR& parse_error )
2294  {
2295  if( m_parser->IsTooRecent() )
2296  throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() );
2297  else
2298  throw;
2299  }
2300 
2301  if( !board )
2302  {
2303  // The parser loaded something that was valid, but wasn't a board.
2304  THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), m_parser->CurSource(),
2305  m_parser->CurLine(), m_parser->CurLineNumber(), m_parser->CurOffset() );
2306  }
2307 
2308  return board;
2309 }
2310 
2311 
2312 void PCB_IO::init( const PROPERTIES* aProperties )
2313 {
2314  m_board = nullptr;
2315  m_reader = nullptr;
2316  m_props = aProperties;
2317 }
2318 
2319 
2320 void PCB_IO::validateCache( const wxString& aLibraryPath, bool checkModified )
2321 {
2322  if( !m_cache || !m_cache->IsPath( aLibraryPath ) || ( checkModified && m_cache->IsModified() ) )
2323  {
2324  // a spectacular episode in memory management:
2325  delete m_cache;
2326  m_cache = new FP_CACHE( this, aLibraryPath );
2327  m_cache->Load();
2328  }
2329 }
2330 
2331 
2332 void PCB_IO::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibPath,
2333  bool aBestEfforts, const PROPERTIES* aProperties )
2334 {
2335  LOCALE_IO toggle; // toggles on, then off, the C locale.
2336  wxDir dir( aLibPath );
2337  wxString errorMsg;
2338 
2339  init( aProperties );
2340 
2341  try
2342  {
2343  validateCache( aLibPath );
2344  }
2345  catch( const IO_ERROR& ioe )
2346  {
2347  errorMsg = ioe.What();
2348  }
2349 
2350  // Some of the files may have been parsed correctly so we want to add the valid files to
2351  // the library.
2352 
2353  for( const auto& footprint : m_cache->GetFootprints() )
2354  aFootprintNames.Add( footprint.first );
2355 
2356  if( !errorMsg.IsEmpty() && !aBestEfforts )
2357  THROW_IO_ERROR( errorMsg );
2358 }
2359 
2360 
2361 const FOOTPRINT* PCB_IO::getFootprint( const wxString& aLibraryPath,
2362  const wxString& aFootprintName,
2363  const PROPERTIES* aProperties,
2364  bool checkModified )
2365 {
2366  LOCALE_IO toggle; // toggles on, then off, the C locale.
2367 
2368  init( aProperties );
2369 
2370  try
2371  {
2372  validateCache( aLibraryPath, checkModified );
2373  }
2374  catch( const IO_ERROR& )
2375  {
2376  // do nothing with the error
2377  }
2378 
2379  FOOTPRINT_MAP& footprints = m_cache->GetFootprints();
2380  FOOTPRINT_MAP::const_iterator it = footprints.find( aFootprintName );
2381 
2382  if( it == footprints.end() )
2383  return nullptr;
2384 
2385  return it->second->GetFootprint();
2386 }
2387 
2388 
2389 const FOOTPRINT* PCB_IO::GetEnumeratedFootprint( const wxString& aLibraryPath,
2390  const wxString& aFootprintName,
2391  const PROPERTIES* aProperties )
2392 {
2393  return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
2394 }
2395 
2396 
2397 bool PCB_IO::FootprintExists( const wxString& aLibraryPath, const wxString& aFootprintName,
2398  const PROPERTIES* aProperties )
2399 {
2400  // Note: checking the cache sounds like a good idea, but won't catch files which differ
2401  // only in case.
2402  //
2403  // Since this goes out to the native filesystem, we get platform differences (ie: MSW's
2404  // case-insensitive filesystem) handled "for free".
2405  // Warning: footprint names frequently contain a point. So be careful when initializing
2406  // wxFileName, and use a CTOR with extension specified
2407  wxFileName footprintFile( aLibraryPath, aFootprintName, KiCadFootprintFileExtension );
2408 
2409  return footprintFile.Exists();
2410 }
2411 
2412 
2413 FOOTPRINT* PCB_IO::FootprintLoad( const wxString& aLibraryPath,
2414  const wxString& aFootprintName,
2415  bool aKeepUUID,
2416  const PROPERTIES* aProperties )
2417 {
2418  const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
2419 
2420  if( footprint )
2421  {
2422  FOOTPRINT* copy;
2423 
2424  if( aKeepUUID )
2425  copy = static_cast<FOOTPRINT*>( footprint->Clone() );
2426  else
2427  copy = static_cast<FOOTPRINT*>( footprint->Duplicate() );
2428 
2429  copy->SetParent( nullptr );
2430  return copy;
2431  }
2432 
2433  return nullptr;
2434 }
2435 
2436 
2437 void PCB_IO::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* aFootprint,
2438  const PROPERTIES* aProperties )
2439 {
2440  LOCALE_IO toggle; // toggles on, then off, the C locale.
2441 
2442  init( aProperties );
2443 
2444  // In this public PLUGIN API function, we can safely assume it was
2445  // called for saving into a library path.
2447 
2448  validateCache( aLibraryPath );
2449 
2450  if( !m_cache->IsWritable() )
2451  {
2452  if( !m_cache->Exists() )
2453  {
2454  const wxString msg = wxString::Format( _( "Library '%s' does not exist.\n"
2455  "Would you like to create it?"),
2456  aLibraryPath );
2457 
2458  if( wxMessageBox( msg, _( "Library Not Found"), wxYES_NO | wxICON_QUESTION ) != wxYES )
2459  return;
2460 
2461  // Save throws its own IO_ERROR on failure, so no need to recreate here
2462  m_cache->Save( nullptr );
2463  }
2464  else
2465  {
2466  wxString msg = wxString::Format( _( "Library '%s' is read only." ), aLibraryPath );
2467  THROW_IO_ERROR( msg );
2468  }
2469  }
2470 
2471  wxString footprintName = aFootprint->GetFPID().GetLibItemName();
2472 
2473  FOOTPRINT_MAP& footprints = m_cache->GetFootprints();
2474 
2475  // Quietly overwrite footprint and delete footprint file from path for any by same name.
2476  wxFileName fn( aLibraryPath, aFootprint->GetFPID().GetLibItemName(),
2478 
2479  // Write through symlinks, don't replace them
2481 
2482  if( !fn.IsOk() )
2483  {
2484  THROW_IO_ERROR( wxString::Format( _( "Footprint file name '%s' is not valid." ),
2485  fn.GetFullPath() ) );
2486  }
2487 
2488  if( fn.FileExists() && !fn.IsFileWritable() )
2489  {
2490  THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete '%s'." ),
2491  fn.GetFullPath() ) );
2492  }
2493 
2494  wxString fullPath = fn.GetFullPath();
2495  wxString fullName = fn.GetFullName();
2496  FOOTPRINT_MAP::const_iterator it = footprints.find( footprintName );
2497 
2498  if( it != footprints.end() )
2499  {
2500  wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint file '%s'." ), fullPath );
2501  footprints.erase( footprintName );
2502  wxRemoveFile( fullPath );
2503  }
2504 
2505  // I need my own copy for the cache
2506  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aFootprint->Clone() );
2507 
2508  // It's orientation should be zero and it should be on the front layer.
2509  footprint->SetOrientation( 0 );
2510 
2511  if( footprint->GetLayer() != F_Cu )
2512  {
2513  PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
2514 
2515  if( cfg )
2516  footprint->Flip( footprint->GetPosition(), cfg->m_FlipLeftRight );
2517  else
2518  footprint->Flip( footprint->GetPosition(), false );
2519  }
2520 
2521  // Detach it from the board
2522  footprint->SetParent( nullptr );
2523 
2524  wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
2525  footprints.insert( footprintName,
2526  new FP_CACHE_ITEM( footprint, WX_FILENAME( fn.GetPath(), fullName ) ) );
2527  m_cache->Save( footprint );
2528 }
2529 
2530 
2531 void PCB_IO::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
2532  const PROPERTIES* aProperties )
2533 {
2534  LOCALE_IO toggle; // toggles on, then off, the C locale.
2535 
2536  init( aProperties );
2537 
2538  validateCache( aLibraryPath );
2539 
2540  if( !m_cache->IsWritable() )
2541  {
2542  THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
2543  aLibraryPath.GetData() ) );
2544  }
2545 
2546  m_cache->Remove( aFootprintName );
2547 }
2548 
2549 
2550 
2551 long long PCB_IO::GetLibraryTimestamp( const wxString& aLibraryPath ) const
2552 {
2553  return FP_CACHE::GetTimestamp( aLibraryPath );
2554 }
2555 
2556 
2557 void PCB_IO::FootprintLibCreate( const wxString& aLibraryPath, const PROPERTIES* aProperties )
2558 {
2559  if( wxDir::Exists( aLibraryPath ) )
2560  {
2561  THROW_IO_ERROR( wxString::Format( _( "Cannot overwrite library path '%s'." ),
2562  aLibraryPath.GetData() ) );
2563  }
2564 
2565  LOCALE_IO toggle;
2566 
2567  init( aProperties );
2568 
2569  delete m_cache;
2570  m_cache = new FP_CACHE( this, aLibraryPath );
2571  m_cache->Save();
2572 }
2573 
2574 
2575 bool PCB_IO::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
2576 {
2577  wxFileName fn;
2578  fn.SetPath( aLibraryPath );
2579 
2580  // Return if there is no library path to delete.
2581  if( !fn.DirExists() )
2582  return false;
2583 
2584  if( !fn.IsDirWritable() )
2585  {
2586  THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
2587  aLibraryPath.GetData() ) );
2588  }
2589 
2590  wxDir dir( aLibraryPath );
2591 
2592  if( dir.HasSubDirs() )
2593  {
2594  THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
2595  aLibraryPath.GetData() ) );
2596  }
2597 
2598  // All the footprint files must be deleted before the directory can be deleted.
2599  if( dir.HasFiles() )
2600  {
2601  unsigned i;
2602  wxFileName tmp;
2603  wxArrayString files;
2604 
2605  wxDir::GetAllFiles( aLibraryPath, &files );
2606 
2607  for( i = 0; i < files.GetCount(); i++ )
2608  {
2609  tmp = files[i];
2610 
2611  if( tmp.GetExt() != KiCadFootprintFileExtension )
2612  {
2613  THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library "
2614  "path '%s'." ),
2615  files[i].GetData(),
2616  aLibraryPath.GetData() ) );
2617  }
2618  }
2619 
2620  for( i = 0; i < files.GetCount(); i++ )
2621  wxRemoveFile( files[i] );
2622  }
2623 
2624  wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library '%s'." ),
2625  aLibraryPath.GetData() );
2626 
2627  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
2628  // we don't want that. we want bare metal portability with no UI here.
2629  if( !wxRmdir( aLibraryPath ) )
2630  {
2631  THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
2632  aLibraryPath.GetData() ) );
2633  }
2634 
2635  // For some reason removing a directory in Windows is not immediately updated. This delay
2636  // prevents an error when attempting to immediately recreate the same directory when over
2637  // writing an existing library.
2638 #ifdef __WINDOWS__
2639  wxMilliSleep( 250L );
2640 #endif
2641 
2642  if( m_cache && !m_cache->IsPath( aLibraryPath ) )
2643  {
2644  delete m_cache;
2645  m_cache = nullptr;
2646  }
2647 
2648  return true;
2649 }
2650 
2651 
2652 bool PCB_IO::IsFootprintLibWritable( const wxString& aLibraryPath )
2653 {
2654  LOCALE_IO toggle;
2655 
2656  init( nullptr );
2657 
2658  validateCache( aLibraryPath );
2659 
2660  return m_cache->IsWritable();
2661 }
void SetProgressReporter(PROGRESS_REPORTER *aProgressReporter, const LINE_READER *aLineReader, unsigned aLineCount)
Definition: pcb_parser.h:110
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
bool GetKeepTextAligned() const
std::vector< SHAPE_LINE_CHAIN > POLYGON
< represents a single polygon outline with holes.
FOOTPRINT_MAP & GetFootprints()
bool IsPolyShapeValid() const
Definition: pcb_shape.cpp:1294
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition: zone.h:524
void format(const BOARD *aBoard, int aNestLevel=0) const
bool IsLocked() const override
Definition: footprint.h:277
DIM_UNITS_FORMAT GetUnitsFormat() const
int GetLocalSolderMaskMargin() const
Definition: pad.h:383
const SHAPE_ARC & Arc(size_t aArc) const
int GetHatchGap() const
Definition: zone.h:257
int GetHeight() const
OUTPUTFORMATTER * m_out
output any Format()s to this, no ownership
const wxString & GetDescription() const
Definition: footprint.h:188
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition: lset.cpp:170
Arcs (with rounded ends)
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:80
bool FootprintExists(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=nullptr) override
Check for the existence of a footprint.
bool IsFilled() const
Definition: pcb_shape.h:75
LINE_READER * m_reader
no ownership here.
Bezier Curve.
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:214
const PAGE_INFO & GetPageSettings() const
Definition: board.h:535
bool IsModified()
Return true if the cache is not up-to-date.
bool m_CompactSave
Save files in compact display mode When is is not specified, points are written one per line.
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:100
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:538
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
Definition: typeinfo.h:84
int OutlineCount() const
Return the number of vertices in a given outline/hole.
BOARD_ITEM * Parse(const wxString &aClipboardSourceInput)
A PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
wxPoint m_GridOrigin
origin for grid offsets
bool GetDoNotAllowFootprints() const
Definition: zone.h:739
Container for project specific data.
Definition: project.h:62
void formatBoardLayers(const BOARD *aBoard, int aNestLevel=0) const
formats the board layer information
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:173
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:360
void formatLayer(const BOARD_ITEM *aItem) const
ZONES & Zones()
Definition: board.h:239
#define CTL_OMIT_AT
Omit position and rotation.
polygon (not yet used for tracks, but could be in microwave apps)
int GetLocalSolderPasteMargin() const
Definition: pad.h:390
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties, bool checkModified)
double GetRoundRectRadiusRatio() const
Definition: pad.h:520
SHAPE_POLY_SET & GetPolyShape()
Definition: pcb_shape.h:240
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
wxFileName m_lib_path
int GetHatchThickness() const
Definition: zone.h:254
const KIID_PATH & GetPath() const
Definition: footprint.h:194
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:45
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
unsigned GetPriority() const
Definition: zone.h:123
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: pcb_shape.h:134
Manage layers needed to make a physical board.
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:734
Implementation of conversion functions that require both schematic and board internal units.
This file is part of the common library.
bool IsKeepUpright() const
Definition: fp_text.h:110
void Save(FOOTPRINT *aFootprint=nullptr)
Save the footprint cache or a single footprint from it to disk.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
int GetPlacementCost180() const
Definition: footprint.h:529
#define CTL_OMIT_TSTAMPS
Omit component time stamp (useless in library)
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
const std::string KiCadFootprintFileExtension
virtual ~PCB_IO()
SHAPE_T GetShape() const
Definition: pcb_shape.h:110
FP_CACHE * m_cache
Footprint library cache.
boost::ptr_map< wxString, FP_CACHE_ITEM > FOOTPRINT_MAP
PAD_PROP GetProperty() const
Definition: pad.h:371
static constexpr double IU_PER_MM
Mock up a conversion function.
GROUPS & Groups()
The groups must maintain the following invariants.
Definition: board.h:253
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
Read a Pcbnew s-expression formatted LINE_READER object and returns the appropriate BOARD_ITEM object...
Definition: pcb_parser.h:71
PCB_TEXT & Text()
A progress reporter for use in multi-threaded environments.
int GetSize() const
Definition: pcb_target.h:62
int GetBorderHatchPitch() const
HatchBorder related methods.
Definition: zone.cpp:890
const PROPERTIES * m_props
passed via Save() or Load(), no ownership, may be NULL.
int GetExtensionOffset() const
void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControl=0) const
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:102
void init(const PROPERTIES *aProperties)
SHAPE_POLY_SET * Outline()
Definition: zone.h:321
bool GetDoNotAllowVias() const
Definition: zone.h:736
Like smd, does not appear on the solder paste layer (default)
int GetWidth() const
Definition: pcb_shape.h:97
Smd pad, appears on the solder paste layer (default)
const wxPoint & GetBezierC2() const
Definition: pcb_shape.h:116
const WX_FILENAME & GetFileName() const
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.cpp:291
wxString m_lib_raw_path
usual segment : line with rounded ends
double GetHatchSmoothingValue() const
Definition: zone.h:266
bool IsLocked() const override
Definition: pad.cpp:145
double GetOrientation() const
Definition: footprint.h:181
#define CTL_OMIT_INITIAL_COMMENTS
omit FOOTPRINT initial comments
virtual void Report(const wxString &aMessage)
Display aMessage in the progress bar dialog.
double GetTextAngle() const
Definition: eda_text.h:174
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
wxString GetName() const
Definition: wx_filename.cpp:40
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
const wxPoint & GetStart0() const
Definition: fp_shape.h:112
void formatProperties(const BOARD *aBoard, int aNestLevel=0) const
formats the Nets and Netclasses
wxString AsString() const
Definition: kiid.cpp:218
bool IsFootprintLibWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
bool GetOverrideTextEnabled() const
class PAD, a pad in a footprint
Definition: typeinfo.h:89
Abstract dimension API.
Definition: pcb_dimension.h:95
const SHAPE_POLY_SET & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:636
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:465
virtual std::string Quotes(const std::string &aWrapee) const
Check aWrapee input string for a need to be quoted (e.g.
Definition: richio.cpp:455
int GetWidth() const
Definition: pcb_track.h:102
bool IsFilled() const
Definition: zone.h:235
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControlBits) const
Output the page class to aFormatter in s-expression form.
Definition: page_info.cpp:271
void SetFullName(const wxString &aFileNameAndExtension)
Definition: wx_filename.cpp:34
A name/value tuple with unique names and optional values.
Definition: properties.h:33
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
#define CTL_FOR_LIBRARY
Format output for a footprint library instead of clipboard or BOARD.
a test point pad
virtual bool IsLocked() const
Definition: board_item.cpp:65
int PointCount() const
Return the number of points (vertices) in this line chain.
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
int GetThermalGap() const
Definition: pad.h:496
FOOTPRINT_MAP m_footprints
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:68
std::unique_ptr< FOOTPRINT > m_footprint
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:588
bool GetKeepTopBottom() const
Definition: pad.h:560
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
int GetPrecision() const
ZONE_FILL_MODE GetFillMode() const
Definition: zone.h:182
timestamp_t GetLastEditTime() const
Definition: footprint.h:327
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:116
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
PADS & Pads()
Definition: footprint.h:159
#define CTL_OMIT_FOOTPRINT_VERSION
Omit the version string from the (footprint)
Plated through hole pad.
This file contains miscellaneous commonly used macros and functions.
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:457
static const char * ShowType(LAYER_T aType)
Convert a LAYER_T enum to a string representation of the layer type.
Definition: board.cpp:425
DIM_UNITS_MODE GetUnitsMode() const
For better understanding of the points that make a dimension:
void SetOutputFormatter(OUTPUTFORMATTER *aFormatter)
void SetBoard(const BOARD *aBoard)
Set a BOARD object that is used to prepare the net code map.
Definition: netinfo.h:193
long long m_cache_timestamp
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const PROPERTIES *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
FP_TEXT & Reference()
Definition: footprint.h:458
a pad used as heat sink, usually in SMD footprints
int GetThermalReliefGap() const
Definition: zone.h:192
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:78
wxPoint GetPosition() const override
Definition: pcb_target.h:56
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
void Rewind()
Rewind the file and resets the line number back to zero.
Definition: richio.h:222
wxString GetPrefix() const
const std::map< wxString, wxString > & GetProperties() const
Definition: board.h:258
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
double GetHatchHoleMinArea() const
Definition: zone.h:269
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
FP_ZONES & Zones()
Definition: footprint.h:165
DIM_TEXT_FRAME GetTextFrame() const
virtual const wxPoint & GetStart() const
The dimension's origin is the first feature point for the dimension.
bool GetDoNotAllowPads() const
Definition: zone.h:738
long long GetTimestamp()
Definition: wx_filename.cpp:74
const wxPoint & GetOffset() const
Definition: pad.h:246
void Remove(const wxString &aFootprintName)
int GetWidth() const
Definition: pcb_target.h:65
const wxSize & GetDrillSize() const
Definition: pad.h:239
like PAD_PTH, but not plated
std::string FormatAngle(double aAngle)
Function FormatAngle converts aAngle from board units to a string appropriate for writing to file.
Definition: base_units.cpp:520
BOARD_STACKUP & GetStackupDescriptor()
PCB_IO(int aControlFlags=CTL_FOR_BOARD)
PCB_LAYER_ID
A quick note on layer IDs:
A LINE_READER that reads from an open file.
Definition: richio.h:172
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
Definition: pcb_parser.cpp:244
bool GetDoNotAllowCopperPour() const
Definition: zone.h:735
int GetExtensionHeight() const
LSET is a set of PCB_LAYER_IDs.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
pads are covered by copper
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const PROPERTIES *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PL...
int GetThermalSpokeWidth() const
Definition: pad.h:488
void SetBoard(BOARD *aBoard)
Definition: pcb_parser.h:101
int GetArrowLength() const
int GetMinThickness() const
Definition: zone.h:245
double GetOrientation() const
Return the rotation angle of the pad in a variety of units (the basic call returns tenths of degrees)...
Definition: pad.h:346
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
#define CTL_OMIT_LIBNAME
Omit lib alias when saving (used for.
wxString GetName() const
Definition: pcb_group.h:65
int GetHatchSmoothingLevel() const
Definition: zone.h:263
static const wxChar * Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:82
wxString GetSuffix() const
TEXT_TYPE GetType() const
Definition: fp_text.h:141
void formatHeader(const BOARD *aBoard, int aNestLevel=0) const
writes everything that comes before the board_items, like settings and layers etc
bool IsPath(const wxString &aPath) const
Check if aPath is the same as the current cache path.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
int GetLocalClearance(wxString *aSource) const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: zone.cpp:494
virtual bool IsVisible() const
Definition: eda_text.h:186
void formatGeneral(const BOARD *aBoard, int aNestLevel=0) const
formats the General section of the file
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
void SetOrientation(double aNewAngle)
Definition: footprint.cpp:1571
bool GetSuppressZeroes() const
const VECTOR2I & GetP0() const
Definition: shape_arc.h:111
FOOTPRINTS & Footprints()
Definition: board.h:233
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
const wxSize & GetSize() const
Definition: pad.h:229
void formatNetInformation(const BOARD *aBoard, int aNestLevel=0) const
formats the Nets and Netclasses
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: pcb_shape.h:124
#define UNDEFINED_DRILL_DIAMETER
Definition: pcb_track.h:69
ZONE_CONNECTION GetEffectiveZoneConnection(wxString *aSource=nullptr) const
Return the zone connection in effect (either locally overridden or overridden in the parent footprint...
Definition: pad.cpp:792
void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1381
Mark the center of a circle or arc with a cross shape.
Smd pad, used in BGA footprints.
bool GetDoNotAllowTracks() const
Definition: zone.h:737
static void ResolvePossibleSymlinks(wxFileName &aFilename)
Definition: wx_filename.cpp:85
const wxString & GetName() const
Definition: pad.h:130
static wxString SHAPE_T_asString(SHAPE_T a)
Definition: board_item.h:57
Definition of file extensions used in Kicad.
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:113
const wxChar *const traceKicadPcbPlugin
Flag to enable GEDA PCB plugin debug output.
const wxPoint & GetBezierC1() const
Definition: pcb_shape.h:113
int GetLocalClearance() const
Definition: footprint.h:200
const wxString & GetPinType() const
Definition: pad.h:142
PAD_SHAPE GetShape() const
Definition: pad.h:166
a fiducial (usually a smd) local to the parent footprint
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function private to src/common/...
Definition: common.cpp:516
#define _(s)
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
PCB_IO * m_owner
wxLogTrace helper definitions.
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:1615
DRAWINGS & GraphicalItems()
Definition: footprint.h:162
bool IsPlaced() const
Definition: footprint.h:295
virtual void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControlBits) const
Output the object to aFormatter in s-expression form.
Definition: title_block.cpp:29
const LIB_ID & GetFPID() const
Definition: footprint.h:185
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:365
class ZONE, a copper pour area
Definition: typeinfo.h:105
WX_FILENAME m_filename
void SetFileName(const wxString &aFileName)
Definition: board.h:226
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition: zone.h:614
void formatSetup(const BOARD *aBoard, int aNestLevel=0) const
formats the board setup information
BOARD_ITEM * Parse()
Definition: pcb_parser.cpp:624
Helper class for creating a footprint library cache.
const wxString & GetKeywords() const
Definition: footprint.h:191
int GetThermalWidth() const
Definition: footprint.h:221
NETINFO_MAPPING * m_mapping
mapping for net codes, so only not empty net codes are stored with consecutive integers as net codes
int GetLocalClearance(wxString *aSource) const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: pad.cpp:688
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
UTF8 Format() const
Definition: lib_id.cpp:116
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, const BOARD *aBoard, int aNestLevel) const
Write the stackup info on board file.
void FootprintLibCreate(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Create a new empty footprint library at aLibraryPath empty.
LINE_READER * SetLineReader(LINE_READER *aReader)
Set aLineReader into the parser, and returns the previous one, if any.
Definition: pcb_parser.h:94
Thermal relief only for THT pads.
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:104
int LAYER_NUM
This can be replaced with int and removed.
double GetLocalSolderPasteMarginRatio() const
Definition: pad.h:393
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
wxString GetPath() const
Definition: wx_filename.cpp:53
const KIID m_Uuid
Definition: eda_item.h:475
const wxSize & GetDelta() const
Definition: pad.h:236
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
const wxPoint & GetPos0() const
Definition: fp_text.h:166
const wxArrayString * GetInitialComments() const
Definition: footprint.h:639
double GetHatchOrientation() const
Definition: zone.h:260
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:394
void Load()
FP_GROUPS & Groups()
Definition: footprint.h:168
Use thermal relief for pads.
unsigned int GetCornerRadius() const
Definition: zone.h:688
const wxPoint & GetPos0() const
Definition: pad.h:223
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:186
wxString GetFullPath() const
Definition: wx_filename.cpp:59
int GetHatchBorderAlgorithm() const
Definition: zone.h:272
void Format(const BOARD_ITEM *aItem, int aNestLevel=0) const
Output aItem to aFormatter in s-expression format.
const ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition: zone.h:750
Handle the data for a net.
Definition: netinfo.h:64
bool IsWritable() const
bool IsTooRecent()
Return whether a version number, if any was parsed, was too recent.
Definition: pcb_parser.h:132
int GetLocalSolderMaskMargin() const
Definition: footprint.h:197
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:118
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
Definition: pad.h:184
virtual wxString GetClass() const =0
Return the class name.
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:541
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition: wx_filename.h:36
int GetAttributes() const
Definition: footprint.h:226
a fiducial (usually a smd) for the full board
double GetChamferRectRatio() const
Definition: pad.h:529
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: pad.h:351
no special fabrication property
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition: zone.cpp:1123
bool FootprintLibDelete(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Delete an existing footprint library and returns true, or if library does not exist returns false,...
Represent a polyline (an zero-thickness chain of connected line segments).
PAD_ATTRIB GetAttribute() const
Definition: pad.h:368
#define CTL_OMIT_PATH
Omit component sheet time stamp (useless in library)
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
std::map< wxString, FOOTPRINT * > FOOTPRINT_MAP
Definition: eagle_parser.h:50
T NormalizeAnglePos(T Angle)
Normalize angle to be in the 0.0 .. 360.0 range: angle is in 1/10 degrees.
Definition: trigo.h:281
wxString AsString() const
Definition: kiid.cpp:277
bool KeepRefreshing(bool aWait=false)
Update the UI dialog.
class ZONE, managed by a footprint
Definition: typeinfo.h:94
#define CTL_OMIT_PAD_NETS
Omit pads net names (useless in library)
double GetAngle() const
Definition: pcb_shape.h:107
BOARD * m_board
which BOARD, no ownership here
int GetShape() const
Definition: pcb_target.h:59
const wxPoint & GetEnd0() const
Definition: fp_shape.h:115
Pads are not covered.
wxPoint GetPosition() const override
Definition: footprint.h:177
double GetLocalSolderPasteMarginRatio() const
Definition: footprint.h:214
Used for text file output.
Definition: richio.h:456
int ShowModal() override
Definition: confirm.cpp:99
const std::map< wxString, wxString > & GetProperties() const
Definition: footprint.h:464
BOARD * Load(const wxString &aFileName, BOARD *aAppendToMe, const PROPERTIES *aProperties=nullptr, PROJECT *aProject=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr) override
Load information from some input file format that this PLUGIN implementation knows about into either ...
int GetChamferPositions() const
Definition: pad.h:539
const wxPoint & GetTextPos() const
Definition: eda_text.h:247
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition: board.cpp:2052
A leader is a dimension-like object pointing to a specific point.
const wxPoint & GetBezierC2_0() const
Definition: fp_shape.h:124
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
a pad with a castellated through hole
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
ZONE_CONNECTION GetZoneConnection() const
Definition: footprint.h:218
int GetPlacementCost90() const
Definition: footprint.h:532
virtual void Save(const wxString &aFileName, BOARD *aBoard, const PROPERTIES *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PLUGIN implementation knows about or it can be u...
virtual const wxPoint & GetEnd() const
BOARD * DoLoad(LINE_READER &aReader, BOARD *aAppendToMe, const PROPERTIES *aProperties, PROGRESS_REPORTER *aProgressReporter, unsigned aLineCount)
Pcbnew s-expression file format parser definition.
long long int GetMinIslandArea() const
Definition: zone.h:754
ZONE_SEGMENT_FILL & FillSegments(PCB_LAYER_ID aLayer)
Definition: zone.h:309
int GetThermalReliefSpokeWidth() const
Definition: zone.h:203
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:174
FP_CACHE(PCB_IO *aOwner, const wxString &aLibraryPath)
bool m_cache_dirty
POLYGON & Polygon(int aIndex)
int GetLocalSolderPasteMargin() const
Definition: footprint.h:211
const FOOTPRINT * GetFootprint() const
void formatLayers(LSET aLayerMask, int aNestLevel=0) const
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:426
segment with non rounded ends
ZONE_CONNECTION GetPadConnection(PAD *aPad, wxString *aSource=nullptr) const
Definition: zone.cpp:774
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
Definition: pad.h:57
DIM_TEXT_POSITION GetTextPositionMode() const
T NormalizeAngle360Min(T Angle)
Normalize angle to be > -360.0 and < 360.0 Angle equal to -360 or +360 are set to 0.
Definition: trigo.h:254
wxString GetZoneName() const
Definition: zone.h:130
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: footprint.cpp:1183
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:166
const FOOTPRINT * GetEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=nullptr) override
A version of FootprintLoad() for use after FootprintEnumerate() for more efficient cache management.
FP_CACHE_ITEM(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition: richio.h:240
#define CTL_OMIT_HIDE
Definition: eda_text.h:56
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
int GetPadToDieLength() const
Definition: pad.h:381
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
bool Exists() const
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:171
void FootprintSave(const wxString &aLibraryPath, const FOOTPRINT *aFootprint, const PROPERTIES *aProperties=nullptr) override
Write aFootprint to an existing library located at aLibraryPath.
const wxString & GetPinFunction() const
Definition: pad.h:136
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:365
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
bool GetRemoveUnconnected() const
Definition: pad.h:554
const wxPoint & GetMid() const
Definition: pcb_track.h:273
int GetThermalGap() const
Definition: footprint.h:224
DRAWINGS & Drawings()
Definition: board.h:236
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
const VECTOR2I & GetP1() const
Definition: shape_arc.h:112
int GetLineThickness() const
wxPoint m_AuxOrigin
origin for plot exports
int GetCornerSmoothingType() const
Definition: zone.h:684
wxString GetPath() const
TRACKS & Tracks()
Definition: board.h:230
const wxPoint & GetBezierC1_0() const
Definition: fp_shape.h:121
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives() const
Accessor to the basic shape list for custom-shaped pads.
Definition: pad.h:298
PAD_SHAPE GetAnchorPadShape() const
Definition: pad.h:179
wxString GetOverrideText() const
std::string FormatInternalUnits(int aValue)
Function FormatInternalUnits converts aValue from internal units to a string appropriate for writing ...
Definition: base_units.cpp:485
const wxPoint & GetStart() const
Definition: pcb_track.h:108
int Translate(int aNetCode) const
Translate net number according to the map prepared by Update() function.
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
std::string Quotew(const wxString &aWrapee) const
Definition: richio.cpp:494
static wxString GetStandardLayerName(PCB_LAYER_ID aLayerId)
Return an "English Standard" name of a PCB layer when given aLayerNumber.
Definition: board.h:605
bool GetFilledPolysUseThickness() const
Definition: zone.h:690
Container for design settings for a BOARD object.
std::string Double2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 We want to avoid scientific ...
Definition: string.cpp:955