KiCad PCB EDA Suite
Loading...
Searching...
No Matches
import_proj.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include "import_proj.h"
23#include <confirm.h>
24#include <kidialog.h>
26#include <macros.h>
27#include <string_utils.h>
28#include <richio.h>
29
30#include <wx/fileconf.h>
31#include <wx/filedlg.h>
32#include <wx/msgdlg.h>
33#include <wx/tokenzr.h>
34#include <wx/dir.h>
35#include <wx/textfile.h>
36#include <wx/wfstream.h>
37
38#include <kiway.h>
39#include <kiway_player.h>
40#include <kicad_manager_frame.h>
41#include <pcb_edit_frame.h>
42#include <sch_edit_frame.h>
43
44#include <sch_io/sch_io_mgr.h>
45#include <pcb_io/pcb_io_mgr.h>
46#include <project_sch.h>
47#include <project_pcb.h>
48
53#include <io/pads/pads_common.h>
57
58#include <wx/log.h>
59
60
62 const std::vector<wxString>& aSchFileExtensions,
63 const std::vector<wxString>& aPcbFileExtensions ) :
64 m_frame( aFrame ),
65 m_schExtenstions( aSchFileExtensions ), m_pcbExtenstions( aPcbFileExtensions )
66{
67}
68
69
71{
72 // Append a new directory with the same name of the project file
73 // Keep iterating until we find an empty directory
74 wxString newDir = m_TargetProj.GetName();
75 int attempt = 0;
76
77 m_TargetProj.AppendDir( newDir );
78
79 while( m_TargetProj.DirExists() )
80 {
81 m_TargetProj.RemoveLastDir();
82 wxString suffix = wxString::Format( "_%d", ++attempt );
83 m_TargetProj.AppendDir( newDir + suffix );
84 }
85}
86
87
89{
90 wxString m_file;
91
92public:
93 SCOPED_FILE_REMOVER( const wxString& aFile ) : m_file( aFile ) {}
94 ~SCOPED_FILE_REMOVER() { wxRemoveFile( m_file ); }
95};
96
97
98void IMPORT_PROJ_HELPER::ImportIndividualFile( KICAD_T aFT, int aImportedFileType )
99{
100 FRAME_T frame_type;
101 wxString appImportFile;
102 std::vector<wxString> neededExts;
103
104 switch( aFT )
105 {
106 case SCHEMATIC_T:
107 neededExts = m_schExtenstions;
108 frame_type = FRAME_SCH;
109 break;
110
111 case PCB_T:
112 neededExts = m_pcbExtenstions;
113 frame_type = FRAME_PCB_EDITOR;
114 break;
115
116 default: return;
117 }
118
119 std::vector<SCOPED_FILE_REMOVER> copiedFiles;
120
121 for( wxString ext : neededExts )
122 {
123 if( ext == wxS( "INPUT" ) )
124 ext = m_InputFile.GetExt();
125
126 wxFileName candidate = m_InputFile;
127
128 // Selected file often the one wanted (e.g. schematic in schematic-only
129 // import); keep on-disk name so uppercase ext still resolves on
130 // case-sensitive fs. Rewrite ext only when seeking differently-typed sibling
131 if( m_InputFile.GetExt().CmpNoCase( ext ) != 0 )
132 candidate.SetExt( ext );
133
134 if( !candidate.FileExists() )
135 continue;
136
137 wxFileName targetFile( m_TargetProj.GetPath(), candidate.GetName(), candidate.GetExt() );
138
139 if( !targetFile.FileExists() )
140 {
141 bool copied = wxCopyFile( candidate.GetFullPath(), targetFile.GetFullPath(), false );
142
143 if( copied )
144 {
145 // Will be auto-removed
146 copiedFiles.emplace_back( targetFile.GetFullPath() );
147 }
148 }
149
150 // Pick the first file to pass to application
151 if( appImportFile.empty() && targetFile.FileExists() )
152 appImportFile = targetFile.GetFullPath();
153 }
154
155 if( appImportFile.empty() )
156 return;
157
158 doImport( appImportFile, frame_type, aImportedFileType );
159}
160
161
162void IMPORT_PROJ_HELPER::doImport( const wxString& aFile, FRAME_T aFrameType, int aImportedFileType )
163{
164 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( aFrameType, true ) )
165 {
166 std::stringstream ss;
167 ss << aImportedFileType << '\n' << TO_UTF8( aFile );
168
169 for( const auto& [key, value] : m_properties )
170 ss << '\n' << key << '\n' << value.wx_str();
171
172 std::string packet = ss.str();
173 frame->Kiway().ExpressMail( aFrameType, MAIL_IMPORT_FILE, packet, m_frame );
174
175 if( aFrameType == FRAME_SCH && aImportedFileType == SCH_IO_MGR::SCH_ORCAD
178
179 if( !frame->IsShownOnScreen() )
180 frame->Show( true );
181
182 // On Windows, Raise() does not bring the window on screen, when iconized
183 if( frame->IsIconized() )
184 frame->Iconize( false );
185
186 frame->Raise();
187 }
188}
189
190
191void IMPORT_PROJ_HELPER::EasyEDAProProjectHandler( int aImportedSchFileType, int aImportedPcbFileType )
192{
193 const bool isV3 =
194 aImportedSchFileType == SCH_IO_MGR::SCH_EASYEDAPRO_V3 || aImportedPcbFileType == PCB_IO_MGR::EASYEDAPRO_V3;
195
196 // Sch/PCB importers both assign FPIDs under ShortenLibName( archive ); advertise that
197 // nickname as the source footprint lib so post-import reconciler keeps it (instead of
198 // rewriting schematic Footprint fields onto the generated *-import-fps cache).
199 wxArrayString sourceFpLibs;
200 sourceFpLibs.Add( EASYEDAPRO::ShortenLibName( m_InputFile.GetName() ) );
202
203 nlohmann::json project;
204
205 if( isV3 )
206 {
207 // For v3, build a minimal legacy-format project from raw documents
208 EASYEDAPRO::V3_DOC_PARSER adapter( m_InputFile.GetFullPath() );
209 adapter.Load();
211 }
212 else
213 {
215 }
216
217 std::vector<IMPORT_PROJECT_DESC> toImport = EASYEDAPRO::ProjectToSelectorDialog( project, false, false );
218
219 if( toImport.size() > 1 )
220 toImport = DIALOG_IMPORT_CHOOSE_PROJECT::RunModal( m_frame, toImport );
221
222 if( toImport.size() == 1 )
223 {
224 const IMPORT_PROJECT_DESC& desc = toImport[0];
225 wxString pcbId = desc.PCBId;
226 wxString schId = desc.SchematicId;
227
228 if( pcbId.empty() && project.contains( "pcbs" ) && project.at( "pcbs" ).is_object()
229 && project.at( "pcbs" ).size() == 1 )
230 {
231 pcbId = wxString::FromUTF8( project.at( "pcbs" ).begin().key() );
232 }
233
234 if( schId.empty() && project.contains( "schematics" ) && project.at( "schematics" ).is_object()
235 && project.at( "schematics" ).size() == 1 )
236 {
237 schId = wxString::FromUTF8( project.at( "schematics" ).begin().key() );
238 }
239
240 if( !pcbId.empty() )
241 m_properties["pcb_id"] = pcbId;
242
243 if( !schId.empty() )
244 m_properties["sch_id"] = schId;
245 }
246 else
247 {
248 m_properties["pcb_id"] = "";
249 m_properties["sch_id"] = "";
250 }
251}
252
253
255{
256 // fresh empty target dir, so a stem-derived name is collision-free; reconciler still guards on-disk
257 wxString stem = m_TargetProj.GetName();
258
259 if( stem.IsEmpty() )
260 stem = m_InputFile.GetName();
261
263}
264
265
266wxString IMPORT_PROJ_HELPER::joinSourceLibNicknames( const std::set<wxString>& aPaths ) const
267{
268 wxArrayString nicks;
269
270 for( const wxString& path : aPaths )
271 {
272 wxString nick = wxFileName( path ).GetName();
273
274 if( !nick.IsEmpty() )
275 nicks.Add( nick );
276 }
277
278 return IMPORT_PROJ_PROPS::JoinList( nicks );
279}
280
281
282void IMPORT_PROJ_HELPER::addLocalLibraries( const std::set<wxString>& aNames, FRAME_T aFrameType )
283{
284 KIWAY_PLAYER* frame = m_frame->Kiway().Player( aFrameType, true );
285
286 std::stringstream ss;
287
288 // First line is the source project directory so the handler can distinguish
289 // project-local libraries from external fixed-path references.
290 ss << TO_UTF8( m_InputFile.GetPath() ) << '\n';
291
292 for( const wxString& name : aNames )
293 {
294 wxFileName fname( name );
295 fname.MakeAbsolute( m_InputFile.GetPath() );
296 ss << TO_UTF8( fname.GetFullPath() ) << '\n';
297 }
298
299 std::string packet = ss.str();
300 frame->Kiway().ExpressMail( aFrameType, MAIL_ADD_LOCAL_LIB, packet, m_frame );
301}
302
303
305{
306 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, m_InputFile.GetFullPath(),
307 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
308
309 wxString groupname;
310 long groupid;
311
312 std::set<wxString> sch_file;
313 std::set<wxString> sch_libs;
314 std::set<wxString> pcb_libs;
315
316 wxString pcb_file;
317 int pcb_file_type = PCB_IO_MGR::ALTIUM_DESIGNER;
318
319 auto matchPcbExt = []( const wxString& aExt ) -> int
320 {
321 if( !aExt.CmpNoCase( wxS( "PcbDoc" ) ) )
323
324 if( !aExt.CmpNoCase( wxS( "CSPcbDoc" ) ) )
326
327 if( !aExt.CmpNoCase( wxS( "CMPcbDoc" ) ) )
329
330 if( !aExt.CmpNoCase( wxS( "SWPcbDoc" ) ) )
332
333 return -1;
334 };
335
336 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
337 more = config.GetNextGroup( groupname, groupid ) )
338 {
339 if( !groupname.StartsWith( wxS( "Document" ) ) )
340 continue;
341
342 wxString number = groupname.Mid( 8 );
343 long docNumber;
344
345 if( !number.ToLong( &docNumber ) )
346 continue;
347
348 wxString path = config.Read( groupname + wxS( "/DocumentPath" ), wxEmptyString );
349
350 if( path.empty() )
351 continue;
352
353 wxFileName fname( path, wxPATH_WIN );
354
355 if( !fname.IsAbsolute() )
356 fname.MakeAbsolute( m_InputFile.GetPath() );
357
358 int pcbType = matchPcbExt( fname.GetExt() );
359
360 if( pcbType >= 0 && pcb_file.empty() )
361 {
362 pcb_file = fname.GetFullPath();
363 pcb_file_type = pcbType;
364 }
365
366 if( !fname.GetExt().CmpNoCase( "SCHDOC" ) )
367 sch_file.insert( fname.GetFullPath() );
368
369 if( !fname.GetExt().CmpNoCase( "PCBLIB" ) )
370 pcb_libs.insert( fname.GetFullPath() );
371
372 if( !fname.GetExt().CmpNoCase( "SCHLIB" ) )
373 sch_libs.insert( fname.GetFullPath() );
374 }
375
376 addLocalLibraries( sch_libs, FRAME_SCH );
378
379 // pass source fp libs to both imports so found footprints relink to source, not the cache
381
382 m_properties["project_file"] = m_InputFile.GetFullPath();
383
384 int ii = 0;
385
386 for( auto& path : sch_file )
387 {
388 std::string key = "sch" + std::to_string( ii++ );
389 m_properties[key] = path.ToStdString();
390 }
391
392 if( !sch_file.empty() )
393 doImport( "", FRAME_SCH, SCH_IO_MGR::SCH_ALTIUM );
394
395 if( !pcb_file.empty() )
396 doImport( pcb_file, FRAME_PCB_EDITOR, pcb_file_type );
397}
398
399
401{
402 m_properties.clear();
403
405
406 if( !related.HasPcb() && !related.HasSchematic() )
407 return;
408
409 std::vector<SCOPED_FILE_REMOVER> copiedFiles;
410
411 auto copyAndImport = [&]( const wxString& sourceFile, FRAME_T frameType, int fileType )
412 {
413 if( sourceFile.IsEmpty() )
414 return;
415
416 wxFileName srcFn( sourceFile );
417 wxFileName targetFile( m_TargetProj.GetPath(), srcFn.GetName(), srcFn.GetExt() );
418
419 if( !targetFile.FileExists() )
420 {
421 bool copied = wxCopyFile( srcFn.GetFullPath(), targetFile.GetFullPath(), false );
422
423 if( copied )
424 copiedFiles.emplace_back( targetFile.GetFullPath() );
425 }
426
427 if( targetFile.FileExists() )
428 doImport( targetFile.GetFullPath(), frameType, fileType );
429 };
430
431 copyAndImport( related.schematicFile, FRAME_SCH, SCH_IO_MGR::SCH_PADS );
432 copyAndImport( related.pcbFile, FRAME_PCB_EDITOR, PCB_IO_MGR::PADS );
433}
434
435
437{
438 wxFileName prjFile = m_InputFile;
439
440 if( prjFile.GetExt().CmpNoCase( "prj" ) != 0 )
441 return;
442
443 wxTextFile file;
444
445 if( !file.Open( prjFile.GetFullPath() ) )
446 {
447 wxLogWarning( _( "Could not open gEDA / Lepton EDA project file '%s'." ), prjFile.GetFullPath() );
448 return;
449 }
450
451 std::vector<wxFileName> schFiles;
452 wxString outputName;
453 wxString elementsDir;
454
455 for( size_t i = 0; i < file.GetLineCount(); ++i )
456 {
457 wxString line = file.GetLine( i );
458
459 line.Trim( true );
460 line.Trim( false );
461
462 if( line.IsEmpty() )
463 continue;
464
465 if( line.StartsWith( wxT( "#" ) ) || line.StartsWith( wxT( ";" ) ) )
466 continue;
467
468 int commentPos = line.Find( wxT( "#" ) );
469
470 if( commentPos != wxNOT_FOUND )
471 {
472 line = line.Left( commentPos );
473 line.Trim( true );
474 line.Trim( false );
475 }
476
477 if( line.IsEmpty() )
478 continue;
479
480 wxStringTokenizer tok( line );
481
482 if( !tok.HasMoreTokens() )
483 continue;
484
485 wxString keyword = tok.GetNextToken();
486
487 if( keyword.CmpNoCase( "schematics" ) == 0 )
488 {
489 while( tok.HasMoreTokens() )
490 {
491 wxString schToken = tok.GetNextToken();
492 wxFileName schFile( schToken );
493
494 if( !schFile.IsAbsolute() )
495 schFile.MakeAbsolute( prjFile.GetPath() );
496
497 schFiles.push_back( schFile );
498 }
499 }
500 else if( keyword.CmpNoCase( "output-name" ) == 0 )
501 {
502 wxString rest = line.Mid( keyword.length() );
503 rest.Trim( true );
504 rest.Trim( false );
505 outputName = rest;
506 }
507 else if( keyword.CmpNoCase( "elements-dir" ) == 0 )
508 {
509 wxString rest = line.Mid( keyword.length() );
510 rest.Trim( true );
511 rest.Trim( false );
512 elementsDir = rest;
513 }
514 }
515
516 if( !elementsDir.IsEmpty() )
517 {
518 wxFileName elementsPath( elementsDir );
519
520 if( !elementsPath.IsAbsolute() )
521 elementsPath.MakeAbsolute( prjFile.GetPath() );
522
523 m_properties["elements_dir"] = elementsPath.GetFullPath().ToStdString();
524 }
525
526 if( schFiles.empty() )
527 {
528 wxFileName candidate = prjFile;
529 candidate.SetExt( wxS( "sch" ) );
530
531 if( candidate.FileExists() )
532 schFiles.push_back( candidate );
533 }
534
535 auto promptForMissingFile =
536 [&]( const wxString& aTitle, const wxString& aWildcard, wxFileName& aFile ) -> bool
537 {
538 wxString defaultDir = aFile.GetPath();
539
540 if( defaultDir.IsEmpty() )
541 defaultDir = prjFile.GetPath();
542
543 wxFileDialog dlg( m_frame, aTitle, defaultDir, wxEmptyString, aWildcard,
544 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
545
546 if( dlg.ShowModal() == wxID_OK )
547 {
548 aFile.Assign( dlg.GetPath() );
549 return true;
550 }
551
552 aFile.Clear();
553 return false;
554 };
555
556 std::vector<wxFileName> resolvedSchFiles;
557 const wxString schWildcard = _( "gEDA / Lepton EDA schematic files" ) + wxS( " (*.sch)|*.sch" );
558
559 for( wxFileName schFile : schFiles )
560 {
561 if( !schFile.FileExists() )
562 {
563 if( !promptForMissingFile( _( "Locate gEDA / Lepton EDA Schematic" ), schWildcard, schFile ) )
564 continue;
565 }
566
567 resolvedSchFiles.push_back( schFile );
568 }
569
570 if( schFiles.empty() && resolvedSchFiles.empty() )
571 {
572 wxFileName schFile;
573
574 if( promptForMissingFile( _( "Locate gEDA / Lepton EDA Schematic" ), schWildcard, schFile ) )
575 resolvedSchFiles.push_back( schFile );
576 }
577
578 if( resolvedSchFiles.size() > 1 )
579 {
580 // Pass additional schematic files as a semicolon-delimited property so the
581 // importer can create sub-sheets for each additional page within a single
582 // KiCad project hierarchy.
583 wxString additionalFiles;
584
585 for( size_t i = 1; i < resolvedSchFiles.size(); i++ )
586 {
587 if( !additionalFiles.IsEmpty() )
588 additionalFiles += wxT( ";" );
589
590 additionalFiles += resolvedSchFiles[i].GetFullPath();
591 }
592
593 m_properties["additional_schematics"] = additionalFiles.ToStdString();
594 }
595
596 // Auto-discover subdirectories containing .sym files and pass them as
597 // additional search paths for the importer's symbol resolution.
598 auto discoverSymDirs = [&]( const wxString& aBaseDir, wxString& aSymPaths )
599 {
600 wxDir dir( aBaseDir );
601
602 if( !dir.IsOpened() )
603 return;
604
605 wxString subdir;
606 bool cont = dir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS );
607
608 while( cont )
609 {
610 wxString subdirPath = aBaseDir + wxFileName::GetPathSeparator() + subdir;
611 wxDir childDir( subdirPath );
612
613 if( childDir.IsOpened() && childDir.HasFiles( wxT( "*.sym" ) ) )
614 {
615 if( !aSymPaths.IsEmpty() )
616 aSymPaths += wxT( "\n" );
617
618 aSymPaths += subdirPath;
619 }
620
621 cont = dir.GetNext( &subdir );
622 }
623 };
624
625 wxString symPaths;
626 discoverSymDirs( prjFile.GetPath(), symPaths );
627
628 if( !resolvedSchFiles.empty() )
629 {
630 wxString schDir = resolvedSchFiles[0].GetPath();
631
632 if( schDir != prjFile.GetPath() )
633 discoverSymDirs( schDir, symPaths );
634 }
635
636 if( !symPaths.IsEmpty() )
637 m_properties["sym_search_paths"] = symPaths.ToStdString();
638
639 if( !resolvedSchFiles.empty() )
640 doImport( resolvedSchFiles[0].GetFullPath(), FRAME_SCH, SCH_IO_MGR::SCH_GEDA );
641
642 wxFileName layoutDir( prjFile.GetPath(), wxEmptyString );
643 layoutDir.RemoveLastDir();
644 layoutDir.AppendDir( wxS( "layout" ) );
645
646 wxFileName pcbFile;
647
648 if( !outputName.IsEmpty() )
649 {
650 wxFileName candidate( layoutDir.GetPath(), outputName, wxS( "pcb" ) );
651
652 if( candidate.FileExists() )
653 pcbFile = candidate;
654 }
655
656 if( !pcbFile.FileExists() )
657 {
658 wxFileName candidate( prjFile.GetPath(), prjFile.GetName(), wxS( "pcb" ) );
659
660 if( candidate.FileExists() )
661 pcbFile = candidate;
662 }
663
664 if( !pcbFile.FileExists() )
665 {
666 wxFileName candidate( layoutDir.GetPath(), prjFile.GetName(), wxS( "pcb" ) );
667
668 if( candidate.FileExists() )
669 pcbFile = candidate;
670 }
671
672 const wxString pcbWildcard = _( "gEDA / Lepton EDA PCB files" ) + wxS( " (*.pcb)|*.pcb" );
673
674 if( !pcbFile.FileExists() )
675 promptForMissingFile( _( "Locate gEDA / Lepton EDA PCB" ), pcbWildcard, pcbFile );
676
677 if( pcbFile.FileExists() )
678 doImport( pcbFile.GetFullPath(), FRAME_PCB_EDITOR, PCB_IO_MGR::GEDA_PCB );
679}
680
681
682static wxFileName findSiblingByExt( const wxFileName& aBase, const wxString& aExt )
683{
684 wxDir dir( aBase.GetPath() );
685
686 if( dir.IsOpened() )
687 {
688 wxString found;
689
690 for( bool more = dir.GetFirst( &found, wxEmptyString, wxDIR_FILES ); more;
691 more = dir.GetNext( &found ) )
692 {
693 wxFileName fn( aBase.GetPath(), found );
694
695 if( fn.GetName().CmpNoCase( aBase.GetName() ) == 0 && fn.GetExt().CmpNoCase( aExt ) == 0 )
696 return fn;
697 }
698 }
699
700 return wxFileName();
701}
702
703
705{
707
708 // Import selected OrCAD Capture schematic (.dsn) first
709 ImportIndividualFile( SCHEMATIC_T, SCH_IO_MGR::SCH_ORCAD );
710
711 // Only a successful schematic import replies with a net-name map, even an empty one.
713 return;
714
715 // OrCAD designs pair w/ Cadence Allegro board; offer import, defaulting to
716 // sibling board (matched case-insensitively) next to schematic
717 wxFileName brdFile = findSiblingByExt( m_InputFile, wxS( "brd" ) );
718 bool haveSibling = brdFile.FileExists();
719
720 wxString msg = haveSibling
721 ? wxString::Format( _( "Import the associated Allegro board '%s'?" ),
722 brdFile.GetFullName() )
723 : _( "Import an associated Allegro board file?" );
724
725 KIDIALOG dlg( m_frame, msg, _( "Import Allegro Board" ), wxOK | wxCANCEL | wxICON_QUESTION );
726 dlg.SetOKCancelLabels( _( "OK" ), _( "Skip" ) );
727
728 if( dlg.ShowModal() != wxID_OK )
729 return;
730
731 if( !haveSibling )
732 {
733 wxFileDialog fileDlg( m_frame, _( "Locate Allegro Board" ), m_InputFile.GetPath(),
734 wxEmptyString, FILEEXT::AllegroPcbFilesWildcard(),
735 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
736
737 if( fileDlg.ShowModal() != wxID_OK )
738 return;
739
740 brdFile.Assign( fileDlg.GetPath() );
741 }
742
743 // ExpressMail dispatches synchronously and loaded target project sets generated
744 // board path, so import source board directly, no staging copy
745 if( brdFile.FileExists() )
746 doImport( brdFile.GetFullPath(), FRAME_PCB_EDITOR, PCB_IO_MGR::ALLEGRO );
747}
748
749
751{
752 wxFileName schFile;
753 wxFileName pcbFile;
754 wxString projectPcb;
755
756 if( m_InputFile.GetExt().CmpNoCase( wxS( "prj" ) ) == 0 )
757 {
758 schFile = m_InputFile;
759
760 try
761 {
762 std::unique_ptr<EASYPC::PROJECT_DOCUMENT> doc = EASYPC::LoadProject( m_InputFile.GetFullPath() );
763 const EASYPC::SOURCE_PROJECT& project = *doc->Project;
764
765 if( !project.Boards.empty() )
766 {
767 projectPcb = project.Boards.front()->Name;
768 pcbFile.Assign( EASYPC::ResolveProjectItem( m_InputFile.GetFullPath(), projectPcb ) );
769 }
770 }
771 catch( const IO_ERROR& e )
772 {
773 DisplayErrorMessage( m_frame, _( "Cannot read the Easy-PC project." ), e.What() );
774 return;
775 }
776 }
777 else if( m_InputFile.GetExt().CmpNoCase( wxS( "pcb" ) ) == 0 )
778 {
779 pcbFile = m_InputFile;
780 schFile = findSiblingByExt( m_InputFile, wxS( "sch" ) );
781 }
782 else
783 {
784 schFile = m_InputFile;
785 pcbFile = findSiblingByExt( m_InputFile, wxS( "pcb" ) );
786 }
787
788 // Sources are imported in place: a project's sheets are found relative to the .prj
789 if( schFile.IsOk() && schFile.FileExists() )
790 doImport( schFile.GetFullPath(), FRAME_SCH, SCH_IO_MGR::SCH_EASYPC );
791
792 if( pcbFile.IsOk() && pcbFile.FileExists() )
793 {
794 doImport( pcbFile.GetFullPath(), FRAME_PCB_EDITOR, PCB_IO_MGR::EASYPC );
795 }
796 else if( !projectPcb.IsEmpty() )
797 {
798 DisplayErrorMessage( m_frame, wxString::Format( _( "The board '%s' named by the Easy-PC project was not "
799 "found." ),
800 projectPcb ) );
801 }
802}
803
804
805void IMPORT_PROJ_HELPER::ImportFiles( int aImportedSchFileType, int aImportedPcbFileType )
806{
807 m_properties.clear();
808
809 // both imports must agree on the cache nickname up front
811
812 int importedSchFileType = aImportedSchFileType;
813 int importedPcbFileType = aImportedPcbFileType;
814
815 if( importedSchFileType == SCH_IO_MGR::SCH_EASYEDAPRO || importedSchFileType == SCH_IO_MGR::SCH_EASYEDAPRO_V3
816 || importedPcbFileType == PCB_IO_MGR::EASYEDAPRO || importedPcbFileType == PCB_IO_MGR::EASYEDAPRO_V3 )
817 {
818 bool isV3 = EASYEDAPRO::V3_DOC_PARSER::IsV3Archive( m_InputFile.GetFullPath() );
819
820 importedSchFileType = isV3 ? SCH_IO_MGR::SCH_EASYEDAPRO_V3 : SCH_IO_MGR::SCH_EASYEDAPRO;
821 importedPcbFileType = isV3 ? PCB_IO_MGR::EASYEDAPRO_V3 : PCB_IO_MGR::EASYEDAPRO;
822
823 EasyEDAProProjectHandler( importedSchFileType, importedPcbFileType );
824 }
825 else if( importedSchFileType == SCH_IO_MGR::SCH_ALTIUM || importedPcbFileType == PCB_IO_MGR::ALTIUM_DESIGNER )
826 {
828 return;
829 }
830 else if( aImportedSchFileType == SCH_IO_MGR::SCH_ORCAD )
831 {
833 return;
834 }
835 else if( aImportedSchFileType == SCH_IO_MGR::SCH_EASYPC )
836 {
838 return;
839 }
840 else if( aImportedSchFileType == SCH_IO_MGR::SCH_GEDA )
841 {
842 if( m_InputFile.GetExt().CmpNoCase( "prj" ) == 0 )
843 {
845 }
846 else if( m_InputFile.GetExt().CmpNoCase( "pcb" ) == 0 )
847 {
848 ImportIndividualFile( PCB_T, aImportedPcbFileType );
849 }
850 else
851 {
852 // When importing a bare .sch file, pass the original source directory
853 // so the importer can find symbols in subdirectories relative to the
854 // gEDA / Lepton EDA schematic, even though the file may be copied to a new location
855 // for the KiCad project.
856 wxString sourceDir = m_InputFile.GetPath();
857 wxString symPaths = sourceDir;
858
859 auto discoverSymDirs = [&]( const wxString& aBaseDir )
860 {
861 wxDir dir( aBaseDir );
862
863 if( !dir.IsOpened() )
864 return;
865
866 wxString subdir;
867 bool cont = dir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS );
868
869 while( cont )
870 {
871 wxString subdirPath =
872 aBaseDir + wxFileName::GetPathSeparator() + subdir;
873 wxDir childDir( subdirPath );
874
875 if( childDir.IsOpened() && childDir.HasFiles( wxT( "*.sym" ) ) )
876 symPaths += wxT( "\n" ) + subdirPath;
877
878 cont = dir.GetNext( &subdir );
879 }
880 };
881
882 discoverSymDirs( sourceDir );
883 m_properties["sym_search_paths"] = symPaths.ToStdString();
884
885 doImport( m_InputFile.GetFullPath(), FRAME_SCH, SCH_IO_MGR::SCH_GEDA );
886 ImportIndividualFile( PCB_T, aImportedPcbFileType );
887 }
888
889 return;
890 }
891
892 ImportIndividualFile( SCHEMATIC_T, importedSchFileType );
893 ImportIndividualFile( PCB_T, importedPcbFileType );
894}
const char * name
static std::vector< IMPORT_PROJECT_DESC > RunModal(wxWindow *aParent, const std::vector< IMPORT_PROJECT_DESC > &aProjectDesc)
Create and show a dialog (modal) and returns the data from it after completion.
Parses EasyEDA Pro v3 .epro2 archives.
void Load()
Load all v3 docs from the archive.
static bool IsV3Archive(const wxString &aFileName)
Return true if aFileName looks like an EasyEDA Pro v3 archive.
void OrcadProjectHandler()
Imports the OrCAD schematic, then offers to import an associated Allegro board.
wxFileName m_TargetProj
Definition import_proj.h:63
wxString joinSourceLibNicknames(const std::set< wxString > &aPaths) const
Join the basenames of the given library paths into a source-library property value.
IMPORT_PROJ_HELPER(KICAD_MANAGER_FRAME *aframe, const std::vector< wxString > &aSchFileExtensions, const std::vector< wxString > &aPcbFileExtensions)
std::map< std::string, UTF8 > m_properties
Definition import_proj.h:68
void FindEmptyTargetDir()
Appends a new directory with the name of the project file Keep iterating until an empty directory is ...
void EasyEDAProProjectHandler(int aImportedSchFileType, int aImportedPcbFileType)
std::vector< wxString > m_pcbExtenstions
Definition import_proj.h:74
void setImportCacheNickname()
Pre-commit one collision-free generated footprint-cache nickname into m_properties so the schematic a...
KICAD_MANAGER_FRAME * m_frame
Definition import_proj.h:66
void ImportFiles(int aImportedSchFileType, int aImportedPcbFileType)
Converts imported files to kicad type files.
void EasyPcProjectHandler()
Imports an Easy-PC / DesignSpark project, or a schematic and board pair.
void addLocalLibraries(const std::set< wxString > &aLibName, FRAME_T aFrameType)
void ImportIndividualFile(KICAD_T aKicad_T, int aImportedFileType)
wxFileName m_InputFile
Definition import_proj.h:62
std::vector< wxString > m_schExtenstions
Definition import_proj.h:73
void ImportPadsFiles()
Converts PADS ASCII schematic and PCB files to KiCad type files.
void doImport(const wxString &aFile, FRAME_T aFrameType, int aImportedFileType)
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
The main KiCad project manager frame.
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:38
bool SetOKCancelLabels(const ButtonLabel &ok, const ButtonLabel &cancel) override
Definition kidialog.h:48
int ShowModal() override
Definition kidialog.cpp:89
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr, bool aFromOtherThread=false)
Send aPayload to aDestination from aSource.
Definition kiway.cpp:486
@ EASYPC
Easy-PC / DesignSpark PCB.
Definition pcb_io_mgr.h:76
@ GEDA_PCB
Geda PCB file formats.
Definition pcb_io_mgr.h:66
@ ALTIUM_DESIGNER
Definition pcb_io_mgr.h:59
@ ALTIUM_CIRCUIT_MAKER
Definition pcb_io_mgr.h:57
@ ALTIUM_CIRCUIT_STUDIO
Definition pcb_io_mgr.h:58
SCOPED_FILE_REMOVER(const wxString &aFile)
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
The project file (.prj), a bare archive stream.
Loading designs, library items and projects.
#define _(s)
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition frame_type.h:29
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_SCH
Definition frame_type.h:30
static wxString AllegroPcbFilesWildcard()
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
static wxFileName findSiblingByExt(const wxFileName &aBase, const wxString &aExt)
This file contains miscellaneous commonly used macros and functions.
@ MAIL_IMPORT_FILE
Definition mail_type.h:44
@ MAIL_ADD_LOCAL_LIB
Definition mail_type.h:50
nlohmann::json BuildV3ProjectIndexFromRawDocs(const V3_DOC_PARSER &aParser, bool aIncludeLibraryMetadata=true)
Build a minimal legacy-style project index from parsed v3 raw documents.
std::vector< IMPORT_PROJECT_DESC > ProjectToSelectorDialog(const nlohmann::json &aProject, bool aPcbOnly=false, bool aSchOnly=false)
nlohmann::json ReadProjectOrDeviceFile(const wxString &aZipFileName)
wxString ShortenLibName(wxString aProjectName)
wxString ResolveProjectItem(const wxString &aProjectPath, const wxString &aItemName)
The file a project item names, as Easy-PC opens it: the Windows path relative to the project's direct...
std::unique_ptr< PROJECT_DOCUMENT > LoadProject(const wxString &aPath)
constexpr char SOURCE_FP_LIBS[]
constexpr char FP_CACHE_NICKNAME[]
wxString JoinList(const wxArrayString &aItems)
Encode library nicknames into a single list-property value.
wxString MakeCacheNickname(const wxString &aStem)
Derive the generated footprint-cache nickname from a project or file stem.
std::optional< std::map< wxString, wxString > > SplitNetNameMap(const wxString &aValue)
Decode a net-name map property value, or nothing when the value is not one.
constexpr char NET_NAME_MAP[]
RELATED_FILES FindRelatedPadsFiles(const wxString &aFilePath)
Find the related PADS project file (schematic for a PCB source, or vice versa) in the same directory,...
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
Describes how non-KiCad boards and schematics should be imported as KiCad projects.
bool copied
std::string path
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_T
Definition typeinfo.h:74
@ SCHEMATIC_T
Definition typeinfo.h:200
Definition of file extensions used in Kicad.