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"
22
23#include <list>
24
26#include <confirm.h>
27#include <kidialog.h>
29#include <macros.h>
30#include <string_utils.h>
31#include <richio.h>
32
33#include <wx/fileconf.h>
34#include <wx/filedlg.h>
35#include <wx/msgdlg.h>
36#include <wx/tokenzr.h>
37#include <wx/dir.h>
38#include <wx/textfile.h>
39#include <wx/wfstream.h>
40
41#include <kiway.h>
42#include <kiway_player.h>
43#include <kicad_manager_frame.h>
44#include <pcb_edit_frame.h>
45#include <sch_edit_frame.h>
46
47#include <sch_io/sch_io_mgr.h>
48#include <pcb_io/pcb_io_mgr.h>
49#include <project_sch.h>
50#include <project_pcb.h>
51
56#include <io/pads/pads_common.h>
60
61#include <wx/log.h>
62
63
65 const std::vector<wxString>& aSchFileExtensions,
66 const std::vector<wxString>& aPcbFileExtensions ) :
67 m_frame( aFrame ),
68 m_schExtenstions( aSchFileExtensions ), m_pcbExtenstions( aPcbFileExtensions )
69{
70}
71
72
74{
75 // Append a new directory with the same name of the project file
76 // Keep iterating until we find an empty directory
77 wxString newDir = m_TargetProj.GetName();
78 int attempt = 0;
79
80 m_TargetProj.AppendDir( newDir );
81
82 while( m_TargetProj.DirExists() )
83 {
84 m_TargetProj.RemoveLastDir();
85 wxString suffix = wxString::Format( "_%d", ++attempt );
86 m_TargetProj.AppendDir( newDir + suffix );
87 }
88}
89
90
92{
93public:
94 SCOPED_FILE_REMOVER( const wxString& aFile ) :
95 m_file( aFile )
96 {
97 }
98
100 {
101 wxRemoveFile( m_file );
102 }
103
104 // No copy operations allowed or the destructor might remove files multiple times.
107
108private:
109 wxString m_file;
110};
111
112
113void IMPORT_PROJ_HELPER::ImportIndividualFile( KICAD_T aFT, int aImportedFileType )
114{
115 FRAME_T frame_type;
116 wxString appImportFile;
117 std::vector<wxString> neededExts;
118
119 switch( aFT )
120 {
121 case SCHEMATIC_T:
122 neededExts = m_schExtenstions;
123 frame_type = FRAME_SCH;
124 break;
125
126 case PCB_T:
127 neededExts = m_pcbExtenstions;
128 frame_type = FRAME_PCB_EDITOR;
129 break;
130
131 default: return;
132 }
133
134 std::list<SCOPED_FILE_REMOVER> copiedFiles;
135
136 for( wxString ext : neededExts )
137 {
138 if( ext == wxS( "INPUT" ) )
139 ext = m_InputFile.GetExt();
140
141 wxFileName candidate = m_InputFile;
142
143 // Selected file often the one wanted (e.g. schematic in schematic-only
144 // import); keep on-disk name so uppercase ext still resolves on
145 // case-sensitive fs. Rewrite ext only when seeking differently-typed sibling
146 if( m_InputFile.GetExt().CmpNoCase( ext ) != 0 )
147 candidate.SetExt( ext );
148
149 if( !candidate.FileExists() )
150 continue;
151
152 wxFileName targetFile( m_TargetProj.GetPath(), candidate.GetName(), candidate.GetExt() );
153
154 if( !targetFile.FileExists() )
155 {
156 bool copied = wxCopyFile( candidate.GetFullPath(), targetFile.GetFullPath(), false );
157
158 if( copied )
159 {
160 // Will be auto-removed
161 copiedFiles.emplace_back( targetFile.GetFullPath() );
162 }
163 }
164
165 // Pick the first file to pass to application
166 if( appImportFile.empty() && targetFile.FileExists() )
167 appImportFile = targetFile.GetFullPath();
168 }
169
170 if( appImportFile.empty() )
171 return;
172
173 doImport( appImportFile, frame_type, aImportedFileType );
174}
175
176
177void IMPORT_PROJ_HELPER::doImport( const wxString& aFile, FRAME_T aFrameType, int aImportedFileType )
178{
179 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( aFrameType, true ) )
180 {
181 std::stringstream ss;
182 ss << aImportedFileType << '\n' << TO_UTF8( aFile );
183
184 for( const auto& [key, value] : m_properties )
185 ss << '\n' << key << '\n' << value.wx_str();
186
187 std::string packet = ss.str();
188 frame->Kiway().ExpressMail( aFrameType, MAIL_IMPORT_FILE, packet, m_frame );
189
190 // Editors reply with an empty packet when the import fails
191 if( !packet.empty() )
192 m_importedFrames.insert( aFrameType );
193
194 if( aFrameType == FRAME_SCH && aImportedFileType == SCH_IO_MGR::SCH_ORCAD
197
198 if( !frame->IsShownOnScreen() )
199 frame->Show( true );
200
201 // On Windows, Raise() does not bring the window on screen, when iconized
202 if( frame->IsIconized() )
203 frame->Iconize( false );
204
205 frame->Raise();
206 }
207}
208
209
211{
212 for( FRAME_T frameType : m_importedFrames )
213 {
214 std::string payload;
215 MAIL_T mail = frameType == FRAME_SCH ? MAIL_SCH_SAVE : MAIL_PCB_SAVE;
216
217 m_frame->Kiway().ExpressMail( frameType, mail, payload, m_frame );
218 }
219}
220
221
222void IMPORT_PROJ_HELPER::EasyEDAProProjectHandler( int aImportedSchFileType, int aImportedPcbFileType )
223{
224 const bool isV3 =
225 aImportedSchFileType == SCH_IO_MGR::SCH_EASYEDAPRO_V3 || aImportedPcbFileType == PCB_IO_MGR::EASYEDAPRO_V3;
226
227 // Sch/PCB importers both assign FPIDs under ShortenLibName( archive ); advertise that
228 // nickname as the source footprint lib so post-import reconciler keeps it (instead of
229 // rewriting schematic Footprint fields onto the generated *-import-fps cache).
230 wxArrayString sourceFpLibs;
231 sourceFpLibs.Add( EASYEDAPRO::ShortenLibName( m_InputFile.GetName() ) );
233
234 nlohmann::json project;
235
236 if( isV3 )
237 {
238 // For v3, build a minimal legacy-format project from raw documents
239 EASYEDAPRO::V3_DOC_PARSER adapter( m_InputFile.GetFullPath() );
240 adapter.Load();
242 }
243 else
244 {
246 }
247
248 std::vector<IMPORT_PROJECT_DESC> toImport = EASYEDAPRO::ProjectToSelectorDialog( project, false, false );
249
250 if( toImport.size() > 1 )
251 toImport = DIALOG_IMPORT_CHOOSE_PROJECT::RunModal( m_frame, toImport );
252
253 if( toImport.size() == 1 )
254 {
255 const IMPORT_PROJECT_DESC& desc = toImport[0];
256 wxString pcbId = desc.PCBId;
257 wxString schId = desc.SchematicId;
258
259 if( pcbId.empty() && project.contains( "pcbs" ) && project.at( "pcbs" ).is_object()
260 && project.at( "pcbs" ).size() == 1 )
261 {
262 pcbId = wxString::FromUTF8( project.at( "pcbs" ).begin().key() );
263 }
264
265 if( schId.empty() && project.contains( "schematics" ) && project.at( "schematics" ).is_object()
266 && project.at( "schematics" ).size() == 1 )
267 {
268 schId = wxString::FromUTF8( project.at( "schematics" ).begin().key() );
269 }
270
271 if( !pcbId.empty() )
272 m_properties["pcb_id"] = pcbId;
273
274 if( !schId.empty() )
275 m_properties["sch_id"] = schId;
276 }
277 else
278 {
279 m_properties["pcb_id"] = "";
280 m_properties["sch_id"] = "";
281 }
282}
283
284
286{
287 // fresh empty target dir, so a stem-derived name is collision-free; reconciler still guards on-disk
288 wxString stem = m_TargetProj.GetName();
289
290 if( stem.IsEmpty() )
291 stem = m_InputFile.GetName();
292
294}
295
296
297wxString IMPORT_PROJ_HELPER::joinSourceLibNicknames( const std::set<wxString>& aPaths ) const
298{
299 wxArrayString nicks;
300
301 for( const wxString& path : aPaths )
302 {
303 wxString nick = wxFileName( path ).GetName();
304
305 if( !nick.IsEmpty() )
306 nicks.Add( nick );
307 }
308
309 return IMPORT_PROJ_PROPS::JoinList( nicks );
310}
311
312
313void IMPORT_PROJ_HELPER::addLocalLibraries( const std::set<wxString>& aNames, FRAME_T aFrameType )
314{
315 KIWAY_PLAYER* frame = m_frame->Kiway().Player( aFrameType, true );
316
317 std::stringstream ss;
318
319 // First line is the source project directory so the handler can distinguish
320 // project-local libraries from external fixed-path references.
321 ss << TO_UTF8( m_InputFile.GetPath() ) << '\n';
322
323 for( const wxString& name : aNames )
324 {
325 wxFileName fname( name );
326 fname.MakeAbsolute( m_InputFile.GetPath() );
327 ss << TO_UTF8( fname.GetFullPath() ) << '\n';
328 }
329
330 std::string packet = ss.str();
331 frame->Kiway().ExpressMail( aFrameType, MAIL_ADD_LOCAL_LIB, packet, m_frame );
332}
333
334
336{
337 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, m_InputFile.GetFullPath(),
338 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
339
340 wxString groupname;
341 long groupid;
342
343 std::set<wxString> sch_file;
344 std::set<wxString> sch_libs;
345 std::set<wxString> pcb_libs;
346
347 wxString pcb_file;
348 int pcb_file_type = PCB_IO_MGR::ALTIUM_DESIGNER;
349
350 auto matchPcbExt = []( const wxString& aExt ) -> int
351 {
352 if( !aExt.CmpNoCase( wxS( "PcbDoc" ) ) )
354
355 if( !aExt.CmpNoCase( wxS( "CSPcbDoc" ) ) )
357
358 if( !aExt.CmpNoCase( wxS( "CMPcbDoc" ) ) )
360
361 if( !aExt.CmpNoCase( wxS( "SWPcbDoc" ) ) )
363
364 return -1;
365 };
366
367 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
368 more = config.GetNextGroup( groupname, groupid ) )
369 {
370 if( !groupname.StartsWith( wxS( "Document" ) ) )
371 continue;
372
373 wxString number = groupname.Mid( 8 );
374 long docNumber;
375
376 if( !number.ToLong( &docNumber ) )
377 continue;
378
379 wxString path = config.Read( groupname + wxS( "/DocumentPath" ), wxEmptyString );
380
381 if( path.empty() )
382 continue;
383
384 wxFileName fname( path, wxPATH_WIN );
385
386 if( !fname.IsAbsolute() )
387 fname.MakeAbsolute( m_InputFile.GetPath() );
388
389 int pcbType = matchPcbExt( fname.GetExt() );
390
391 if( pcbType >= 0 && pcb_file.empty() )
392 {
393 pcb_file = fname.GetFullPath();
394 pcb_file_type = pcbType;
395 }
396
397 if( !fname.GetExt().CmpNoCase( "SCHDOC" ) )
398 sch_file.insert( fname.GetFullPath() );
399
400 if( !fname.GetExt().CmpNoCase( "PCBLIB" ) )
401 pcb_libs.insert( fname.GetFullPath() );
402
403 if( !fname.GetExt().CmpNoCase( "SCHLIB" ) )
404 sch_libs.insert( fname.GetFullPath() );
405 }
406
407 addLocalLibraries( sch_libs, FRAME_SCH );
409
410 // pass source fp libs to both imports so found footprints relink to source, not the cache
412
413 m_properties["project_file"] = m_InputFile.GetFullPath();
414
415 int ii = 0;
416
417 for( auto& path : sch_file )
418 {
419 std::string key = "sch" + std::to_string( ii++ );
420 m_properties[key] = path.ToStdString();
421 }
422
423 if( !sch_file.empty() )
424 doImport( "", FRAME_SCH, SCH_IO_MGR::SCH_ALTIUM );
425
426 if( !pcb_file.empty() )
427 doImport( pcb_file, FRAME_PCB_EDITOR, pcb_file_type );
428}
429
430
432{
433 m_properties.clear();
434
436
437 if( !related.HasPcb() && !related.HasSchematic() )
438 return;
439
440 std::list<SCOPED_FILE_REMOVER> copiedFiles;
441
442 auto copyAndImport = [&]( const wxString& sourceFile, FRAME_T frameType, int fileType )
443 {
444 if( sourceFile.IsEmpty() )
445 return;
446
447 wxFileName srcFn( sourceFile );
448 wxFileName targetFile( m_TargetProj.GetPath(), srcFn.GetName(), srcFn.GetExt() );
449
450 if( !targetFile.FileExists() )
451 {
452 bool copied = wxCopyFile( srcFn.GetFullPath(), targetFile.GetFullPath(), false );
453
454 if( copied )
455 copiedFiles.emplace_back( targetFile.GetFullPath() );
456 }
457
458 if( targetFile.FileExists() )
459 doImport( targetFile.GetFullPath(), frameType, fileType );
460 };
461
462 copyAndImport( related.schematicFile, FRAME_SCH, SCH_IO_MGR::SCH_PADS );
463 copyAndImport( related.pcbFile, FRAME_PCB_EDITOR, PCB_IO_MGR::PADS );
464}
465
466
468{
469 wxFileName prjFile = m_InputFile;
470
471 if( prjFile.GetExt().CmpNoCase( "prj" ) != 0 )
472 return;
473
474 wxTextFile file;
475
476 if( !file.Open( prjFile.GetFullPath() ) )
477 {
478 wxLogWarning( _( "Could not open gEDA / Lepton EDA project file '%s'." ), prjFile.GetFullPath() );
479 return;
480 }
481
482 std::vector<wxFileName> schFiles;
483 wxString outputName;
484 wxString elementsDir;
485
486 for( size_t i = 0; i < file.GetLineCount(); ++i )
487 {
488 wxString line = file.GetLine( i );
489
490 line.Trim( true );
491 line.Trim( false );
492
493 if( line.IsEmpty() )
494 continue;
495
496 if( line.StartsWith( wxT( "#" ) ) || line.StartsWith( wxT( ";" ) ) )
497 continue;
498
499 int commentPos = line.Find( wxT( "#" ) );
500
501 if( commentPos != wxNOT_FOUND )
502 {
503 line = line.Left( commentPos );
504 line.Trim( true );
505 line.Trim( false );
506 }
507
508 if( line.IsEmpty() )
509 continue;
510
511 wxStringTokenizer tok( line );
512
513 if( !tok.HasMoreTokens() )
514 continue;
515
516 wxString keyword = tok.GetNextToken();
517
518 if( keyword.CmpNoCase( "schematics" ) == 0 )
519 {
520 while( tok.HasMoreTokens() )
521 {
522 wxString schToken = tok.GetNextToken();
523 wxFileName schFile( schToken );
524
525 if( !schFile.IsAbsolute() )
526 schFile.MakeAbsolute( prjFile.GetPath() );
527
528 schFiles.push_back( schFile );
529 }
530 }
531 else if( keyword.CmpNoCase( "output-name" ) == 0 )
532 {
533 wxString rest = line.Mid( keyword.length() );
534 rest.Trim( true );
535 rest.Trim( false );
536 outputName = rest;
537 }
538 else if( keyword.CmpNoCase( "elements-dir" ) == 0 )
539 {
540 wxString rest = line.Mid( keyword.length() );
541 rest.Trim( true );
542 rest.Trim( false );
543 elementsDir = rest;
544 }
545 }
546
547 if( !elementsDir.IsEmpty() )
548 {
549 wxFileName elementsPath( elementsDir );
550
551 if( !elementsPath.IsAbsolute() )
552 elementsPath.MakeAbsolute( prjFile.GetPath() );
553
554 m_properties["elements_dir"] = elementsPath.GetFullPath().ToStdString();
555 }
556
557 if( schFiles.empty() )
558 {
559 wxFileName candidate = prjFile;
560 candidate.SetExt( wxS( "sch" ) );
561
562 if( candidate.FileExists() )
563 schFiles.push_back( candidate );
564 }
565
566 auto promptForMissingFile =
567 [&]( const wxString& aTitle, const wxString& aWildcard, wxFileName& aFile ) -> bool
568 {
569 wxString defaultDir = aFile.GetPath();
570
571 if( defaultDir.IsEmpty() )
572 defaultDir = prjFile.GetPath();
573
574 wxFileDialog dlg( m_frame, aTitle, defaultDir, wxEmptyString, aWildcard,
575 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
576
577 if( dlg.ShowModal() == wxID_OK )
578 {
579 aFile.Assign( dlg.GetPath() );
580 return true;
581 }
582
583 aFile.Clear();
584 return false;
585 };
586
587 std::vector<wxFileName> resolvedSchFiles;
588 const wxString schWildcard = _( "gEDA / Lepton EDA schematic files" ) + wxS( " (*.sch)|*.sch" );
589
590 for( wxFileName schFile : schFiles )
591 {
592 if( !schFile.FileExists() )
593 {
594 if( !promptForMissingFile( _( "Locate gEDA / Lepton EDA Schematic" ), schWildcard, schFile ) )
595 continue;
596 }
597
598 resolvedSchFiles.push_back( schFile );
599 }
600
601 if( schFiles.empty() && resolvedSchFiles.empty() )
602 {
603 wxFileName schFile;
604
605 if( promptForMissingFile( _( "Locate gEDA / Lepton EDA Schematic" ), schWildcard, schFile ) )
606 resolvedSchFiles.push_back( schFile );
607 }
608
609 if( resolvedSchFiles.size() > 1 )
610 {
611 // Pass additional schematic files as a semicolon-delimited property so the
612 // importer can create sub-sheets for each additional page within a single
613 // KiCad project hierarchy.
614 wxString additionalFiles;
615
616 for( size_t i = 1; i < resolvedSchFiles.size(); i++ )
617 {
618 if( !additionalFiles.IsEmpty() )
619 additionalFiles += wxT( ";" );
620
621 additionalFiles += resolvedSchFiles[i].GetFullPath();
622 }
623
624 m_properties["additional_schematics"] = additionalFiles.ToStdString();
625 }
626
627 // Auto-discover subdirectories containing .sym files and pass them as
628 // additional search paths for the importer's symbol resolution.
629 auto discoverSymDirs = [&]( const wxString& aBaseDir, wxString& aSymPaths )
630 {
631 wxDir dir( aBaseDir );
632
633 if( !dir.IsOpened() )
634 return;
635
636 wxString subdir;
637 bool cont = dir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS );
638
639 while( cont )
640 {
641 wxString subdirPath = aBaseDir + wxFileName::GetPathSeparator() + subdir;
642 wxDir childDir( subdirPath );
643
644 if( childDir.IsOpened() && childDir.HasFiles( wxT( "*.sym" ) ) )
645 {
646 if( !aSymPaths.IsEmpty() )
647 aSymPaths += wxT( "\n" );
648
649 aSymPaths += subdirPath;
650 }
651
652 cont = dir.GetNext( &subdir );
653 }
654 };
655
656 wxString symPaths;
657 discoverSymDirs( prjFile.GetPath(), symPaths );
658
659 if( !resolvedSchFiles.empty() )
660 {
661 wxString schDir = resolvedSchFiles[0].GetPath();
662
663 if( schDir != prjFile.GetPath() )
664 discoverSymDirs( schDir, symPaths );
665 }
666
667 if( !symPaths.IsEmpty() )
668 m_properties["sym_search_paths"] = symPaths.ToStdString();
669
670 if( !resolvedSchFiles.empty() )
671 doImport( resolvedSchFiles[0].GetFullPath(), FRAME_SCH, SCH_IO_MGR::SCH_GEDA );
672
673 wxFileName layoutDir( prjFile.GetPath(), wxEmptyString );
674 layoutDir.RemoveLastDir();
675 layoutDir.AppendDir( wxS( "layout" ) );
676
677 wxFileName pcbFile;
678
679 if( !outputName.IsEmpty() )
680 {
681 wxFileName candidate( layoutDir.GetPath(), outputName, wxS( "pcb" ) );
682
683 if( candidate.FileExists() )
684 pcbFile = candidate;
685 }
686
687 if( !pcbFile.FileExists() )
688 {
689 wxFileName candidate( prjFile.GetPath(), prjFile.GetName(), wxS( "pcb" ) );
690
691 if( candidate.FileExists() )
692 pcbFile = candidate;
693 }
694
695 if( !pcbFile.FileExists() )
696 {
697 wxFileName candidate( layoutDir.GetPath(), prjFile.GetName(), wxS( "pcb" ) );
698
699 if( candidate.FileExists() )
700 pcbFile = candidate;
701 }
702
703 const wxString pcbWildcard = _( "gEDA / Lepton EDA PCB files" ) + wxS( " (*.pcb)|*.pcb" );
704
705 if( !pcbFile.FileExists() )
706 promptForMissingFile( _( "Locate gEDA / Lepton EDA PCB" ), pcbWildcard, pcbFile );
707
708 if( pcbFile.FileExists() )
709 doImport( pcbFile.GetFullPath(), FRAME_PCB_EDITOR, PCB_IO_MGR::GEDA_PCB );
710}
711
712
713static wxFileName findSiblingByExt( const wxFileName& aBase, const wxString& aExt )
714{
715 wxDir dir( aBase.GetPath() );
716
717 if( dir.IsOpened() )
718 {
719 wxString found;
720
721 for( bool more = dir.GetFirst( &found, wxEmptyString, wxDIR_FILES ); more;
722 more = dir.GetNext( &found ) )
723 {
724 wxFileName fn( aBase.GetPath(), found );
725
726 if( fn.GetName().CmpNoCase( aBase.GetName() ) == 0 && fn.GetExt().CmpNoCase( aExt ) == 0 )
727 return fn;
728 }
729 }
730
731 return wxFileName();
732}
733
734
736{
738
739 // Import selected OrCAD Capture schematic (.dsn) first
740 ImportIndividualFile( SCHEMATIC_T, SCH_IO_MGR::SCH_ORCAD );
741
742 // Only a successful schematic import replies with a net-name map, even an empty one.
744 return;
745
746 // OrCAD designs pair w/ Cadence Allegro board; offer import, defaulting to
747 // sibling board (matched case-insensitively) next to schematic
748 wxFileName brdFile = findSiblingByExt( m_InputFile, wxS( "brd" ) );
749 bool haveSibling = brdFile.FileExists();
750
751 wxString msg = haveSibling
752 ? wxString::Format( _( "Import the associated Allegro board '%s'?" ),
753 brdFile.GetFullName() )
754 : _( "Import an associated Allegro board file?" );
755
756 KIDIALOG dlg( m_frame, msg, _( "Import Allegro Board" ), wxOK | wxCANCEL | wxICON_QUESTION );
757 dlg.SetOKCancelLabels( _( "OK" ), _( "Skip" ) );
758
759 if( dlg.ShowModal() != wxID_OK )
760 return;
761
762 if( !haveSibling )
763 {
764 wxFileDialog fileDlg( m_frame, _( "Locate Allegro Board" ), m_InputFile.GetPath(),
765 wxEmptyString, FILEEXT::AllegroPcbFilesWildcard(),
766 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
767
768 if( fileDlg.ShowModal() != wxID_OK )
769 return;
770
771 brdFile.Assign( fileDlg.GetPath() );
772 }
773
774 // ExpressMail dispatches synchronously and loaded target project sets generated
775 // board path, so import source board directly, no staging copy
776 if( brdFile.FileExists() )
777 doImport( brdFile.GetFullPath(), FRAME_PCB_EDITOR, PCB_IO_MGR::ALLEGRO );
778}
779
780
782{
783 wxFileName schFile;
784 wxFileName pcbFile;
785 wxString projectPcb;
786
787 if( m_InputFile.GetExt().CmpNoCase( wxS( "prj" ) ) == 0 )
788 {
789 schFile = m_InputFile;
790
791 try
792 {
793 std::unique_ptr<EASYPC::PROJECT_DOCUMENT> doc = EASYPC::LoadProject( m_InputFile.GetFullPath() );
794 const EASYPC::SOURCE_PROJECT& project = *doc->Project;
795
796 if( !project.Boards.empty() )
797 {
798 projectPcb = project.Boards.front()->Name;
799 pcbFile.Assign( EASYPC::ResolveProjectItem( m_InputFile.GetFullPath(), projectPcb ) );
800 }
801 }
802 catch( const IO_ERROR& e )
803 {
804 DisplayErrorMessage( m_frame, _( "Cannot read the Easy-PC project." ), e.What() );
805 return;
806 }
807 }
808 else if( m_InputFile.GetExt().CmpNoCase( wxS( "pcb" ) ) == 0 )
809 {
810 pcbFile = m_InputFile;
811 schFile = findSiblingByExt( m_InputFile, wxS( "sch" ) );
812 }
813 else
814 {
815 schFile = m_InputFile;
816 pcbFile = findSiblingByExt( m_InputFile, wxS( "pcb" ) );
817 }
818
819 // Sources are imported in place: a project's sheets are found relative to the .prj
820 if( schFile.IsOk() && schFile.FileExists() )
821 doImport( schFile.GetFullPath(), FRAME_SCH, SCH_IO_MGR::SCH_EASYPC );
822
823 if( pcbFile.IsOk() && pcbFile.FileExists() )
824 {
825 doImport( pcbFile.GetFullPath(), FRAME_PCB_EDITOR, PCB_IO_MGR::EASYPC );
826 }
827 else if( !projectPcb.IsEmpty() )
828 {
829 DisplayErrorMessage( m_frame, wxString::Format( _( "The board '%s' named by the Easy-PC project was not "
830 "found." ),
831 projectPcb ) );
832 }
833}
834
835
836void IMPORT_PROJ_HELPER::ImportFiles( int aImportedSchFileType, int aImportedPcbFileType )
837{
838 m_properties.clear();
839
840 // both imports must agree on the cache nickname up front
842
843 int importedSchFileType = aImportedSchFileType;
844 int importedPcbFileType = aImportedPcbFileType;
845
846 if( importedSchFileType == SCH_IO_MGR::SCH_EASYEDAPRO || importedSchFileType == SCH_IO_MGR::SCH_EASYEDAPRO_V3
847 || importedPcbFileType == PCB_IO_MGR::EASYEDAPRO || importedPcbFileType == PCB_IO_MGR::EASYEDAPRO_V3 )
848 {
849 bool isV3 = EASYEDAPRO::V3_DOC_PARSER::IsV3Archive( m_InputFile.GetFullPath() );
850
851 importedSchFileType = isV3 ? SCH_IO_MGR::SCH_EASYEDAPRO_V3 : SCH_IO_MGR::SCH_EASYEDAPRO;
852 importedPcbFileType = isV3 ? PCB_IO_MGR::EASYEDAPRO_V3 : PCB_IO_MGR::EASYEDAPRO;
853
854 EasyEDAProProjectHandler( importedSchFileType, importedPcbFileType );
855 }
856 else if( importedSchFileType == SCH_IO_MGR::SCH_ALTIUM || importedPcbFileType == PCB_IO_MGR::ALTIUM_DESIGNER )
857 {
859 return;
860 }
861 else if( aImportedSchFileType == SCH_IO_MGR::SCH_ORCAD )
862 {
864 return;
865 }
866 else if( aImportedSchFileType == SCH_IO_MGR::SCH_EASYPC )
867 {
869 return;
870 }
871 else if( aImportedSchFileType == SCH_IO_MGR::SCH_GEDA )
872 {
873 if( m_InputFile.GetExt().CmpNoCase( "prj" ) == 0 )
874 {
876 }
877 else if( m_InputFile.GetExt().CmpNoCase( "pcb" ) == 0 )
878 {
879 ImportIndividualFile( PCB_T, aImportedPcbFileType );
880 }
881 else
882 {
883 // When importing a bare .sch file, pass the original source directory
884 // so the importer can find symbols in subdirectories relative to the
885 // gEDA / Lepton EDA schematic, even though the file may be copied to a new location
886 // for the KiCad project.
887 wxString sourceDir = m_InputFile.GetPath();
888 wxString symPaths = sourceDir;
889
890 auto discoverSymDirs = [&]( const wxString& aBaseDir )
891 {
892 wxDir dir( aBaseDir );
893
894 if( !dir.IsOpened() )
895 return;
896
897 wxString subdir;
898 bool cont = dir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS );
899
900 while( cont )
901 {
902 wxString subdirPath =
903 aBaseDir + wxFileName::GetPathSeparator() + subdir;
904 wxDir childDir( subdirPath );
905
906 if( childDir.IsOpened() && childDir.HasFiles( wxT( "*.sym" ) ) )
907 symPaths += wxT( "\n" ) + subdirPath;
908
909 cont = dir.GetNext( &subdir );
910 }
911 };
912
913 discoverSymDirs( sourceDir );
914 m_properties["sym_search_paths"] = symPaths.ToStdString();
915
916 doImport( m_InputFile.GetFullPath(), FRAME_SCH, SCH_IO_MGR::SCH_GEDA );
917 ImportIndividualFile( PCB_T, aImportedPcbFileType );
918 }
919
920 return;
921 }
922
923 ImportIndividualFile( SCHEMATIC_T, importedSchFileType );
924 ImportIndividualFile( PCB_T, importedPcbFileType );
925}
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 SaveImportedFiles()
Saves every design an editor imported, so the new project exists on disk.
void OrcadProjectHandler()
Imports the OrCAD schematic, then offers to import an associated Allegro board.
wxFileName m_TargetProj
Definition import_proj.h:68
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:73
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:81
std::set< FRAME_T > m_importedFrames
editors whose import succeeded
Definition import_proj.h:75
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:71
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:67
std::vector< wxString > m_schExtenstions
Definition import_proj.h:80
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:499
@ 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 & operator=(SCOPED_FILE_REMOVER const &)=delete
SCOPED_FILE_REMOVER(SCOPED_FILE_REMOVER const &)=delete
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_T
The set of mail types sendable via KIWAY::ExpressMail() and supplied as the aCommand parameter to tha...
Definition mail_type.h:34
@ MAIL_IMPORT_FILE
Definition mail_type.h:44
@ MAIL_SCH_SAVE
Definition mail_type.h:38
@ MAIL_ADD_LOCAL_LIB
Definition mail_type.h:50
@ MAIL_PCB_SAVE
Definition mail_type.h:39
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.