1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
5  * Copyright (C) 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 <memory>
26 
27 #include "altium_parser_sch.h"
28 #include <plugins/altium/altium_parser.h>
29 #include <plugins/altium/altium_parser_utils.h>
30 #include <sch_plugins/altium/sch_altium_plugin.h>
31 
32 #include <schematic.h>
33 
34 #include <lib_shape.h>
35 #include <lib_id.h>
36 #include <lib_item.h>
37 #include <lib_pin.h>
38 #include <lib_text.h>
39 
40 #include <sch_bitmap.h>
41 #include <sch_bus_entry.h>
42 #include <sch_symbol.h>
43 #include <sch_junction.h>
44 #include <sch_line.h>
45 #include <sch_no_connect.h>
46 #include <sch_screen.h>
47 #include <sch_sheet.h>
48 #include <sch_sheet_pin.h>
49 #include <sch_text.h>
50 
51 #include <bezier_curves.h>
52 #include <compoundfilereader.h>
53 #include <string_utils.h>
54 #include <sch_edit_frame.h>
55 #include <trigo.h>
56 #include <wildcards_and_files_ext.h>
57 #include <wx/mstream.h>
58 #include <wx/log.h>
59 #include <wx/zstream.h>
60 #include <wx/wfstream.h>
61 #include <trigo.h>
62 
GetRelativePosition(const wxPoint & aPosition,const SCH_SYMBOL * aSymbol)63 const wxPoint GetRelativePosition( const wxPoint& aPosition, const SCH_SYMBOL* aSymbol )
64 {
65     TRANSFORM t = aSymbol->GetTransform().InverseTransform();
66     return t.TransformCoordinate( aPosition - aSymbol->GetPosition() );
67 }
68 
69 
GetColorFromInt(int color)70 COLOR4D GetColorFromInt( int color )
71 {
72     int red   = color & 0x0000FF;
73     int green = ( color & 0x00FF00 ) >> 8;
74     int blue  = ( color & 0xFF0000 ) >> 16;
75 
76     return COLOR4D().FromCSSRGBA( red, green, blue, 1.0 );
77 }
78 
SCH_ALTIUM_PLUGIN()79 SCH_ALTIUM_PLUGIN::SCH_ALTIUM_PLUGIN()
80 {
81     m_rootSheet    = nullptr;
82     m_currentSheet = nullptr;
83     m_schematic    = nullptr;
84 
85     m_reporter     = &WXLOG_REPORTER::GetInstance();
86 }
87 
88 
~SCH_ALTIUM_PLUGIN()89 SCH_ALTIUM_PLUGIN::~SCH_ALTIUM_PLUGIN()
90 {
91 }
92 
93 
GetName() const94 const wxString SCH_ALTIUM_PLUGIN::GetName() const
95 {
96     return "Altium";
97 }
98 
99 
GetFileExtension() const100 const wxString SCH_ALTIUM_PLUGIN::GetFileExtension() const
101 {
102     return "SchDoc";
103 }
104 
105 
GetLibraryFileExtension() const106 const wxString SCH_ALTIUM_PLUGIN::GetLibraryFileExtension() const
107 {
108     return "SchLib";
109 }
110 
111 
GetModifyHash() const112 int SCH_ALTIUM_PLUGIN::GetModifyHash() const
113 {
114     return 0;
115 }
116 
117 
CheckHeader(const wxString & aFileName)118 bool SCH_ALTIUM_PLUGIN::CheckHeader( const wxString& aFileName )
119 {
120     // TODO
121 
122     return true;
123 }
124 
125 
getLibName()126 wxString SCH_ALTIUM_PLUGIN::getLibName()
127 {
128     if( m_libName.IsEmpty() )
129     {
130         // Try to come up with a meaningful name
131         m_libName = m_schematic->Prj().GetProjectName();
132 
133         if( m_libName.IsEmpty() )
134         {
135             wxFileName fn( m_rootSheet->GetFileName() );
136             m_libName = fn.GetName();
137         }
138 
139         if( m_libName.IsEmpty() )
140             m_libName = "noname";
141 
142         m_libName += "-altium-import";
143         m_libName = LIB_ID::FixIllegalChars( m_libName, true );
144     }
145 
146     return m_libName;
147 }
148 
149 
getLibFileName()150 wxFileName SCH_ALTIUM_PLUGIN::getLibFileName()
151 {
152     wxFileName fn( m_schematic->Prj().GetProjectPath(), getLibName(), KiCadSymbolLibFileExtension );
153 
154     return fn;
155 }
156 
157 
Load(const wxString & aFileName,SCHEMATIC * aSchematic,SCH_SHEET * aAppendToMe,const PROPERTIES * aProperties)158 SCH_SHEET* SCH_ALTIUM_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
159                                     SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties )
160 {
161     wxASSERT( !aFileName || aSchematic != nullptr );
162 
163     wxFileName fileName( aFileName );
164     fileName.SetExt( KiCadSchematicFileExtension );
165     m_schematic = aSchematic;
166 
167     // Delete on exception, if I own m_rootSheet, according to aAppendToMe
168     std::unique_ptr<SCH_SHEET> deleter( aAppendToMe ? nullptr : m_rootSheet );
169 
170     if( aAppendToMe )
171     {
172         wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
173         m_rootSheet = &aSchematic->Root();
174     }
175     else
176     {
177         m_rootSheet = new SCH_SHEET( aSchematic );
178         m_rootSheet->SetFileName( fileName.GetFullPath() );
179 
180         aSchematic->SetRoot( m_rootSheet );
181 
182         SCH_SHEET_PATH sheetpath;
183         sheetpath.push_back( m_rootSheet );
184 
185         m_rootSheet->AddInstance( sheetpath );
186         m_rootSheet->SetPageNumber( sheetpath, "#" );   // We'll update later if we find a
187                                                         // pageNumber record for it
188     }
189 
190     if( !m_rootSheet->GetScreen() )
191     {
192         SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
193         screen->SetFileName( aFileName );
194         m_rootSheet->SetScreen( screen );
195     }
196 
197     SYMBOL_LIB_TABLE* libTable = m_schematic->Prj().SchSymbolLibTable();
198 
199     wxCHECK_MSG( libTable, nullptr, "Could not load symbol lib table." );
200 
201     m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
202 
203     /// @note No check is being done here to see if the existing symbol library exists so this
204     ///       will overwrite the existing one.
205     if( !libTable->HasLibrary( getLibName() ) )
206     {
207         // Create a new empty symbol library.
208         m_pi->CreateSymbolLib( getLibFileName().GetFullPath() );
209         wxString libTableUri = "${KIPRJMOD}/" + getLibFileName().GetFullName();
210 
211         // Add the new library to the project symbol library table.
212         libTable->InsertRow( new SYMBOL_LIB_TABLE_ROW( getLibName(), libTableUri,
213                                                        wxString( "KiCad" ) ) );
214 
215         // Save project symbol library table.
216         wxFileName fn( m_schematic->Prj().GetProjectPath(),
217                        SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
218 
219         // So output formatter goes out of scope and closes the file before reloading.
220         {
221             FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
222             libTable->Format( &formatter, 0 );
223         }
224 
225         // Reload the symbol library table.
226         m_schematic->Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, nullptr );
227         m_schematic->Prj().SchSymbolLibTable();
228     }
229 
230     m_currentSheet = m_rootSheet;
231     ParseAltiumSch( aFileName );
232 
233     m_pi->SaveLibrary( getLibFileName().GetFullPath() );
234 
235     SCH_SCREENS allSheets( m_rootSheet );
236     allSheets.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
237     allSheets.ClearEditFlags();
238 
239     return m_rootSheet;
240 }
241 
242 
ParseAltiumSch(const wxString & aFileName)243 void SCH_ALTIUM_PLUGIN::ParseAltiumSch( const wxString& aFileName )
244 {
245     // Open file
246     FILE* fp = wxFopen( aFileName, "rb" );
247 
248     if( fp == nullptr )
249     {
250         m_reporter->Report( wxString::Format( _( "Cannot open file '%s'." ), aFileName ),
251                             RPT_SEVERITY_ERROR );
252         return;
253     }
254 
255     fseek( fp, 0, SEEK_END );
256     long len = ftell( fp );
257 
258     if( len < 0 )
259     {
260         fclose( fp );
261         THROW_IO_ERROR( "Read error, cannot determine length of file." );
262     }
263 
264     std::unique_ptr<unsigned char[]> buffer( new unsigned char[len] );
265     fseek( fp, 0, SEEK_SET );
266 
267     size_t bytesRead = fread( buffer.get(), sizeof( unsigned char ), len, fp );
268     fclose( fp );
269 
270     if( static_cast<size_t>( len ) != bytesRead )
271         THROW_IO_ERROR( "Read error." );
272 
273     try
274     {
275         CFB::CompoundFileReader reader( buffer.get(), bytesRead );
276         ParseStorage( reader ); // we need this before parsing the FileHeader
277         ParseFileHeader( reader );
278     }
279     catch( CFB::CFBException& exception )
280     {
281         THROW_IO_ERROR( exception.what() );
282     }
283 }
284 
285 
ParseStorage(const CFB::CompoundFileReader & aReader)286 void SCH_ALTIUM_PLUGIN::ParseStorage( const CFB::CompoundFileReader& aReader )
287 {
288     const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, "Storage" );
289 
290     if( file == nullptr )
291         return;
292 
293     ALTIUM_PARSER reader( aReader, file );
294 
295     std::map<wxString, wxString> properties = reader.ReadProperties();
296     wxString header = ALTIUM_PARSER::ReadString( properties, "HEADER", "" );
297     int      weight = ALTIUM_PARSER::ReadInt( properties, "WEIGHT", 0 );
298 
299     if( weight < 0 )
300         THROW_IO_ERROR( "Storage weight is negative!" );
301 
302     for( int i = 0; i < weight; i++ )
303     {
304         m_altiumStorage.emplace_back( reader );
305     }
306 
307     if( reader.HasParsingError() )
308         THROW_IO_ERROR( "stream was not parsed correctly!" );
309 
310     // TODO pointhi: is it possible to have multiple headers in one Storage file? Otherwise
311     // throw IO Error.
312     if( reader.GetRemainingBytes() != 0 )
313     {
314         m_reporter->Report( wxString::Format( _( "Storage file not fully parsed "
315                                                  "(%d bytes remaining)." ),
316                                               reader.GetRemainingBytes() ),
317                             RPT_SEVERITY_ERROR );
318     }
319 }
320 
321 
ParseFileHeader(const CFB::CompoundFileReader & aReader)322 void SCH_ALTIUM_PLUGIN::ParseFileHeader( const CFB::CompoundFileReader& aReader )
323 {
324     const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, "FileHeader" );
325 
326     if( file == nullptr )
327         THROW_IO_ERROR( "FileHeader not found" );
328 
329     ALTIUM_PARSER reader( aReader, file );
330 
331     if( reader.GetRemainingBytes() <= 0 )
332     {
333         THROW_IO_ERROR( "FileHeader does not contain any data" );
334     }
335     else
336     {
337         std::map<wxString, wxString> properties = reader.ReadProperties();
338 
339         int               recordId = ALTIUM_PARSER::ReadInt( properties, "RECORD", 0 );
340         ALTIUM_SCH_RECORD record   = static_cast<ALTIUM_SCH_RECORD>( recordId );
341 
342         if( record != ALTIUM_SCH_RECORD::HEADER )
343             THROW_IO_ERROR( "Header expected" );
344     }
345 
346     // Prepare some local variables
347     wxASSERT( m_altiumPortsCurrentSheet.empty() );
348     wxASSERT( !m_currentTitleBlock );
349 
350     m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
351 
352     // index is required to resolve OWNERINDEX
353     for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
354     {
355         std::map<wxString, wxString> properties = reader.ReadProperties();
356 
357         int               recordId = ALTIUM_PARSER::ReadInt( properties, "RECORD", 0 );
358         ALTIUM_SCH_RECORD record   = static_cast<ALTIUM_SCH_RECORD>( recordId );
359 
360         // see: https://github.com/vadmium/python-altium/blob/master/format.md
361         switch( record )
362         {
363         case ALTIUM_SCH_RECORD::HEADER:
364             THROW_IO_ERROR( "Header already parsed" );
365         case ALTIUM_SCH_RECORD::COMPONENT:
366             ParseComponent( index, properties );
367             break;
368         case ALTIUM_SCH_RECORD::PIN:
369             ParsePin( properties );
370             break;
371         case ALTIUM_SCH_RECORD::IEEE_SYMBOL:
372             break;
373         case ALTIUM_SCH_RECORD::LABEL:
374             ParseLabel( properties );
375             break;
376         case ALTIUM_SCH_RECORD::BEZIER:
377             ParseBezier( properties );
378             break;
379         case ALTIUM_SCH_RECORD::POLYLINE:
380             ParsePolyline( properties );
381             break;
382         case ALTIUM_SCH_RECORD::POLYGON:
383             ParsePolygon( properties );
384             break;
385         case ALTIUM_SCH_RECORD::ELLIPSE:
386             break;
387         case ALTIUM_SCH_RECORD::PIECHART:
388             break;
389         case ALTIUM_SCH_RECORD::ROUND_RECTANGLE:
390             ParseRoundRectangle( properties );
391             break;
392         case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC:
393             break;
394         case ALTIUM_SCH_RECORD::ARC:
395             ParseArc( properties );
396             break;
397         case ALTIUM_SCH_RECORD::LINE:
398             ParseLine( properties );
399             break;
400         case ALTIUM_SCH_RECORD::RECTANGLE:
401             ParseRectangle( properties );
402             break;
403         case ALTIUM_SCH_RECORD::SHEET_SYMBOL:
404             ParseSheetSymbol( index, properties );
405             break;
406         case ALTIUM_SCH_RECORD::SHEET_ENTRY:
407             ParseSheetEntry( properties );
408             break;
409         case ALTIUM_SCH_RECORD::POWER_PORT:
410             ParsePowerPort( properties );
411             break;
412         case ALTIUM_SCH_RECORD::PORT:
413             // Ports are parsed after the sheet was parsed
414             // This is required because we need all electrical connection points before placing.
415             m_altiumPortsCurrentSheet.emplace_back( properties );
416             break;
417         case ALTIUM_SCH_RECORD::NO_ERC:
418             ParseNoERC( properties );
419             break;
420         case ALTIUM_SCH_RECORD::NET_LABEL:
421             ParseNetLabel( properties );
422             break;
423         case ALTIUM_SCH_RECORD::BUS:
424             ParseBus( properties );
425             break;
426         case ALTIUM_SCH_RECORD::WIRE:
427             ParseWire( properties );
428             break;
429         case ALTIUM_SCH_RECORD::TEXT_FRAME:
430             ParseTextFrame( properties );
431             break;
432         case ALTIUM_SCH_RECORD::JUNCTION:
433             ParseJunction( properties );
434             break;
435         case ALTIUM_SCH_RECORD::IMAGE:
436             ParseImage( properties );
437             break;
438         case ALTIUM_SCH_RECORD::SHEET:
439             ParseSheet( properties );
440             break;
441         case ALTIUM_SCH_RECORD::SHEET_NAME:
442             ParseSheetName( properties );
443             break;
444         case ALTIUM_SCH_RECORD::FILE_NAME:
445             ParseFileName( properties );
446             break;
447         case ALTIUM_SCH_RECORD::DESIGNATOR:
448             ParseDesignator( properties );
449             break;
450         case ALTIUM_SCH_RECORD::BUS_ENTRY:
451             ParseBusEntry( properties );
452             break;
453         case ALTIUM_SCH_RECORD::TEMPLATE:
454             break;
455         case ALTIUM_SCH_RECORD::PARAMETER:
456             ParseParameter( properties );
457             break;
458         case ALTIUM_SCH_RECORD::WARNING_SIGN:
459             break;
460         case ALTIUM_SCH_RECORD::IMPLEMENTATION_LIST:
461             ParseImplementationList( index, properties );
462             break;
463         case ALTIUM_SCH_RECORD::IMPLEMENTATION:
464             ParseImplementation( properties );
465             break;
466         case ALTIUM_SCH_RECORD::RECORD_46:
467             break;
468         case ALTIUM_SCH_RECORD::RECORD_47:
469             break;
470         case ALTIUM_SCH_RECORD::RECORD_48:
471             break;
472         case ALTIUM_SCH_RECORD::NOTE:
473             ParseNote( properties );
474             break;
475         case ALTIUM_SCH_RECORD::COMPILE_MASK:
476             m_reporter->Report( _( "Compile mask not currently supported." ), RPT_SEVERITY_ERROR );
477             break;
478         case ALTIUM_SCH_RECORD::RECORD_215:
479             break;
480         case ALTIUM_SCH_RECORD::RECORD_216:
481             break;
482         case ALTIUM_SCH_RECORD::RECORD_217:
483             break;
484         case ALTIUM_SCH_RECORD::RECORD_218:
485             break;
486         case ALTIUM_SCH_RECORD::RECORD_226:
487             break;
488         default:
489             m_reporter->Report( wxString::Format( _( "Unknown Record id: %d." ), recordId ),
490                                 RPT_SEVERITY_ERROR );
491             break;
492         }
493     }
494 
495     if( reader.HasParsingError() )
496         THROW_IO_ERROR( "stream was not parsed correctly!" );
497 
498     if( reader.GetRemainingBytes() != 0 )
499         THROW_IO_ERROR( "stream is not fully parsed" );
500 
501     // assign LIB_SYMBOL -> COMPONENT
502     for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
503     {
504         auto libSymbolIt = m_libSymbols.find( symbol.first );
505 
506         if( libSymbolIt == m_libSymbols.end() )
507             THROW_IO_ERROR( "every symbol should have a symbol attached" );
508 
509         m_pi->SaveSymbol( getLibFileName().GetFullPath(),
510                           new LIB_SYMBOL( *( libSymbolIt->second ) ), m_properties.get() );
511 
512         symbol.second->SetLibSymbol( libSymbolIt->second );
513     }
514 
515     // Handle title blocks
516     m_currentSheet->GetScreen()->SetTitleBlock( *m_currentTitleBlock );
517     m_currentTitleBlock.reset();
518 
519     // Handle Ports
520     for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
521         ParsePort( port );
522 
523     m_altiumPortsCurrentSheet.clear();
524 
525     m_symbols.clear();
526     m_libSymbols.clear();
527 
528     // Otherwise we cannot save the imported sheet?
529     m_currentSheet->SetModified();
530 }
531 
532 
IsComponentPartVisible(int aOwnerindex,int aOwnerpartdisplaymode) const533 bool SCH_ALTIUM_PLUGIN::IsComponentPartVisible( int aOwnerindex, int aOwnerpartdisplaymode ) const
534 {
535     const auto& component = m_altiumComponents.find( aOwnerindex );
536 
537     if( component == m_altiumComponents.end() )
538         return false;
539 
540     return component->second.displaymode == aOwnerpartdisplaymode;
541 }
542 
543 
GetFileFromStorage(const wxString & aFilename) const544 const ASCH_STORAGE_FILE* SCH_ALTIUM_PLUGIN::GetFileFromStorage( const wxString& aFilename ) const
545 {
546     const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
547 
548     for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
549     {
550         if( file.filename.IsSameAs( aFilename ) )
551             return &file;
552 
553         if( file.filename.EndsWith( aFilename ) )
554             nonExactMatch = &file;
555     }
556 
557     return nonExactMatch;
558 }
559 
560 
ParseComponent(int aIndex,const std::map<wxString,wxString> & aProperties)561 void SCH_ALTIUM_PLUGIN::ParseComponent( int aIndex,
562                                         const std::map<wxString, wxString>& aProperties )
563 {
564     auto pair = m_altiumComponents.insert( { aIndex, ASCH_SYMBOL( aProperties ) } );
565     const ASCH_SYMBOL& elem = pair.first->second;
566 
567     // TODO: this is a hack until we correctly apply all transformations to every element
568     wxString name = wxString::Format( "%d%s_%s",
569                                       elem.orientation,
570                                       elem.isMirrored ? "_mirrored" : "",
571                                       elem.libreference );
572     LIB_ID libId = AltiumToKiCadLibID( getLibName(), name );
573 
574     LIB_SYMBOL* ksymbol = new LIB_SYMBOL( wxEmptyString );
575     ksymbol->SetName( name );
576     ksymbol->SetDescription( elem.componentdescription );
577     ksymbol->SetLibId( libId );
578     m_libSymbols.insert( { aIndex, ksymbol } );
579 
580     // each component has its own symbol for now
581     SCH_SYMBOL* symbol = new SCH_SYMBOL();
582 
583     symbol->SetPosition( elem.location + m_sheetOffset );
584 
585     // TODO: keep it simple for now, and only set position.
586     //component->SetOrientation( elem.orientation );
587     symbol->SetLibId( libId );
588 
589     symbol->SetUnit( elem.currentpartid );
590 
591     m_currentSheet->GetScreen()->Append( symbol );
592 
593     m_symbols.insert( { aIndex, symbol } );
594 }
595 
596 
ParsePin(const std::map<wxString,wxString> & aProperties)597 void SCH_ALTIUM_PLUGIN::ParsePin( const std::map<wxString, wxString>& aProperties )
598 {
599     ASCH_PIN elem( aProperties );
600 
601     const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
602 
603     if( libSymbolIt == m_libSymbols.end() )
604     {
605         // TODO: e.g. can depend on Template (RECORD=39
606         m_reporter->Report( wxString::Format( _( "Pin's owner (%d) not found." ), elem.ownerindex ),
607                             RPT_SEVERITY_ERROR );
608         return;
609     }
610 
611     if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
612         return;
613 
614     SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
615     LIB_PIN*    pin = new LIB_PIN( libSymbolIt->second );
616     libSymbolIt->second->AddDrawItem( pin );
617 
618     pin->SetUnit( elem.ownerpartid );
619 
620     pin->SetName( elem.name );
621     pin->SetNumber( elem.designator );
622     pin->SetLength( elem.pinlength );
623 
624     if( !elem.showDesignator )
625         pin->SetNumberTextSize( 0 );
626 
627     if( !elem.showPinName )
628         pin->SetNameTextSize( 0 );
629 
630     wxPoint pinLocation = elem.location; // the location given is not the connection point!
631 
632     switch( elem.orientation )
633     {
634     case ASCH_RECORD_ORIENTATION::RIGHTWARDS:
635         pin->SetOrientation( DrawPinOrient::PIN_LEFT );
636         pinLocation.x += elem.pinlength;
637         break;
638     case ASCH_RECORD_ORIENTATION::UPWARDS:
639         pin->SetOrientation( DrawPinOrient::PIN_DOWN );
640         pinLocation.y -= elem.pinlength;
641         break;
642     case ASCH_RECORD_ORIENTATION::LEFTWARDS:
643         pin->SetOrientation( DrawPinOrient::PIN_RIGHT );
644         pinLocation.x -= elem.pinlength;
645         break;
646     case ASCH_RECORD_ORIENTATION::DOWNWARDS:
647         pin->SetOrientation( DrawPinOrient::PIN_UP );
648         pinLocation.y += elem.pinlength;
649         break;
650     default:
651         m_reporter->Report( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
652         break;
653     }
654 
655     // TODO: position can be sometimes off a little bit!
656     pin->SetPosition( GetRelativePosition( pinLocation + m_sheetOffset, symbol ) );
657 
658     // TODO: the following fix is even worse for now?
659     // pin->SetPosition( GetRelativePosition( elem.kicadLocation, symbol ) );
660 
661     switch( elem.electrical )
662     {
663     case ASCH_PIN_ELECTRICAL::INPUT:
664         pin->SetType( ELECTRICAL_PINTYPE::PT_INPUT );
665         break;
666     case ASCH_PIN_ELECTRICAL::BIDI:
667         pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
668         break;
669     case ASCH_PIN_ELECTRICAL::OUTPUT:
670         pin->SetType( ELECTRICAL_PINTYPE::PT_OUTPUT );
671         break;
672     case ASCH_PIN_ELECTRICAL::OPEN_COLLECTOR:
673         pin->SetType( ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR );
674         break;
675     case ASCH_PIN_ELECTRICAL::PASSIVE:
676         pin->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE );
677         break;
678     case ASCH_PIN_ELECTRICAL::TRISTATE:
679         pin->SetType( ELECTRICAL_PINTYPE::PT_TRISTATE );
680         break;
681     case ASCH_PIN_ELECTRICAL::OPEN_EMITTER:
682         pin->SetType( ELECTRICAL_PINTYPE::PT_OPENEMITTER );
683         break;
684     case ASCH_PIN_ELECTRICAL::POWER:
685         pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
686         break;
687     case ASCH_PIN_ELECTRICAL::UNKNOWN:
688     default:
689         pin->SetType( ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
690         m_reporter->Report( _( "Pin has unexpected electrical type." ), RPT_SEVERITY_WARNING );
691         break;
692     }
693 
694     if( elem.symbolOuterEdge == ASCH_PIN_SYMBOL_OUTEREDGE::UNKNOWN )
695         m_reporter->Report( _( "Pin has unexpected outer edge type." ), RPT_SEVERITY_WARNING );
696 
697     if( elem.symbolInnerEdge == ASCH_PIN_SYMBOL_INNEREDGE::UNKNOWN )
698         m_reporter->Report( _( "Pin has unexpected inner edge type." ), RPT_SEVERITY_WARNING );
699 
700     if( elem.symbolOuterEdge == ASCH_PIN_SYMBOL_OUTEREDGE::NEGATED )
701     {
702         switch( elem.symbolInnerEdge )
703         {
704         case ASCH_PIN_SYMBOL_INNEREDGE::CLOCK:
705             pin->SetShape( GRAPHIC_PINSHAPE::INVERTED_CLOCK );
706             break;
707         default:
708             pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
709             break;
710         }
711     }
712     else if( elem.symbolOuterEdge == ASCH_PIN_SYMBOL_OUTEREDGE::LOW_INPUT )
713     {
714         switch( elem.symbolInnerEdge )
715         {
716         case ASCH_PIN_SYMBOL_INNEREDGE::CLOCK:
717             pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
718             break;
719         default:
720             pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
721             break;
722         }
723     }
724     else if( elem.symbolOuterEdge == ASCH_PIN_SYMBOL_OUTEREDGE::LOW_OUTPUT )
725     {
726         pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
727     }
728     else
729     {
730         switch( elem.symbolInnerEdge )
731         {
732         case ASCH_PIN_SYMBOL_INNEREDGE::CLOCK:
733             pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
734             break;
735         default:
736             pin->SetShape( GRAPHIC_PINSHAPE::LINE ); // nothing to do
737             break;
738         }
739     }
740 }
741 
742 
SetTextPositioning(EDA_TEXT * text,ASCH_LABEL_JUSTIFICATION justification,ASCH_RECORD_ORIENTATION orientation)743 void SetTextPositioning( EDA_TEXT* text, ASCH_LABEL_JUSTIFICATION justification,
744                          ASCH_RECORD_ORIENTATION orientation )
745 {
746     int    vjustify, hjustify;
747     double angle = TEXT_ANGLE_HORIZ;
748 
749     switch( justification )
750     {
751     default:
752     case ASCH_LABEL_JUSTIFICATION::UNKNOWN:
753     case ASCH_LABEL_JUSTIFICATION::BOTTOM_LEFT:
754     case ASCH_LABEL_JUSTIFICATION::BOTTOM_CENTER:
755     case ASCH_LABEL_JUSTIFICATION::BOTTOM_RIGHT:
756         vjustify = EDA_TEXT_VJUSTIFY_T::GR_TEXT_VJUSTIFY_BOTTOM;
757         break;
758     case ASCH_LABEL_JUSTIFICATION::CENTER_LEFT:
759     case ASCH_LABEL_JUSTIFICATION::CENTER_CENTER:
760     case ASCH_LABEL_JUSTIFICATION::CENTER_RIGHT:
761         vjustify = EDA_TEXT_VJUSTIFY_T::GR_TEXT_VJUSTIFY_CENTER;
762         break;
763     case ASCH_LABEL_JUSTIFICATION::TOP_LEFT:
764     case ASCH_LABEL_JUSTIFICATION::TOP_CENTER:
765     case ASCH_LABEL_JUSTIFICATION::TOP_RIGHT:
766         vjustify = EDA_TEXT_VJUSTIFY_T::GR_TEXT_VJUSTIFY_TOP;
767         break;
768     }
769 
770     switch( justification )
771     {
772     default:
773     case ASCH_LABEL_JUSTIFICATION::UNKNOWN:
774     case ASCH_LABEL_JUSTIFICATION::BOTTOM_LEFT:
775     case ASCH_LABEL_JUSTIFICATION::CENTER_LEFT:
776     case ASCH_LABEL_JUSTIFICATION::TOP_LEFT:
777         hjustify = EDA_TEXT_HJUSTIFY_T::GR_TEXT_HJUSTIFY_LEFT;
778         break;
779     case ASCH_LABEL_JUSTIFICATION::BOTTOM_CENTER:
780     case ASCH_LABEL_JUSTIFICATION::CENTER_CENTER:
781     case ASCH_LABEL_JUSTIFICATION::TOP_CENTER:
782         hjustify = EDA_TEXT_HJUSTIFY_T::GR_TEXT_HJUSTIFY_CENTER;
783         break;
784     case ASCH_LABEL_JUSTIFICATION::BOTTOM_RIGHT:
785     case ASCH_LABEL_JUSTIFICATION::CENTER_RIGHT:
786     case ASCH_LABEL_JUSTIFICATION::TOP_RIGHT:
787         hjustify = EDA_TEXT_HJUSTIFY_T::GR_TEXT_HJUSTIFY_RIGHT;
788         break;
789     }
790 
791     switch( orientation )
792     {
793     case ASCH_RECORD_ORIENTATION::RIGHTWARDS:
794         angle = TEXT_ANGLE_HORIZ;
795         break;
796     case ASCH_RECORD_ORIENTATION::LEFTWARDS:
797         vjustify *= -1;
798         hjustify *= -1;
799         angle = TEXT_ANGLE_HORIZ;
800         break;
801     case ASCH_RECORD_ORIENTATION::UPWARDS:
802         angle = TEXT_ANGLE_VERT;
803         break;
804     case ASCH_RECORD_ORIENTATION::DOWNWARDS:
805         vjustify *= -1;
806         hjustify *= -1;
807         angle = TEXT_ANGLE_VERT;
808         break;
809     }
810 
811     text->SetVertJustify( static_cast<EDA_TEXT_VJUSTIFY_T>( vjustify ) );
812     text->SetHorizJustify( static_cast<EDA_TEXT_HJUSTIFY_T>( hjustify ) );
813     text->SetTextAngle( angle );
814 }
815 
816 
ParseLabel(const std::map<wxString,wxString> & aProperties)817 void SCH_ALTIUM_PLUGIN::ParseLabel( const std::map<wxString, wxString>& aProperties )
818 {
819     ASCH_LABEL elem( aProperties );
820 
821     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
822     {
823         std::map<wxString, wxString> variableMap = {
824             { "APPLICATION_BUILDNUMBER", "KICAD_VERSION" },
825             { "SHEETNUMBER",  "#"            },
826             { "SHEETTOTAL",   "##"           },
827             { "TITLE",        "TITLE"        }, // 1:1 maps are sort of useless, but it makes it
828             { "REVISION",     "REVISION"     }, // easier to see that the list is complete
829             { "DATE",         "ISSUE_DATE"   },
830             { "CURRENTDATE",  "CURRENT_DATE" },
831             { "COMPANYNAME",  "COMPANY"      },
832             { "DOCUMENTNAME", "FILENAME"     },
833             { "PROJECTNAME",  "PROJECTNAME"  },
834         };
835 
836         wxString  kicadText = AltiumSpecialStringsToKiCadVariables( elem.text, variableMap );
837         SCH_TEXT* textItem = new SCH_TEXT( elem.location + m_sheetOffset, kicadText );
838 
839         SetTextPositioning( textItem, elem.justification, elem.orientation );
840 
841         size_t fontId = static_cast<int>( elem.fontId );
842 
843         if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
844         {
845             const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
846             textItem->SetItalic( font.italic );
847             textItem->SetBold( font.bold );
848             textItem->SetTextSize( { font.size / 2, font.size / 2 } );
849         }
850 
851         textItem->SetFlags(IS_NEW );
852         m_currentSheet->GetScreen()->Append( textItem );
853     }
854     else
855     {
856         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
857 
858         if( libSymbolIt == m_libSymbols.end() )
859         {
860             // TODO: e.g. can depend on Template (RECORD=39
861             m_reporter->Report( wxString::Format( _( "Label's owner (%d) not found." ),
862                                                   elem.ownerindex ),
863                                 RPT_SEVERITY_ERROR );
864             return;
865         }
866 
867         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
868         LIB_TEXT*   textItem = new LIB_TEXT( libSymbolIt->second );
869         libSymbolIt->second->AddDrawItem( textItem );
870 
871         textItem->SetUnit( elem.ownerpartid );
872 
873         textItem->SetPosition( GetRelativePosition( elem.location + m_sheetOffset, symbol ) );
874         textItem->SetText( elem.text );
875         SetTextPositioning( textItem, elem.justification, elem.orientation );
876 
877         size_t fontId = static_cast<int>( elem.fontId );
878 
879         if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
880         {
881             const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
882             textItem->SetItalic( font.italic );
883             textItem->SetBold( font.bold );
884             textItem->SetTextSize( { font.size / 2, font.size / 2 } );
885         }
886     }
887 }
888 
889 
ParseTextFrame(const std::map<wxString,wxString> & aProperties)890 void SCH_ALTIUM_PLUGIN::ParseTextFrame( const std::map<wxString, wxString>& aProperties )
891 {
892     ASCH_TEXT_FRAME elem( aProperties );
893 
894     SCH_TEXT* text = new SCH_TEXT( elem.location + m_sheetOffset, elem.text );
895 
896     switch( elem.alignment )
897     {
898     default:
899     case ASCH_TEXT_FRAME_ALIGNMENT::LEFT:
900         text->SetLabelSpinStyle( LABEL_SPIN_STYLE::SPIN::RIGHT );
901         break;
902     case ASCH_TEXT_FRAME_ALIGNMENT::CENTER:
903         // No support for centered text in Eeschema yet...
904         text->SetLabelSpinStyle( LABEL_SPIN_STYLE::SPIN::RIGHT );
905         break;
906     case ASCH_TEXT_FRAME_ALIGNMENT::RIGHT:
907         text->SetLabelSpinStyle( LABEL_SPIN_STYLE::SPIN::LEFT );
908         break;
909     }
910 
911     // TODO: set size and word-wrap once KiCad supports wrapped text.
912 
913     // TODO: set border and background color once KiCad supports them.
914 
915     size_t fontId = static_cast<int>( elem.fontId );
916 
917     if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
918     {
919         const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
920         text->SetItalic( font.italic );
921         text->SetBold( font.bold );
922         text->SetTextSize( { font.size / 2, font.size / 2 } );
923     }
924 
925     text->SetFlags( IS_NEW );
926     m_currentSheet->GetScreen()->Append( text );
927 }
928 
929 
ParseNote(const std::map<wxString,wxString> & aProperties)930 void SCH_ALTIUM_PLUGIN::ParseNote( const std::map<wxString, wxString>& aProperties )
931 {
932     ASCH_NOTE elem( aProperties );
933 
934     SCH_TEXT* text = new SCH_TEXT( elem.location + m_sheetOffset, elem.text );
935 
936     switch( elem.alignment )
937     {
938     default:
939     case ASCH_TEXT_FRAME_ALIGNMENT::LEFT:
940         text->SetLabelSpinStyle( LABEL_SPIN_STYLE::SPIN::RIGHT );
941         break;
942     case ASCH_TEXT_FRAME_ALIGNMENT::CENTER:
943         // No support for centered text in Eeschema yet...
944         text->SetLabelSpinStyle( LABEL_SPIN_STYLE::SPIN::RIGHT );
945         break;
946     case ASCH_TEXT_FRAME_ALIGNMENT::RIGHT:
947         text->SetLabelSpinStyle( LABEL_SPIN_STYLE::SPIN::LEFT );
948         break;
949     }
950 
951     // TODO: set size and word-wrap once KiCad supports wrapped text.
952 
953     // TODO: set border and background color once KiCad supports them.
954 
955     // TODO: need some sort of property system for storing author....
956 
957     size_t fontId = static_cast<int>( elem.fontId );
958 
959     if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
960     {
961         const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
962         text->SetItalic( font.italic );
963         text->SetBold( font.bold );
964         text->SetTextSize( { font.size / 2, font.size / 2 } );
965     }
966 
967     text->SetFlags( IS_NEW );
968     m_currentSheet->GetScreen()->Append( text );
969 }
970 
971 
ParseBezier(const std::map<wxString,wxString> & aProperties)972 void SCH_ALTIUM_PLUGIN::ParseBezier( const std::map<wxString, wxString>& aProperties )
973 {
974     ASCH_BEZIER elem( aProperties );
975 
976     if( elem.points.size() < 2 )
977     {
978         m_reporter->Report( wxString::Format( _( "Bezier has %d control points. At least 2 are "
979                                                  "expected." ),
980                                               static_cast<int>( elem.points.size() ) ),
981                             RPT_SEVERITY_WARNING );
982         return;
983     }
984 
985     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
986     {
987         for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
988         {
989             if( i + 2 == elem.points.size() )
990             {
991                 // special case: single line
992                 SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
993                                                SCH_LAYER_ID::LAYER_NOTES );
994 
995                 line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
996                 line->SetLineWidth( elem.lineWidth );
997                 line->SetLineStyle( PLOT_DASH_TYPE::SOLID );
998 
999                 line->SetFlags( IS_NEW );
1000                 m_currentSheet->GetScreen()->Append( line );
1001             }
1002             else
1003             {
1004                 // simulate Bezier using line segments
1005                 std::vector<wxPoint> bezierPoints;
1006                 std::vector<wxPoint> polyPoints;
1007 
1008                 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
1009                     bezierPoints.push_back( elem.points.at( j ) + m_sheetOffset );
1010 
1011                 BEZIER_POLY converter( bezierPoints );
1012                 converter.GetPoly( polyPoints );
1013 
1014                 for( size_t k = 0; k + 1 < polyPoints.size(); k++ )
1015                 {
1016                     SCH_LINE* line = new SCH_LINE( polyPoints.at( k ) + m_sheetOffset,
1017                                                    SCH_LAYER_ID::LAYER_NOTES );
1018 
1019                     line->SetEndPoint( polyPoints.at( k + 1 ) + m_sheetOffset );
1020                     line->SetLineWidth( elem.lineWidth );
1021 
1022                     line->SetFlags( IS_NEW );
1023                     m_currentSheet->GetScreen()->Append( line );
1024                 }
1025             }
1026         }
1027     }
1028     else
1029     {
1030         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1031 
1032         if( libSymbolIt == m_libSymbols.end() )
1033         {
1034             // TODO: e.g. can depend on Template (RECORD=39
1035             m_reporter->Report( wxString::Format( _( "Bezier's owner (%d) not found." ),
1036                                                   elem.ownerindex ),
1037                                 RPT_SEVERITY_ERROR );
1038             return;
1039         }
1040 
1041         if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
1042             return;
1043 
1044         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
1045 
1046         for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
1047         {
1048             if( i + 2 == elem.points.size() )
1049             {
1050                 // special case: single line
1051                 LIB_SHAPE* line = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::POLY );
1052                 libSymbolIt->second->AddDrawItem( line );
1053 
1054                 line->SetUnit( elem.ownerpartid );
1055 
1056                 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
1057                 {
1058                     line->AddPoint( GetRelativePosition( elem.points.at( j ) + m_sheetOffset,
1059                                                          symbol ) );
1060                 }
1061 
1062                 line->SetWidth( elem.lineWidth );
1063             }
1064             else if( i + 3 == elem.points.size() )
1065             {
1066                 // TODO: special case of a single line with an extra point?
1067                 // I haven't a clue what this is all about, but the sample document we have in
1068                 // https://gitlab.com/kicad/code/kicad/-/issues/8974 responds best by treating it
1069                 // as another single line special case.
1070                 LIB_SHAPE* line = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::POLY );
1071                 libSymbolIt->second->AddDrawItem( line );
1072 
1073                 line->SetUnit( elem.ownerpartid );
1074 
1075                 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
1076                 {
1077                     line->AddPoint( GetRelativePosition( elem.points.at( j ) + m_sheetOffset,
1078                                                          symbol ) );
1079                 }
1080 
1081                 line->SetWidth( elem.lineWidth );
1082             }
1083             else
1084             {
1085                 // Bezier always has exactly 4 control points
1086                 LIB_SHAPE* bezier = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::BEZIER );
1087                 libSymbolIt->second->AddDrawItem( bezier );
1088 
1089                 bezier->SetUnit( elem.ownerpartid );
1090 
1091                 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
1092                 {
1093                     wxPoint pos = GetRelativePosition( elem.points.at( j ) + m_sheetOffset, symbol );
1094 
1095                     switch( j - i )
1096                     {
1097                     case 0: bezier->SetStart( pos ); break;
1098                     case 1: bezier->SetBezierC1( pos ); break;
1099                     case 2: bezier->SetBezierC2( pos ); break;
1100                     case 3: bezier->SetEnd( pos ); break;
1101                     default: break; // Can't get here but silence warnings
1102                     }
1103                 }
1104 
1105                 bezier->SetWidth( elem.lineWidth );
1106             }
1107         }
1108     }
1109 }
1110 
1111 
ParsePolyline(const std::map<wxString,wxString> & aProperties)1112 void SCH_ALTIUM_PLUGIN::ParsePolyline( const std::map<wxString, wxString>& aProperties )
1113 {
1114     ASCH_POLYLINE elem( aProperties );
1115 
1116     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1117     {
1118         PLOT_DASH_TYPE dashType = PLOT_DASH_TYPE::DEFAULT;
1119         switch( elem.linestyle )
1120         {
1121         default:
1122         case ASCH_POLYLINE_LINESTYLE::SOLID:       dashType = PLOT_DASH_TYPE::SOLID;   break;
1123         case ASCH_POLYLINE_LINESTYLE::DASHED:      dashType = PLOT_DASH_TYPE::DASH;    break;
1124         case ASCH_POLYLINE_LINESTYLE::DOTTED:      dashType = PLOT_DASH_TYPE::DOT;     break;
1125         case ASCH_POLYLINE_LINESTYLE::DASH_DOTTED: dashType = PLOT_DASH_TYPE::DASHDOT; break;
1126         }
1127 
1128         for( size_t i = 0; i + 1 < elem.points.size(); i++ )
1129         {
1130             SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
1131                                            SCH_LAYER_ID::LAYER_NOTES );
1132 
1133             line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
1134             line->SetLineWidth( elem.lineWidth );
1135             line->SetLineStyle( dashType );
1136 
1137             line->SetFlags( IS_NEW );
1138             m_currentSheet->GetScreen()->Append( line );
1139         }
1140     }
1141     else
1142     {
1143         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1144 
1145         if( libSymbolIt == m_libSymbols.end() )
1146         {
1147             // TODO: e.g. can depend on Template (RECORD=39
1148             m_reporter->Report( wxString::Format( _( "Polyline's owner (%d) not found." ),
1149                                                   elem.ownerindex ),
1150                                 RPT_SEVERITY_ERROR );
1151             return;
1152         }
1153 
1154         if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
1155             return;
1156 
1157         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
1158         LIB_SHAPE*  line = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::POLY );
1159         libSymbolIt->second->AddDrawItem( line );
1160 
1161         line->SetUnit( elem.ownerpartid );
1162 
1163         for( wxPoint& point : elem.points )
1164             line->AddPoint( GetRelativePosition( point + m_sheetOffset, symbol ) );
1165 
1166         line->SetWidth( elem.lineWidth );
1167     }
1168 }
1169 
1170 
ParsePolygon(const std::map<wxString,wxString> & aProperties)1171 void SCH_ALTIUM_PLUGIN::ParsePolygon( const std::map<wxString, wxString>& aProperties )
1172 {
1173     ASCH_POLYGON elem( aProperties );
1174 
1175     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1176     {
1177         // TODO: we cannot fill this polygon, only draw it for now
1178         for( size_t i = 0; i + 1 < elem.points.size(); i++ )
1179         {
1180             SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
1181                                            SCH_LAYER_ID::LAYER_NOTES );
1182             line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
1183             line->SetLineWidth( elem.lineWidth );
1184             line->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1185 
1186             line->SetFlags( IS_NEW );
1187             m_currentSheet->GetScreen()->Append( line );
1188         }
1189 
1190         // close polygon
1191         SCH_LINE* line = new SCH_LINE( elem.points.front() + m_sheetOffset,
1192                                        SCH_LAYER_ID::LAYER_NOTES );
1193         line->SetEndPoint( elem.points.back() + m_sheetOffset );
1194         line->SetLineWidth( elem.lineWidth );
1195         line->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1196 
1197         line->SetFlags( IS_NEW );
1198         m_currentSheet->GetScreen()->Append( line );
1199     }
1200     else
1201     {
1202         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1203 
1204         if( libSymbolIt == m_libSymbols.end() )
1205         {
1206             // TODO: e.g. can depend on Template (RECORD=39
1207             m_reporter->Report( wxString::Format( _( "Polygon's owner (%d) not found." ),
1208                                                   elem.ownerindex ),
1209                                 RPT_SEVERITY_ERROR );
1210             return;
1211         }
1212 
1213         if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
1214             return;
1215 
1216         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
1217         LIB_SHAPE*  line = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::POLY );
1218         libSymbolIt->second->AddDrawItem( line );
1219 
1220         line->SetUnit( elem.ownerpartid );
1221 
1222         for( wxPoint& point : elem.points )
1223             line->AddPoint( GetRelativePosition( point + m_sheetOffset, symbol ) );
1224 
1225         line->AddPoint( GetRelativePosition( elem.points.front() + m_sheetOffset, symbol ) );
1226 
1227         line->SetWidth( elem.lineWidth );
1228 
1229         if( !elem.isSolid )
1230             line->SetFillMode( FILL_T::NO_FILL );
1231         else if( elem.color == elem.areacolor )
1232             line->SetFillMode( FILL_T::FILLED_SHAPE );
1233         else
1234             line->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
1235     }
1236 }
1237 
1238 
ParseRoundRectangle(const std::map<wxString,wxString> & aProperties)1239 void SCH_ALTIUM_PLUGIN::ParseRoundRectangle( const std::map<wxString, wxString>& aProperties )
1240 {
1241     ASCH_ROUND_RECTANGLE elem( aProperties );
1242 
1243     wxPoint sheetTopRight   = elem.topRight + m_sheetOffset;
1244     wxPoint sheetBottomLeft = elem.bottomLeft + m_sheetOffset;
1245 
1246     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1247     {
1248         const wxPoint topLeft     = { sheetBottomLeft.x, sheetTopRight.y };
1249         const wxPoint bottomRight = { sheetTopRight.x, sheetBottomLeft.y };
1250 
1251         // TODO: we cannot fill this rectangle, only draw it for now
1252         // TODO: misses rounded edges
1253         SCH_LINE* lineTop = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1254         lineTop->SetEndPoint( topLeft );
1255         lineTop->SetLineWidth( elem.lineWidth );
1256         lineTop->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1257         lineTop->SetFlags( IS_NEW );
1258         m_currentSheet->GetScreen()->Append( lineTop );
1259 
1260         SCH_LINE* lineBottom = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1261         lineBottom->SetEndPoint( bottomRight );
1262         lineBottom->SetLineWidth( elem.lineWidth );
1263         lineBottom->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1264         lineBottom->SetFlags( IS_NEW );
1265         m_currentSheet->GetScreen()->Append( lineBottom );
1266 
1267         SCH_LINE* lineRight = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1268         lineRight->SetEndPoint( bottomRight );
1269         lineRight->SetLineWidth( elem.lineWidth );
1270         lineRight->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1271         lineRight->SetFlags( IS_NEW );
1272         m_currentSheet->GetScreen()->Append( lineRight );
1273 
1274         SCH_LINE* lineLeft = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1275         lineLeft->SetEndPoint( topLeft );
1276         lineLeft->SetLineWidth( elem.lineWidth );
1277         lineLeft->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1278         lineLeft->SetFlags( IS_NEW );
1279         m_currentSheet->GetScreen()->Append( lineLeft );
1280     }
1281     else
1282     {
1283         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1284 
1285         if( libSymbolIt == m_libSymbols.end() )
1286         {
1287             // TODO: e.g. can depend on Template (RECORD=39
1288             m_reporter->Report( wxString::Format( _( "Rounded rectangle's owner (%d) not found." ),
1289                                                   elem.ownerindex ),
1290                                 RPT_SEVERITY_ERROR );
1291             return;
1292         }
1293 
1294         if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
1295             return;
1296 
1297         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
1298         // TODO: misses rounded edges
1299         LIB_SHAPE*  rect = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::RECT );
1300         libSymbolIt->second->AddDrawItem( rect );
1301 
1302         rect->SetUnit( elem.ownerpartid );
1303 
1304         rect->SetPosition( GetRelativePosition( elem.topRight + m_sheetOffset, symbol ) );
1305         rect->SetEnd( GetRelativePosition( elem.bottomLeft + m_sheetOffset, symbol ) );
1306         rect->SetWidth( elem.lineWidth );
1307 
1308         if( !elem.isSolid )
1309             rect->SetFillMode( FILL_T::NO_FILL );
1310         else if( elem.color == elem.areacolor )
1311             rect->SetFillMode( FILL_T::FILLED_SHAPE );
1312         else
1313             rect->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
1314     }
1315 }
1316 
1317 
ParseArc(const std::map<wxString,wxString> & aProperties)1318 void SCH_ALTIUM_PLUGIN::ParseArc( const std::map<wxString, wxString>& aProperties )
1319 {
1320     ASCH_ARC elem( aProperties );
1321 
1322     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1323     {
1324         m_reporter->Report( _( "Arcs on schematic not currently supported." ),
1325                             RPT_SEVERITY_ERROR );
1326     }
1327     else
1328     {
1329         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1330 
1331         if( libSymbolIt == m_libSymbols.end() )
1332         {
1333             // TODO: e.g. can depend on Template (RECORD=39
1334             m_reporter->Report( wxString::Format( _( "Arc's owner (%d) not found." ),
1335                                                   elem.ownerindex ),
1336                                 RPT_SEVERITY_ERROR );
1337             return;
1338         }
1339 
1340         if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
1341             return;
1342 
1343         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
1344 
1345         if( elem.startAngle == 0 && ( elem.endAngle == 0 || elem.endAngle == 360 ) )
1346         {
1347             LIB_SHAPE* circle = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::CIRCLE );
1348             libSymbolIt->second->AddDrawItem( circle );
1349 
1350             circle->SetUnit( elem.ownerpartid );
1351 
1352             circle->SetPosition( GetRelativePosition( elem.center + m_sheetOffset, symbol ) );
1353             circle->SetEnd( circle->GetPosition() + wxPoint( elem.radius, 0 ) );
1354             circle->SetWidth( elem.lineWidth );
1355         }
1356         else
1357         {
1358             LIB_SHAPE* arc = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::ARC );
1359             libSymbolIt->second->AddDrawItem( arc );
1360             arc->SetUnit( elem.ownerpartid );
1361 
1362             arc->SetCenter( GetRelativePosition( elem.center + m_sheetOffset, symbol ) );
1363 
1364             wxPoint arcStart( elem.radius, 0 );
1365             RotatePoint( &arcStart.x, &arcStart.y, -elem.startAngle * 10.0 );
1366             arcStart += arc->GetCenter();
1367             arc->SetStart( arcStart );
1368 
1369             wxPoint arcEnd( elem.radius, 0 );
1370             RotatePoint( &arcEnd.x, &arcEnd.y, -elem.endAngle * 10.0 );
1371             arcEnd += arc->GetCenter();
1372             arc->SetEnd( arcEnd );
1373 
1374             arc->SetWidth( elem.lineWidth );
1375         }
1376     }
1377 }
1378 
1379 
ParseLine(const std::map<wxString,wxString> & aProperties)1380 void SCH_ALTIUM_PLUGIN::ParseLine( const std::map<wxString, wxString>& aProperties )
1381 {
1382     ASCH_LINE elem( aProperties );
1383 
1384     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1385     {
1386         // close polygon
1387         SCH_LINE* line = new SCH_LINE( elem.point1 + m_sheetOffset, SCH_LAYER_ID::LAYER_NOTES );
1388         line->SetEndPoint( elem.point2 + m_sheetOffset );
1389         line->SetLineWidth( elem.lineWidth );
1390         line->SetLineStyle( PLOT_DASH_TYPE::SOLID ); // TODO?
1391 
1392         line->SetFlags( IS_NEW );
1393         m_currentSheet->GetScreen()->Append( line );
1394     }
1395     else
1396     {
1397         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1398 
1399         if( libSymbolIt == m_libSymbols.end() )
1400         {
1401             // TODO: e.g. can depend on Template (RECORD=39
1402             m_reporter->Report( wxString::Format( _( "Line's owner (%d) not found." ),
1403                                                   elem.ownerindex ),
1404                                 RPT_SEVERITY_ERROR );
1405             return;
1406         }
1407 
1408         if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
1409             return;
1410 
1411         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
1412         LIB_SHAPE*  line = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::POLY );
1413         libSymbolIt->second->AddDrawItem( line );
1414 
1415         line->SetUnit( elem.ownerpartid );
1416 
1417         line->AddPoint( GetRelativePosition( elem.point1 + m_sheetOffset, symbol ) );
1418         line->AddPoint( GetRelativePosition( elem.point2 + m_sheetOffset, symbol ) );
1419 
1420         line->SetWidth( elem.lineWidth );
1421     }
1422 }
1423 
1424 
ParseRectangle(const std::map<wxString,wxString> & aProperties)1425 void SCH_ALTIUM_PLUGIN::ParseRectangle( const std::map<wxString, wxString>& aProperties )
1426 {
1427     ASCH_RECTANGLE elem( aProperties );
1428 
1429     wxPoint sheetTopRight   = elem.topRight + m_sheetOffset;
1430     wxPoint sheetBottomLeft = elem.bottomLeft + m_sheetOffset;
1431 
1432     if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1433     {
1434         const wxPoint topLeft     = { sheetBottomLeft.x, sheetTopRight.y };
1435         const wxPoint bottomRight = { sheetTopRight.x, sheetBottomLeft.y };
1436 
1437         // TODO: we cannot fill this rectangle, only draw it for now
1438         SCH_LINE* lineTop = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1439         lineTop->SetEndPoint( topLeft );
1440         lineTop->SetLineWidth( elem.lineWidth );
1441         lineTop->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1442         lineTop->SetFlags( IS_NEW );
1443         m_currentSheet->GetScreen()->Append( lineTop );
1444 
1445         SCH_LINE* lineBottom = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1446         lineBottom->SetEndPoint( bottomRight );
1447         lineBottom->SetLineWidth( elem.lineWidth );
1448         lineBottom->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1449         lineBottom->SetFlags( IS_NEW );
1450         m_currentSheet->GetScreen()->Append( lineBottom );
1451 
1452         SCH_LINE* lineRight = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1453         lineRight->SetEndPoint( bottomRight );
1454         lineRight->SetLineWidth( elem.lineWidth );
1455         lineRight->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1456         lineRight->SetFlags( IS_NEW );
1457         m_currentSheet->GetScreen()->Append( lineRight );
1458 
1459         SCH_LINE* lineLeft = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1460         lineLeft->SetEndPoint( topLeft );
1461         lineLeft->SetLineWidth( elem.lineWidth );
1462         lineLeft->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1463         lineLeft->SetFlags( IS_NEW );
1464         m_currentSheet->GetScreen()->Append( lineLeft );
1465     }
1466     else
1467     {
1468         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1469 
1470         if( libSymbolIt == m_libSymbols.end() )
1471         {
1472             // TODO: e.g. can depend on Template (RECORD=39
1473             m_reporter->Report( wxString::Format( _( "Rectangle's owner (%d) not found." ),
1474                                                   elem.ownerindex ),
1475                                 RPT_SEVERITY_ERROR );
1476             return;
1477         }
1478 
1479         if( !IsComponentPartVisible( elem.ownerindex, elem.ownerpartdisplaymode ) )
1480             return;
1481 
1482         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
1483         LIB_SHAPE*  rect = new LIB_SHAPE( libSymbolIt->second, SHAPE_T::RECT );
1484         libSymbolIt->second->AddDrawItem( rect );
1485 
1486         rect->SetUnit( elem.ownerpartid );
1487 
1488         rect->SetPosition( GetRelativePosition( sheetTopRight, symbol ) );
1489         rect->SetEnd( GetRelativePosition( sheetBottomLeft, symbol ) );
1490         rect->SetWidth( elem.lineWidth );
1491 
1492         if( !elem.isSolid )
1493             rect->SetFillMode( FILL_T::NO_FILL );
1494         else if( elem.color == elem.areacolor )
1495             rect->SetFillMode( FILL_T::FILLED_SHAPE );
1496         else
1497             rect->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
1498     }
1499 }
1500 
1501 
ParseSheetSymbol(int aIndex,const std::map<wxString,wxString> & aProperties)1502 void SCH_ALTIUM_PLUGIN::ParseSheetSymbol( int aIndex,
1503                                           const std::map<wxString, wxString>& aProperties )
1504 {
1505     ASCH_SHEET_SYMBOL elem( aProperties );
1506 
1507     SCH_SHEET*  sheet  = new SCH_SHEET( m_currentSheet, elem.location + m_sheetOffset );
1508     SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
1509 
1510     sheet->SetSize( elem.size );
1511     sheet->SetBorderColor( GetColorFromInt( elem.color ) );
1512 
1513     if( elem.isSolid )
1514         sheet->SetBackgroundColor( GetColorFromInt( elem.areacolor ) );
1515 
1516     sheet->SetScreen( screen );
1517 
1518     sheet->SetFlags( IS_NEW );
1519     m_currentSheet->GetScreen()->Append( sheet );
1520 
1521     SCH_SHEET_PATH sheetpath;
1522     m_rootSheet->LocatePathOfScreen( m_currentSheet->GetScreen(), &sheetpath );
1523     sheetpath.push_back( sheet );
1524 
1525     sheet->AddInstance( sheetpath );
1526     sheet->SetPageNumber( sheetpath, "#" );   // We'll update later if we find a pageNumber
1527                                               // record for it
1528 
1529     m_sheets.insert( { aIndex, sheet } );
1530 }
1531 
1532 
ParseSheetEntry(const std::map<wxString,wxString> & aProperties)1533 void SCH_ALTIUM_PLUGIN::ParseSheetEntry( const std::map<wxString, wxString>& aProperties )
1534 {
1535     ASCH_SHEET_ENTRY elem( aProperties );
1536 
1537     const auto& sheetIt = m_sheets.find( elem.ownerindex );
1538 
1539     if( sheetIt == m_sheets.end() )
1540     {
1541         m_reporter->Report( wxString::Format( _( "Sheet entry's owner (%d) not found." ),
1542                                               elem.ownerindex ),
1543                             RPT_SEVERITY_ERROR );
1544         return;
1545     }
1546 
1547     SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheetIt->second );
1548     sheetIt->second->AddPin( sheetPin );
1549 
1550     sheetPin->SetText( elem.name );
1551     sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );
1552     //sheetPin->SetLabelSpinStyle( getSpinStyle( term.OrientAngle, false ) );
1553     //sheetPin->SetPosition( getKiCadPoint( term.Position ) );
1554 
1555     wxPoint pos  = sheetIt->second->GetPosition();
1556     wxSize  size = sheetIt->second->GetSize();
1557 
1558     switch( elem.side )
1559     {
1560     default:
1561     case ASCH_SHEET_ENTRY_SIDE::LEFT:
1562         sheetPin->SetPosition( { pos.x, pos.y + elem.distanceFromTop } );
1563         sheetPin->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
1564         sheetPin->SetSide( SHEET_SIDE::LEFT );
1565         break;
1566     case ASCH_SHEET_ENTRY_SIDE::RIGHT:
1567         sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.distanceFromTop } );
1568         sheetPin->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
1569         sheetPin->SetSide( SHEET_SIDE::RIGHT );
1570         break;
1571     case ASCH_SHEET_ENTRY_SIDE::TOP:
1572         sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y } );
1573         sheetPin->SetLabelSpinStyle( LABEL_SPIN_STYLE::UP );
1574         sheetPin->SetSide( SHEET_SIDE::TOP );
1575         break;
1576     case ASCH_SHEET_ENTRY_SIDE::BOTTOM:
1577         sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y + size.y } );
1578         sheetPin->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM );
1579         sheetPin->SetSide( SHEET_SIDE::BOTTOM );
1580         break;
1581     }
1582 
1583     switch( elem.iotype )
1584     {
1585     default:
1586     case ASCH_PORT_IOTYPE::UNSPECIFIED:
1587         sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );
1588         break;
1589     case ASCH_PORT_IOTYPE::OUTPUT:
1590         sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT );
1591         break;
1592     case ASCH_PORT_IOTYPE::INPUT:
1593         sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT );
1594         break;
1595     case ASCH_PORT_IOTYPE::BIDI:
1596         sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI );
1597         break;
1598     }
1599 }
1600 
1601 
HelperGeneratePowerPortGraphics(LIB_SYMBOL * aKsymbol,ASCH_POWER_PORT_STYLE aStyle,REPORTER * aReporter)1602 wxPoint HelperGeneratePowerPortGraphics( LIB_SYMBOL* aKsymbol, ASCH_POWER_PORT_STYLE aStyle,
1603                                          REPORTER* aReporter )
1604 {
1605     if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE || aStyle == ASCH_POWER_PORT_STYLE::ARROW )
1606     {
1607         LIB_SHAPE* line1 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1608         aKsymbol->AddDrawItem( line1 );
1609         line1->SetWidth( Mils2iu( 10 ) );
1610         line1->AddPoint( { 0, 0 } );
1611         line1->AddPoint( { 0, Mils2iu( -50 ) } );
1612 
1613         if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE )
1614         {
1615             LIB_SHAPE* circle = new LIB_SHAPE( aKsymbol, SHAPE_T::CIRCLE );
1616             aKsymbol->AddDrawItem( circle );
1617             circle->SetWidth( Mils2iu( 5 ) );
1618             circle->SetPosition( { Mils2iu( 0 ), Mils2iu( -75 ) } );
1619             circle->SetEnd( circle->GetPosition() + wxPoint( Mils2iu( 25 ), 0 ) );
1620         }
1621         else
1622         {
1623             LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1624             aKsymbol->AddDrawItem( line2 );
1625             line2->SetWidth( Mils2iu( 10 ) );
1626             line2->AddPoint( { Mils2iu( -25 ), Mils2iu( -50 ) } );
1627             line2->AddPoint( { Mils2iu( 25 ), Mils2iu( -50 ) } );
1628             line2->AddPoint( { Mils2iu( 0 ), Mils2iu( -100 ) } );
1629             line2->AddPoint( { Mils2iu( -25 ), Mils2iu( -50 ) } );
1630         }
1631 
1632         return { 0, Mils2iu( 150 ) };
1633     }
1634     else if( aStyle == ASCH_POWER_PORT_STYLE::WAVE )
1635     {
1636         LIB_SHAPE* line = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1637         aKsymbol->AddDrawItem( line );
1638         line->SetWidth( Mils2iu( 10 ) );
1639         line->AddPoint( { 0, 0 } );
1640         line->AddPoint( { 0, Mils2iu( -72 ) } );
1641 
1642         LIB_SHAPE* bezier = new LIB_SHAPE( aKsymbol, SHAPE_T::BEZIER );
1643         aKsymbol->AddDrawItem( bezier );
1644         bezier->SetWidth( Mils2iu( 5 ) );
1645         bezier->AddPoint( { Mils2iu( 30 ), Mils2iu( -50 ) } );
1646         bezier->AddPoint( { Mils2iu( 30 ), Mils2iu( -87 ) } );
1647         bezier->AddPoint( { Mils2iu( -30 ), Mils2iu( -63 ) } );
1648         bezier->AddPoint( { Mils2iu( -30 ), Mils2iu( -100 ) } );
1649 
1650         return { 0, Mils2iu( 150 ) };
1651     }
1652     else if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND
1653              || aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND
1654              || aStyle == ASCH_POWER_PORT_STYLE::EARTH
1655              || aStyle == ASCH_POWER_PORT_STYLE::GOST_ARROW )
1656     {
1657         LIB_SHAPE* line1 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1658         aKsymbol->AddDrawItem( line1 );
1659         line1->SetWidth( Mils2iu( 10 ) );
1660         line1->AddPoint( { 0, 0 } );
1661         line1->AddPoint( { 0, Mils2iu( -100 ) } );
1662 
1663         if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND )
1664         {
1665             LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1666             aKsymbol->AddDrawItem( line2 );
1667             line2->SetWidth( Mils2iu( 10 ) );
1668             line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1669             line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -100 ) } );
1670 
1671             LIB_SHAPE* line3 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1672             aKsymbol->AddDrawItem( line3 );
1673             line3->SetWidth( Mils2iu( 10 ) );
1674             line3->AddPoint( { Mils2iu( -70 ), Mils2iu( -130 ) } );
1675             line3->AddPoint( { Mils2iu( 70 ), Mils2iu( -130 ) } );
1676 
1677             LIB_SHAPE* line4 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1678             aKsymbol->AddDrawItem( line4 );
1679             line4->SetWidth( Mils2iu( 10 ) );
1680             line4->AddPoint( { Mils2iu( -40 ), Mils2iu( -160 ) } );
1681             line4->AddPoint( { Mils2iu( 40 ), Mils2iu( -160 ) } );
1682 
1683             LIB_SHAPE* line5 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1684             aKsymbol->AddDrawItem( line5 );
1685             line5->SetWidth( Mils2iu( 10 ) );
1686             line5->AddPoint( { Mils2iu( -10 ), Mils2iu( -190 ) } );
1687             line5->AddPoint( { Mils2iu( 10 ), Mils2iu( -190 ) } );
1688         }
1689         else if( aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND )
1690         {
1691             LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1692             aKsymbol->AddDrawItem( line2 );
1693             line2->SetWidth( Mils2iu( 10 ) );
1694             line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1695             line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -100 ) } );
1696             line2->AddPoint( { Mils2iu( 0 ), Mils2iu( -200 ) } );
1697             line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1698         }
1699         else if( aStyle == ASCH_POWER_PORT_STYLE::EARTH )
1700         {
1701             LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1702             aKsymbol->AddDrawItem( line2 );
1703             line2->SetWidth( Mils2iu( 10 ) );
1704             line2->AddPoint( { Mils2iu( -150 ), Mils2iu( -200 ) } );
1705             line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1706             line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -100 ) } );
1707             line2->AddPoint( { Mils2iu( 50 ), Mils2iu( -200 ) } );
1708 
1709             LIB_SHAPE* line3 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1710             aKsymbol->AddDrawItem( line3 );
1711             line3->SetWidth( Mils2iu( 10 ) );
1712             line3->AddPoint( { Mils2iu( 0 ), Mils2iu( -100 ) } );
1713             line3->AddPoint( { Mils2iu( -50 ), Mils2iu( -200 ) } );
1714         }
1715         else // ASCH_POWER_PORT_STYLE::GOST_ARROW
1716         {
1717             LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1718             aKsymbol->AddDrawItem( line2 );
1719             line2->SetWidth( Mils2iu( 10 ) );
1720             line2->AddPoint( { Mils2iu( -25 ), Mils2iu( -50 ) } );
1721             line2->AddPoint( { Mils2iu( 0 ), Mils2iu( -100 ) } );
1722             line2->AddPoint( { Mils2iu( 25 ), Mils2iu( -50 ) } );
1723 
1724             return { 0, Mils2iu( 150 ) }; // special case
1725         }
1726 
1727         return { 0, Mils2iu( 250 ) };
1728     }
1729     else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_POWER_GROUND
1730              || aStyle == ASCH_POWER_PORT_STYLE::GOST_EARTH )
1731     {
1732         LIB_SHAPE* line1 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1733         aKsymbol->AddDrawItem( line1 );
1734         line1->SetWidth( Mils2iu( 10 ) );
1735         line1->AddPoint( { 0, 0 } );
1736         line1->AddPoint( { 0, Mils2iu( -160 ) } );
1737 
1738         LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1739         aKsymbol->AddDrawItem( line2 );
1740         line2->SetWidth( Mils2iu( 10 ) );
1741         line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -160 ) } );
1742         line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -160 ) } );
1743 
1744         LIB_SHAPE* line3 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1745         aKsymbol->AddDrawItem( line3 );
1746         line3->SetWidth( Mils2iu( 10 ) );
1747         line3->AddPoint( { Mils2iu( -60 ), Mils2iu( -200 ) } );
1748         line3->AddPoint( { Mils2iu( 60 ), Mils2iu( -200 ) } );
1749 
1750         LIB_SHAPE* line4 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1751         aKsymbol->AddDrawItem( line4 );
1752         line4->SetWidth( Mils2iu( 10 ) );
1753         line4->AddPoint( { Mils2iu( -20 ), Mils2iu( -240 ) } );
1754         line4->AddPoint( { Mils2iu( 20 ), Mils2iu( -240 ) } );
1755 
1756         if( aStyle == ASCH_POWER_PORT_STYLE::GOST_POWER_GROUND )
1757             return { 0, Mils2iu( 300 ) };
1758 
1759         LIB_SHAPE* circle = new LIB_SHAPE( aKsymbol, SHAPE_T::CIRCLE );
1760         aKsymbol->AddDrawItem( circle );
1761         circle->SetWidth( Mils2iu( 10 ) );
1762         circle->SetPosition( { Mils2iu( 0 ), Mils2iu( -160 ) } );
1763         circle->SetEnd( circle->GetPosition() + wxPoint( Mils2iu( 120 ), 0 ) );
1764 
1765         return { 0, Mils2iu( 350 ) };
1766     }
1767     else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_BAR )
1768     {
1769         LIB_SHAPE* line1 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1770         aKsymbol->AddDrawItem( line1 );
1771         line1->SetWidth( Mils2iu( 10 ) );
1772         line1->AddPoint( { 0, 0 } );
1773         line1->AddPoint( { 0, Mils2iu( -200 ) } );
1774 
1775         LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1776         aKsymbol->AddDrawItem( line2 );
1777         line2->SetWidth( Mils2iu( 10 ) );
1778         line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -200 ) } );
1779         line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -200 ) } );
1780 
1781         return { 0, Mils2iu( 250 ) };
1782     }
1783     else
1784     {
1785         if( aStyle != ASCH_POWER_PORT_STYLE::BAR )
1786         {
1787             aReporter->Report( _( "Power Port has unknown style, use bar instead." ),
1788                                RPT_SEVERITY_WARNING );
1789         }
1790 
1791         LIB_SHAPE* line1 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1792         aKsymbol->AddDrawItem( line1 );
1793         line1->SetWidth( Mils2iu( 10 ) );
1794         line1->AddPoint( { 0, 0 } );
1795         line1->AddPoint( { 0, Mils2iu( -100 ) } );
1796 
1797         LIB_SHAPE* line2 = new LIB_SHAPE( aKsymbol, SHAPE_T::POLY );
1798         aKsymbol->AddDrawItem( line2 );
1799         line2->SetWidth( Mils2iu( 10 ) );
1800         line2->AddPoint( { Mils2iu( -50 ), Mils2iu( -100 ) } );
1801         line2->AddPoint( { Mils2iu( 50 ), Mils2iu( -100 ) } );
1802 
1803         return { 0, Mils2iu( 150 ) };
1804     }
1805 }
1806 
1807 
ParsePowerPort(const std::map<wxString,wxString> & aProperties)1808 void SCH_ALTIUM_PLUGIN::ParsePowerPort( const std::map<wxString, wxString>& aProperties )
1809 {
1810     ASCH_POWER_PORT elem( aProperties );
1811     LIB_ID          libId = AltiumToKiCadLibID( getLibName(), elem.text );
1812     LIB_SYMBOL*     libSymbol = nullptr;
1813 
1814     const auto& powerSymbolIt = m_powerSymbols.find( elem.text );
1815 
1816     if( powerSymbolIt != m_powerSymbols.end() )
1817     {
1818         libSymbol = powerSymbolIt->second; // cache hit
1819     }
1820     else
1821     {
1822         libSymbol = new LIB_SYMBOL( wxEmptyString );
1823         libSymbol->SetPower();
1824         libSymbol->SetName( elem.text );
1825         libSymbol->GetReferenceField().SetText( "#PWR" );
1826         libSymbol->GetValueField().SetText( elem.text );
1827         libSymbol->GetValueField().SetVisible( true );
1828         libSymbol->SetDescription( wxString::Format( _( "Power symbol creates a global "
1829                                                         "label with name '%s'" ), elem.text ) );
1830         libSymbol->SetKeyWords( "power-flag" );
1831         libSymbol->SetLibId( libId );
1832 
1833         // generate graphic
1834         LIB_PIN* pin = new LIB_PIN( libSymbol );
1835         libSymbol->AddDrawItem( pin );
1836 
1837         pin->SetName( elem.text );
1838         pin->SetPosition( { 0, 0 } );
1839         pin->SetLength( 0 );
1840 
1841         // marks the pin as a global label
1842         pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
1843         pin->SetVisible( false );
1844 
1845         wxPoint valueFieldPos = HelperGeneratePowerPortGraphics( libSymbol, elem.style, m_reporter );
1846 
1847         libSymbol->GetValueField().SetPosition( valueFieldPos );
1848 
1849         // this has to be done after parsing the LIB_SYMBOL!
1850         m_pi->SaveSymbol( getLibFileName().GetFullPath(), libSymbol, m_properties.get() );
1851         m_powerSymbols.insert( { elem.text, libSymbol } );
1852     }
1853 
1854     SCH_SHEET_PATH sheetpath;
1855     m_rootSheet->LocatePathOfScreen( m_currentSheet->GetScreen(), &sheetpath );
1856 
1857     // each symbol has its own powerSymbolIt for now
1858     SCH_SYMBOL* symbol = new SCH_SYMBOL();
1859     symbol->SetRef( &sheetpath, "#PWR?" );
1860     symbol->SetValue( elem.text );
1861     symbol->SetLibId( libId );
1862     symbol->SetLibSymbol( new LIB_SYMBOL( *libSymbol ) );
1863 
1864     SCH_FIELD* valueField = symbol->GetField( VALUE_FIELD );
1865     valueField->SetVisible( elem.showNetName );
1866 
1867     // TODO: Why do I need to set this a second time?
1868     valueField->SetPosition( libSymbol->GetValueField().GetPosition() );
1869 
1870     symbol->SetPosition( elem.location + m_sheetOffset );
1871 
1872     switch( elem.orientation )
1873     {
1874     case ASCH_RECORD_ORIENTATION::RIGHTWARDS:
1875         symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_90 );
1876         valueField->SetTextAngle( TEXT_ANGLE_VERT );
1877         valueField->SetHorizJustify( EDA_TEXT_HJUSTIFY_T::GR_TEXT_HJUSTIFY_RIGHT );
1878         break;
1879     case ASCH_RECORD_ORIENTATION::UPWARDS:
1880         symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_180 );
1881         valueField->SetTextAngle( TEXT_ANGLE_HORIZ );
1882         valueField->SetHorizJustify( EDA_TEXT_HJUSTIFY_T::GR_TEXT_HJUSTIFY_CENTER );
1883         break;
1884     case ASCH_RECORD_ORIENTATION::LEFTWARDS:
1885         symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_270 );
1886         valueField->SetTextAngle( TEXT_ANGLE_VERT );
1887         valueField->SetHorizJustify( EDA_TEXT_HJUSTIFY_T::GR_TEXT_HJUSTIFY_RIGHT );
1888         break;
1889     case ASCH_RECORD_ORIENTATION::DOWNWARDS:
1890         symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_0 );
1891         valueField->SetTextAngle( TEXT_ANGLE_HORIZ );
1892         valueField->SetHorizJustify( EDA_TEXT_HJUSTIFY_T::GR_TEXT_HJUSTIFY_CENTER );
1893         break;
1894     default:
1895         m_reporter->Report( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
1896         break;
1897     }
1898 
1899     m_currentSheet->GetScreen()->Append( symbol );
1900 }
1901 
1902 
ParsePort(const ASCH_PORT & aElem)1903 void SCH_ALTIUM_PLUGIN::ParsePort( const ASCH_PORT& aElem )
1904 {
1905     bool    isHarness = !aElem.harnessType.IsEmpty();
1906     wxPoint start     = aElem.location + m_sheetOffset;
1907     wxPoint end       = start;
1908 
1909     switch( aElem.style )
1910     {
1911     default:
1912     case ASCH_PORT_STYLE::NONE_HORIZONTAL:
1913     case ASCH_PORT_STYLE::LEFT:
1914     case ASCH_PORT_STYLE::RIGHT:
1915     case ASCH_PORT_STYLE::LEFT_RIGHT:
1916         end.x += aElem.width;
1917         break;
1918     case ASCH_PORT_STYLE::NONE_VERTICAL:
1919     case ASCH_PORT_STYLE::TOP:
1920     case ASCH_PORT_STYLE::BOTTOM:
1921     case ASCH_PORT_STYLE::TOP_BOTTOM:
1922         end.y -= aElem.width;
1923         break;
1924     }
1925 
1926     // Check which connection points exists in the schematic
1927     SCH_SCREEN* screen = m_currentSheet->GetScreen();
1928 
1929     bool startIsWireTerminal = screen->IsTerminalPoint( start, LAYER_WIRE );
1930     bool startIsBusTerminal  = screen->IsTerminalPoint( start, LAYER_BUS );
1931 
1932     bool endIsWireTerminal = screen->IsTerminalPoint( end, LAYER_WIRE );
1933     bool endIsBusTerminal  = screen->IsTerminalPoint( end, LAYER_BUS );
1934 
1935     // check if any of the points is a terminal point
1936     // TODO: there seems a problem to detect approximated connections towards component pins?
1937     bool connectionFound = startIsWireTerminal
1938                             || startIsBusTerminal
1939                             || endIsWireTerminal
1940                             || endIsBusTerminal;
1941 
1942     if( !isHarness && !connectionFound )
1943     {
1944         m_reporter->Report( wxString::Format( _( "Port %s has no connections." ), aElem.name ),
1945                             RPT_SEVERITY_WARNING );
1946     }
1947 
1948     // Select label position. In case both match, we will add a line later.
1949     wxPoint   position = ( startIsWireTerminal || startIsBusTerminal ) ? start : end;
1950     SCH_TEXT* label;
1951 
1952     if( isHarness )
1953     {
1954         wxString name = wxT( "HARNESS: " ) + aElem.name;
1955 
1956         if( aElem.harnessType != aElem.name )
1957             name += wxString::Format( wxT( " (%s)" ), aElem.harnessType );
1958 
1959         label = new SCH_TEXT( position, name );
1960     }
1961     // TODO: detect correct label type depending on sheet settings, etc.
1962     //{
1963     //    label = new SCH_HIERLABEL( elem.location + m_sheetOffset, elem.name );
1964     //}
1965     else
1966     {
1967 
1968         label = new SCH_GLOBALLABEL( position, aElem.name );
1969     }
1970 
1971     switch( aElem.iotype )
1972     {
1973     default:
1974     case ASCH_PORT_IOTYPE::UNSPECIFIED:
1975         label->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );
1976         break;
1977     case ASCH_PORT_IOTYPE::OUTPUT:
1978         label->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT );
1979         break;
1980     case ASCH_PORT_IOTYPE::INPUT:
1981         label->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT );
1982         break;
1983     case ASCH_PORT_IOTYPE::BIDI:
1984         label->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI );
1985         break;
1986     }
1987 
1988     switch( aElem.style )
1989     {
1990     default:
1991     case ASCH_PORT_STYLE::NONE_HORIZONTAL:
1992     case ASCH_PORT_STYLE::LEFT:
1993     case ASCH_PORT_STYLE::RIGHT:
1994     case ASCH_PORT_STYLE::LEFT_RIGHT:
1995         if( ( startIsWireTerminal || startIsBusTerminal ) )
1996             label->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
1997         else
1998             label->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
1999         break;
2000     case ASCH_PORT_STYLE::NONE_VERTICAL:
2001     case ASCH_PORT_STYLE::TOP:
2002     case ASCH_PORT_STYLE::BOTTOM:
2003     case ASCH_PORT_STYLE::TOP_BOTTOM:
2004         if( ( startIsWireTerminal || startIsBusTerminal ) )
2005             label->SetLabelSpinStyle( LABEL_SPIN_STYLE::UP );
2006         else
2007             label->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM );
2008         break;
2009     }
2010 
2011     label->SetFlags( IS_NEW );
2012     m_currentSheet->GetScreen()->Append( label );
2013 
2014     // This is a hack, for the case both connection points are valid: add a small wire
2015     if( ( startIsWireTerminal && endIsWireTerminal ) )
2016     {
2017         SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
2018         wire->SetEndPoint( end );
2019         wire->SetLineWidth( Mils2iu( 2 ) );
2020         wire->SetFlags( IS_NEW );
2021         m_currentSheet->GetScreen()->Append( wire );
2022     }
2023     else if( startIsBusTerminal && endIsBusTerminal )
2024     {
2025         SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
2026         wire->SetEndPoint( end );
2027         wire->SetLineWidth( Mils2iu( 2 ) );
2028         wire->SetFlags( IS_NEW );
2029         m_currentSheet->GetScreen()->Append( wire );
2030     }
2031 }
2032 
2033 
ParseNoERC(const std::map<wxString,wxString> & aProperties)2034 void SCH_ALTIUM_PLUGIN::ParseNoERC( const std::map<wxString, wxString>& aProperties )
2035 {
2036     ASCH_NO_ERC elem( aProperties );
2037 
2038     if( elem.isActive )
2039     {
2040         SCH_NO_CONNECT* noConnect = new SCH_NO_CONNECT( elem.location + m_sheetOffset );
2041 
2042         noConnect->SetFlags( IS_NEW );
2043         m_currentSheet->GetScreen()->Append( noConnect );
2044     }
2045 }
2046 
2047 
ParseNetLabel(const std::map<wxString,wxString> & aProperties)2048 void SCH_ALTIUM_PLUGIN::ParseNetLabel( const std::map<wxString, wxString>& aProperties )
2049 {
2050     ASCH_NET_LABEL elem( aProperties );
2051 
2052     SCH_LABEL* label = new SCH_LABEL( elem.location + m_sheetOffset, elem.text );
2053 
2054     switch( elem.orientation )
2055     {
2056     case ASCH_RECORD_ORIENTATION::RIGHTWARDS:
2057         label->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
2058         break;
2059     case ASCH_RECORD_ORIENTATION::UPWARDS:
2060         label->SetLabelSpinStyle( LABEL_SPIN_STYLE::UP );
2061         break;
2062     case ASCH_RECORD_ORIENTATION::LEFTWARDS:
2063         label->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
2064         break;
2065     case ASCH_RECORD_ORIENTATION::DOWNWARDS:
2066         label->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM );
2067         break;
2068     default:
2069         break;
2070     }
2071 
2072     label->SetFlags( IS_NEW );
2073     m_currentSheet->GetScreen()->Append( label );
2074 }
2075 
2076 
ParseBus(const std::map<wxString,wxString> & aProperties)2077 void SCH_ALTIUM_PLUGIN::ParseBus( const std::map<wxString, wxString>& aProperties )
2078 {
2079     ASCH_BUS elem( aProperties );
2080 
2081     for( size_t i = 0; i + 1 < elem.points.size(); i++ )
2082     {
2083         SCH_LINE* bus = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
2084                                       SCH_LAYER_ID::LAYER_BUS );
2085         bus->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
2086         bus->SetLineWidth( elem.lineWidth );
2087 
2088         bus->SetFlags( IS_NEW );
2089         m_currentSheet->GetScreen()->Append( bus );
2090     }
2091 }
2092 
2093 
ParseWire(const std::map<wxString,wxString> & aProperties)2094 void SCH_ALTIUM_PLUGIN::ParseWire( const std::map<wxString, wxString>& aProperties )
2095 {
2096     ASCH_WIRE elem( aProperties );
2097 
2098     for( size_t i = 0; i + 1 < elem.points.size(); i++ )
2099     {
2100         SCH_LINE* wire =
2101                 new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_WIRE );
2102         wire->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
2103         wire->SetLineWidth( elem.lineWidth );
2104 
2105         wire->SetFlags( IS_NEW );
2106         m_currentSheet->GetScreen()->Append( wire );
2107     }
2108 }
2109 
2110 
ParseJunction(const std::map<wxString,wxString> & aProperties)2111 void SCH_ALTIUM_PLUGIN::ParseJunction( const std::map<wxString, wxString>& aProperties )
2112 {
2113     ASCH_JUNCTION elem( aProperties );
2114 
2115     SCH_JUNCTION* junction = new SCH_JUNCTION( elem.location + m_sheetOffset );
2116 
2117     junction->SetFlags( IS_NEW );
2118     m_currentSheet->GetScreen()->Append( junction );
2119 }
2120 
2121 
ParseImage(const std::map<wxString,wxString> & aProperties)2122 void SCH_ALTIUM_PLUGIN::ParseImage( const std::map<wxString, wxString>& aProperties )
2123 {
2124     ASCH_IMAGE elem( aProperties );
2125 
2126     wxPoint                     center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
2127     std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
2128 
2129     if( elem.embedimage )
2130     {
2131         const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
2132 
2133         if( !storageFile )
2134         {
2135             wxString msg = wxString::Format( _( "Embedded file %s not found in storage." ),
2136                                              elem.filename );
2137             m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2138             return;
2139         }
2140 
2141         wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
2142 
2143         // As wxZlibInputStream is not seekable, we need to write a temporary file
2144         wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
2145         wxZlibInputStream   zlibInputStream( fileStream );
2146         wxFFileOutputStream outputStream( storagePath );
2147         outputStream.Write( zlibInputStream );
2148         outputStream.Close();
2149 
2150         if( !bitmap->ReadImageFile( storagePath ) )
2151         {
2152             m_reporter->Report( wxString::Format( _( "Error reading image %s." ), storagePath ),
2153                                 RPT_SEVERITY_ERROR );
2154             return;
2155         }
2156 
2157         // Remove temporary file
2158         wxRemoveFile( storagePath );
2159     }
2160     else
2161     {
2162         if( !wxFileExists( elem.filename ) )
2163         {
2164             m_reporter->Report( wxString::Format( _( "File not found %s." ), elem.filename ),
2165                                 RPT_SEVERITY_ERROR );
2166             return;
2167         }
2168 
2169         if( !bitmap->ReadImageFile( elem.filename ) )
2170         {
2171             m_reporter->Report( wxString::Format( _( "Error reading image %s." ), elem.filename ),
2172                                 RPT_SEVERITY_ERROR );
2173             return;
2174         }
2175     }
2176 
2177     // we only support one scale, thus we need to select one in case it does not keep aspect ratio
2178     wxSize  currentImageSize = bitmap->GetSize();
2179     wxPoint expectedImageSize = elem.location - elem.corner;
2180     double  scaleX = std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
2181     double  scaleY = std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
2182     bitmap->SetImageScale( std::min( scaleX, scaleY ) );
2183 
2184     bitmap->SetFlags( IS_NEW );
2185     m_currentSheet->GetScreen()->Append( bitmap.release() );
2186 }
2187 
2188 
ParseSheet(const std::map<wxString,wxString> & aProperties)2189 void SCH_ALTIUM_PLUGIN::ParseSheet( const std::map<wxString, wxString>& aProperties )
2190 {
2191     m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
2192 
2193     PAGE_INFO pageInfo;
2194 
2195     bool isPortrait = m_altiumSheet->sheetOrientation == ASCH_SHEET_WORKSPACEORIENTATION::PORTRAIT;
2196 
2197     switch( m_altiumSheet->sheetSize )
2198     {
2199     default:
2200     case ASCH_SHEET_SIZE::A4:      pageInfo.SetType( "A4", isPortrait );       break;
2201     case ASCH_SHEET_SIZE::A3:      pageInfo.SetType( "A3", isPortrait );       break;
2202     case ASCH_SHEET_SIZE::A2:      pageInfo.SetType( "A2", isPortrait );       break;
2203     case ASCH_SHEET_SIZE::A1:      pageInfo.SetType( "A1", isPortrait );       break;
2204     case ASCH_SHEET_SIZE::A0:      pageInfo.SetType( "A0", isPortrait );       break;
2205     case ASCH_SHEET_SIZE::A:       pageInfo.SetType( "A", isPortrait );        break;
2206     case ASCH_SHEET_SIZE::B:       pageInfo.SetType( "B", isPortrait );        break;
2207     case ASCH_SHEET_SIZE::C:       pageInfo.SetType( "C", isPortrait );        break;
2208     case ASCH_SHEET_SIZE::D:       pageInfo.SetType( "D", isPortrait );        break;
2209     case ASCH_SHEET_SIZE::E:       pageInfo.SetType( "E", isPortrait );        break;
2210     case ASCH_SHEET_SIZE::LETTER:  pageInfo.SetType( "USLetter", isPortrait ); break;
2211     case ASCH_SHEET_SIZE::LEGAL:   pageInfo.SetType( "USLegal", isPortrait );  break;
2212     case ASCH_SHEET_SIZE::TABLOID: pageInfo.SetType( "A3", isPortrait );       break;
2213     case ASCH_SHEET_SIZE::ORCAD_A: pageInfo.SetType( "A", isPortrait );        break;
2214     case ASCH_SHEET_SIZE::ORCAD_B: pageInfo.SetType( "B", isPortrait );        break;
2215     case ASCH_SHEET_SIZE::ORCAD_C: pageInfo.SetType( "C", isPortrait );        break;
2216     case ASCH_SHEET_SIZE::ORCAD_D: pageInfo.SetType( "D", isPortrait );        break;
2217     case ASCH_SHEET_SIZE::ORCAD_E: pageInfo.SetType( "E", isPortrait );        break;
2218     }
2219 
2220     m_currentSheet->GetScreen()->SetPageSettings( pageInfo );
2221 
2222     m_sheetOffset = { 0, pageInfo.GetHeightIU() };
2223 }
2224 
2225 
ParseSheetName(const std::map<wxString,wxString> & aProperties)2226 void SCH_ALTIUM_PLUGIN::ParseSheetName( const std::map<wxString, wxString>& aProperties )
2227 {
2228     ASCH_SHEET_NAME elem( aProperties );
2229 
2230     const auto& sheetIt = m_sheets.find( elem.ownerindex );
2231 
2232     if( sheetIt == m_sheets.end() )
2233     {
2234         m_reporter->Report( wxString::Format( _( "Sheetname's owner (%d) not found." ),
2235                                               elem.ownerindex ),
2236                             RPT_SEVERITY_ERROR );
2237         return;
2238     }
2239 
2240     SCH_FIELD& sheetNameField = sheetIt->second->GetFields()[SHEETNAME];
2241 
2242     sheetNameField.SetPosition( elem.location + m_sheetOffset );
2243     sheetNameField.SetText( elem.text );
2244     sheetNameField.SetVisible( !elem.isHidden );
2245     SetTextPositioning( &sheetNameField, ASCH_LABEL_JUSTIFICATION::BOTTOM_LEFT, elem.orientation );
2246 }
2247 
2248 
ParseFileName(const std::map<wxString,wxString> & aProperties)2249 void SCH_ALTIUM_PLUGIN::ParseFileName( const std::map<wxString, wxString>& aProperties )
2250 {
2251     ASCH_FILE_NAME elem( aProperties );
2252 
2253     const auto& sheetIt = m_sheets.find( elem.ownerindex );
2254 
2255     if( sheetIt == m_sheets.end() )
2256     {
2257         m_reporter->Report( wxString::Format( _( "Filename's owner (%d) not found." ),
2258                                               elem.ownerindex ),
2259                             RPT_SEVERITY_ERROR );
2260         return;
2261     }
2262 
2263     SCH_FIELD& filenameField = sheetIt->second->GetFields()[SHEETFILENAME];
2264 
2265     filenameField.SetPosition( elem.location + m_sheetOffset );
2266 
2267     // If last symbols are ".sChDoC", change them to ".kicad_sch"
2268     if( ( elem.text.Right( GetFileExtension().length() + 1 ).Lower() )
2269             == ( "." + GetFileExtension().Lower() ) )
2270     {
2271         elem.text.RemoveLast( GetFileExtension().length() );
2272         elem.text += KiCadSchematicFileExtension;
2273     }
2274 
2275     filenameField.SetText( elem.text );
2276 
2277     filenameField.SetVisible( !elem.isHidden );
2278     SetTextPositioning( &filenameField, ASCH_LABEL_JUSTIFICATION::BOTTOM_LEFT, elem.orientation );
2279 }
2280 
2281 
ParseDesignator(const std::map<wxString,wxString> & aProperties)2282 void SCH_ALTIUM_PLUGIN::ParseDesignator( const std::map<wxString, wxString>& aProperties )
2283 {
2284     ASCH_DESIGNATOR elem( aProperties );
2285 
2286     const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2287 
2288     if( libSymbolIt == m_libSymbols.end() )
2289     {
2290         // TODO: e.g. can depend on Template (RECORD=39
2291         m_reporter->Report( wxString::Format( _( "Designator's owner (%d) not found." ),
2292                                               elem.ownerindex ),
2293                             RPT_SEVERITY_ERROR );
2294         return;
2295     }
2296 
2297     SCH_SYMBOL*    symbol = m_symbols.at( libSymbolIt->first );
2298     SCH_SHEET_PATH sheetpath;
2299     m_rootSheet->LocatePathOfScreen( m_currentSheet->GetScreen(), &sheetpath );
2300 
2301     symbol->SetRef( &sheetpath, elem.text );
2302 
2303     SCH_FIELD* refField = symbol->GetField( REFERENCE_FIELD );
2304 
2305     refField->SetPosition( elem.location + m_sheetOffset );
2306     refField->SetVisible( true );
2307     SetTextPositioning( refField, elem.justification, elem.orientation );
2308 }
2309 
2310 
ParseBusEntry(const std::map<wxString,wxString> & aProperties)2311 void SCH_ALTIUM_PLUGIN::ParseBusEntry( const std::map<wxString, wxString>& aProperties )
2312 {
2313     ASCH_BUS_ENTRY elem( aProperties );
2314 
2315     SCH_BUS_WIRE_ENTRY* busWireEntry = new SCH_BUS_WIRE_ENTRY( elem.location + m_sheetOffset );
2316 
2317     wxPoint vector = elem.corner - elem.location;
2318     busWireEntry->SetSize( { vector.x, vector.y } );
2319 
2320     busWireEntry->SetFlags( IS_NEW );
2321     m_currentSheet->GetScreen()->Append( busWireEntry );
2322 }
2323 
2324 
ParseParameter(const std::map<wxString,wxString> & aProperties)2325 void SCH_ALTIUM_PLUGIN::ParseParameter( const std::map<wxString, wxString>& aProperties )
2326 {
2327     ASCH_PARAMETER elem( aProperties );
2328 
2329     // TODO: fill in replacements from variant, sheet and project
2330     std::map<wxString, wxString> variableMap = {
2331         { "COMMENT", "VALUE"        },
2332         { "VALUE",   "ALTIUM_VALUE" },
2333     };
2334 
2335     if( elem.ownerindex <= 0 && elem.ownerpartid == ALTIUM_COMPONENT_NONE )
2336     {
2337         // This is some sheet parameter
2338         if( elem.text == "*" )
2339             return; // indicates parameter not set?
2340 
2341         wxString paramName = elem.name.Upper();
2342 
2343         if( paramName == "SHEETNUMBER" )
2344         {
2345             SCH_SHEET_PATH sheetpath;
2346             m_rootSheet->LocatePathOfScreen( m_currentSheet->GetScreen(), &sheetpath );
2347 
2348             m_rootSheet->SetPageNumber( sheetpath, elem.text );
2349         }
2350         else if( paramName == "TITLE" )
2351         {
2352             m_currentTitleBlock->SetTitle( elem.text );
2353         }
2354         else if( paramName == "REVISION" )
2355         {
2356             m_currentTitleBlock->SetRevision( elem.text );
2357         }
2358         else if( paramName == "DATE" )
2359         {
2360             m_currentTitleBlock->SetDate( elem.text );
2361         }
2362         else if( paramName == "COMPANYNAME" )
2363         {
2364             m_currentTitleBlock->SetCompany( elem.text );
2365         }
2366         else
2367         {
2368             m_schematic->Prj().GetTextVars()[ paramName ] = elem.text;
2369         }
2370     }
2371     else
2372     {
2373         const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2374 
2375         if( libSymbolIt == m_libSymbols.end() )
2376         {
2377             // TODO: e.g. can depend on Template (RECORD=39
2378             return;
2379         }
2380 
2381         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
2382         SCH_FIELD*  field = nullptr;
2383 
2384         if( elem.name.Upper() == "COMMENT" )
2385             field = symbol->GetField( VALUE_FIELD );
2386         else
2387         {
2388             int      fieldIdx = symbol->GetFieldCount();
2389             wxString fieldName = elem.name.Upper();
2390 
2391             if( fieldName == "VALUE" )
2392                 fieldName = "ALTIUM_VALUE";
2393 
2394             field = symbol->AddField( SCH_FIELD( wxPoint(), fieldIdx, symbol, fieldName ) );
2395         }
2396 
2397         wxString kicadText = AltiumSpecialStringsToKiCadVariables( elem.text, variableMap );
2398         field->SetText( kicadText );
2399         field->SetPosition( elem.location + m_sheetOffset );
2400         field->SetVisible( !elem.isHidden );
2401         SetTextPositioning( field, elem.justification, elem.orientation );
2402     }
2403 }
2404 
2405 
ParseImplementationList(int aIndex,const std::map<wxString,wxString> & aProperties)2406 void SCH_ALTIUM_PLUGIN::ParseImplementationList( int aIndex,
2407                                                  const std::map<wxString, wxString>& aProperties )
2408 {
2409     ASCH_IMPLEMENTATION_LIST elem( aProperties );
2410 
2411     m_altiumImplementationList.emplace( aIndex, elem.ownerindex );
2412 }
2413 
2414 
ParseImplementation(const std::map<wxString,wxString> & aProperties)2415 void SCH_ALTIUM_PLUGIN::ParseImplementation( const std::map<wxString, wxString>& aProperties )
2416 {
2417     ASCH_IMPLEMENTATION elem( aProperties );
2418 
2419     // Only get footprint, currently assigned only
2420     if( ( elem.type == "PCBLIB" ) && ( elem.isCurrent ) )
2421     {
2422         const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex );
2423 
2424         if( implementationOwnerIt == m_altiumImplementationList.end() )
2425         {
2426             m_reporter->Report( wxString::Format( _( "Implementation's owner (%d) not found." ),
2427                                                   elem.ownerindex ),
2428                                 RPT_SEVERITY_ERROR );
2429             return;
2430         }
2431 
2432         const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second );
2433 
2434         if( libSymbolIt == m_libSymbols.end() )
2435         {
2436             m_reporter->Report( wxString::Format( _( "Footprint's owner (%d) not found." ),
2437                                                   implementationOwnerIt->second ),
2438                                 RPT_SEVERITY_ERROR );
2439             return;
2440         }
2441 
2442         LIB_ID        fpLibId = AltiumToKiCadLibID( elem.libname, elem.name );
2443         wxArrayString fpFilters;
2444         fpFilters.Add( fpLibId.Format() );
2445 
2446         libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it
2447 
2448         SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
2449 
2450         symbol->SetFootprint( fpLibId.Format() );
2451     }
2452 }
2453