KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbnew/files.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
6 * Copyright (C) 2023 CERN (www.cern.ch)
7 * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <string>
28
29#include <confirm.h>
30#include <kidialog.h>
31#include <core/arraydim.h>
32#include <core/thread_pool.h>
34#include <gestfich.h>
35#include <pcb_edit_frame.h>
38#include <fp_lib_table.h>
39#include <kiface_base.h>
40#include <macros.h>
41#include <trace_helpers.h>
42#include <lockfile.h>
43#include <wx/snglinst.h>
45#include <pcbnew_id.h>
47#include <tool/tool_manager.h>
48#include <board.h>
49#include <kiplatform/app.h>
51#include <widgets/wx_infobar.h>
54#include <paths.h>
55#include <pgm_base.h>
57#include <project_pcb.h>
61#include <pcb_io/pcb_io_mgr.h>
68#include <tools/pcb_actions.h>
69#include "footprint_info_impl.h"
70#include <board_commit.h>
71#include <zone_filler.h>
74#include <wx_filename.h> // For ::ResolvePossibleSymlinks()
75
76#include <kiplatform/io.h>
77
78#include <wx/stdpaths.h>
79#include <wx/ffile.h>
80#include <wx/filedlg.h>
81#include <wx/txtstrm.h>
82#include <wx/wfstream.h>
83#include <wx/zipstrm.h>
84#include <wx/dir.h>
85
86#include "widgets/filedlg_hook_save_project.h"
87
88//#define USE_INSTRUMENTATION 1
89#define USE_INSTRUMENTATION 0
90
91
101bool AskLoadBoardFileName( PCB_EDIT_FRAME* aParent, wxString* aFileName, int aCtl = 0 )
102{
103 std::vector<IO_BASE::IO_FILE_DESC> descriptions;
104
105 for( const auto& plugin : PCB_IO_MGR::PLUGIN_REGISTRY::Instance()->AllPlugins() )
106 {
107 bool isKiCad = plugin.m_type == PCB_IO_MGR::KICAD_SEXP || plugin.m_type == PCB_IO_MGR::LEGACY;
108
109 if( ( aCtl & KICTL_KICAD_ONLY ) && !isKiCad )
110 continue;
111
112 if( ( aCtl & KICTL_NONKICAD_ONLY ) && isKiCad )
113 continue;
114
115 IO_RELEASER<PCB_IO> pi( plugin.m_createFunc() );
116 wxCHECK( pi, false );
117
118 const IO_BASE::IO_FILE_DESC& desc = pi->GetBoardFileDesc();
119
120 if( desc.m_FileExtensions.empty() || !desc.m_CanRead )
121 continue;
122
123 descriptions.emplace_back( desc );
124 }
125
126 wxString fileFiltersStr;
127 std::vector<std::string> allExtensions;
128 std::set<wxString> allWildcardsSet;
129
130 for( const IO_BASE::IO_FILE_DESC& desc : descriptions )
131 {
132 if( !fileFiltersStr.IsEmpty() )
133 fileFiltersStr += wxChar( '|' );
134
135 fileFiltersStr += desc.FileFilter();
136
137 for( const std::string& ext : desc.m_FileExtensions )
138 {
139 allExtensions.emplace_back( ext );
140 allWildcardsSet.insert( wxT( "*." ) + formatWildcardExt( ext ) + wxT( ";" ) );
141 }
142 }
143
144 wxString allWildcardsStr;
145
146 for( const wxString& wildcard : allWildcardsSet )
147 allWildcardsStr << wildcard;
148
149 if( aCtl & KICTL_KICAD_ONLY )
150 {
151 fileFiltersStr = _( "All KiCad Board Files" ) + AddFileExtListToFilter( allExtensions );
152 }
153 else
154 {
155 fileFiltersStr = _( "All supported formats" ) + wxT( "|" ) + allWildcardsStr + wxT( "|" )
156 + fileFiltersStr;
157 }
158
159 wxFileName fileName( *aFileName );
160 wxString path;
161 wxString name;
162
163 if( fileName.FileExists() )
164 {
165 path = fileName.GetPath();
166 name = fileName.GetFullName();
167 }
168 else
169 {
170 path = aParent->GetMruPath();
171
172 if( path.IsEmpty() )
174 // leave name empty
175 }
176
177 bool kicadFormat = ( aCtl & KICTL_KICAD_ONLY );
178
179 wxFileDialog dlg( aParent,
180 kicadFormat ? _( "Open Board File" ) : _( "Import Non KiCad Board File" ),
181 path, name, fileFiltersStr, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
182
183 FILEDLG_IMPORT_NON_KICAD importOptions( aParent->config()->m_System.show_import_issues );
184
185 if( !kicadFormat )
186 dlg.SetCustomizeHook( importOptions );
187
188 if( dlg.ShowModal() == wxID_OK )
189 {
190 *aFileName = dlg.GetPath();
191 aParent->SetMruPath( wxFileName( dlg.GetPath() ).GetPath() );
192
193 if( !kicadFormat )
194 aParent->config()->m_System.show_import_issues = importOptions.GetShowIssues();
195
196 return true;
197 }
198 else
199 {
200 return false;
201 }
202}
203
204
214bool AskSaveBoardFileName( PCB_EDIT_FRAME* aParent, wxString* aFileName, bool* aCreateProject )
215{
216 wxString wildcard = FILEEXT::PcbFileWildcard();
217 wxFileName fn = *aFileName;
218
220
221 wxFileDialog dlg( aParent, _( "Save Board File As" ), fn.GetPath(), fn.GetFullName(), wildcard,
222 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
223
224// Add a "Create a project" checkbox in standalone mode and one isn't loaded
225 FILEDLG_HOOK_SAVE_PROJECT newProjectHook;
226
227 if( Kiface().IsSingle() && aParent->Prj().IsNullProject() )
228 dlg.SetCustomizeHook( newProjectHook );
229
230 if( dlg.ShowModal() != wxID_OK )
231 return false;
232
233 *aFileName = dlg.GetPath();
234 *aFileName = EnsureFileExtension( *aFileName, FILEEXT::KiCadPcbFileExtension );
235
236 if( newProjectHook.IsAttachedToDialog() )
237 *aCreateProject = newProjectHook.GetCreateNewProject();
238 else if( !aParent->Prj().IsNullProject() )
239 *aCreateProject = true;
240
241 return true;
242}
243
244
245void PCB_EDIT_FRAME::OnFileHistory( wxCommandEvent& event )
246{
247 wxString fn = GetFileFromHistory( event.GetId(), _( "Printed circuit board" ) );
248
249 if( !!fn )
250 {
251 if( !wxFileName::IsFileReadable( fn ) )
252 {
253 if( !AskLoadBoardFileName( this, &fn, KICTL_KICAD_ONLY ) )
254 return;
255 }
256
257 OpenProjectFiles( std::vector<wxString>( 1, fn ), KICTL_KICAD_ONLY );
258 }
259}
260
261
262void PCB_EDIT_FRAME::OnClearFileHistory( wxCommandEvent& aEvent )
263{
265}
266
267
268void PCB_EDIT_FRAME::Files_io( wxCommandEvent& event )
269{
270 int id = event.GetId();
271 Files_io_from_id( id );
272}
273
274
276{
277 wxString msg;
278
279 switch( id )
280 {
281 case ID_LOAD_FILE:
282 {
283 // Only standalone mode can directly load a new document
284 if( !Kiface().IsSingle() )
285 return false;
286
287 int open_ctl = KICTL_KICAD_ONLY;
288 wxString fileName = Prj().AbsolutePath( GetBoard()->GetFileName() );
289
290 return AskLoadBoardFileName( this, &fileName, open_ctl )
291 && OpenProjectFiles( std::vector<wxString>( 1, fileName ), open_ctl );
292 }
293
295 {
296 // Note: we explicitly allow this even if not in standalone mode for now, even though it is dangerous.
297 int open_ctl = KICTL_NONKICAD_ONLY;
298 wxString fileName; // = Prj().AbsolutePath( GetBoard()->GetFileName() );
299
300 return AskLoadBoardFileName( this, &fileName, open_ctl )
301 && OpenProjectFiles( std::vector<wxString>( 1, fileName ), open_ctl );
302 }
303
305 {
306 wxFileName currfn = Prj().AbsolutePath( GetBoard()->GetFileName() );
307 wxFileName fn = currfn;
308
309 wxString rec_name = FILEEXT::AutoSaveFilePrefix + fn.GetName();
310 fn.SetName( rec_name );
311
312 if( !fn.FileExists() )
313 {
314 msg.Printf( _( "Recovery file '%s' not found." ), fn.GetFullPath() );
315 DisplayInfoMessage( this, msg );
316 return false;
317 }
318
319 msg.Printf( _( "OK to load recovery file '%s'?" ), fn.GetFullPath() );
320
321 if( !IsOK( this, msg ) )
322 return false;
323
324 GetScreen()->SetContentModified( false ); // do not prompt the user for changes
325
326 if( OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ) ) )
327 {
328 // Re-set the name since name or extension was changed
329 GetBoard()->SetFileName( currfn.GetFullPath() );
330 UpdateTitle();
331 return true;
332 }
333
334 return false;
335 }
336
337 case ID_REVERT_BOARD:
338 {
339 wxFileName fn = Prj().AbsolutePath( GetBoard()->GetFileName() );
340
341 msg.Printf( _( "Revert '%s' to last version saved?" ), fn.GetFullPath() );
342
343 if( !IsOK( this, msg ) )
344 return false;
345
346 GetScreen()->SetContentModified( false ); // do not prompt the user for changes
347
348 ReleaseFile();
349
350 return OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ), KICTL_REVERT );
351 }
352
353 case ID_NEW_BOARD:
354 {
355 // Only standalone mode can directly load a new document
356 if( !Kiface().IsSingle() )
357 return false;
358
359 if( IsContentModified() )
360 {
361 wxFileName fileName = GetBoard()->GetFileName();
362 wxString saveMsg = _( "Current board will be closed, save changes to '%s' before "
363 "continuing?" );
364
365 if( !HandleUnsavedChanges( this, wxString::Format( saveMsg, fileName.GetFullName() ),
366 [&]()->bool
367 {
368 return Files_io_from_id( ID_SAVE_BOARD );
369 } ) )
370 {
371 return false;
372 }
373 }
374 else if( !GetBoard()->IsEmpty() )
375 {
376 if( !IsOK( this, _( "Current Board will be closed. Continue?" ) ) )
377 return false;
378 }
379
381
383
385 mgr->UnloadProject( &mgr->Prj() );
386
387 if( !Clear_Pcb( false ) )
388 return false;
389
392
394
395 OnModify();
396 return true;
397 }
398
399 case ID_SAVE_BOARD:
400 if( !GetBoard()->GetFileName().IsEmpty() )
401 {
402 if( SavePcbFile( Prj().AbsolutePath( GetBoard()->GetFileName() ) ) )
403 {
404 m_autoSaveRequired = false;
405 return true;
406 }
407
408 return false;
409 }
410
412
413 case ID_COPY_BOARD_AS:
414 case ID_SAVE_BOARD_AS:
415 {
416 bool addToHistory = ( id == ID_SAVE_BOARD_AS );
417 wxString orig_name;
418
419 wxFileName::SplitPath( GetBoard()->GetFileName(), nullptr, nullptr, &orig_name, nullptr );
420
421 if( orig_name.IsEmpty() )
422 orig_name = NAMELESS_PROJECT;
423
424 wxFileName savePath( Prj().GetProjectFullName() );
425
426 if( !savePath.IsOk() || !savePath.IsDirWritable() )
427 {
428 savePath = GetMruPath();
429
430 if( !savePath.IsOk() || !savePath.IsDirWritable() )
432 }
433
434 wxFileName fn( savePath.GetPath(), orig_name, FILEEXT::KiCadPcbFileExtension );
435 wxString filename = fn.GetFullPath();
436 bool createProject = false;
437 bool success = false;
438
439 if( AskSaveBoardFileName( this, &filename, &createProject ) )
440 {
441 if( id == ID_COPY_BOARD_AS )
442 {
443 success = SavePcbCopy( filename, createProject );
444 }
445 else
446 {
447 success = SavePcbFile( filename, addToHistory, createProject );
448
449 if( success )
450 m_autoSaveRequired = false;
451 }
452 }
453
454 return success;
455 }
456
457 default:
458 return false;
459 }
460}
461
462
463int PCB_EDIT_FRAME::inferLegacyEdgeClearance( BOARD* aBoard, bool aShowUserMsg )
464{
465 PCB_LAYER_COLLECTOR collector;
466
467 collector.SetLayerId( Edge_Cuts );
468 collector.Collect( aBoard, GENERAL_COLLECTOR::AllBoardItems );
469
470 int edgeWidth = -1;
471 bool mixed = false;
472
473 for( int i = 0; i < collector.GetCount(); i++ )
474 {
475 if( collector[i]->Type() == PCB_SHAPE_T )
476 {
477 int itemWidth = static_cast<PCB_SHAPE*>( collector[i] )->GetWidth();
478
479 if( edgeWidth != -1 && edgeWidth != itemWidth )
480 {
481 mixed = true;
482 edgeWidth = std::max( edgeWidth, itemWidth );
483 }
484 else
485 {
486 edgeWidth = itemWidth;
487 }
488 }
489 }
490
491 if( mixed && aShowUserMsg )
492 {
493 // If they had different widths then we can't ensure that fills will be the same.
494 DisplayInfoMessage( this,
495 _( "If the zones on this board are refilled the Copper Edge "
496 "Clearance setting will be used (see Board Setup > Design "
497 "Rules > Constraints).\n This may result in different fills "
498 "from previous KiCad versions which used the line thicknesses "
499 "of the board boundary on the Edge Cuts layer." ) );
500 }
501
502 return std::max( 0, edgeWidth / 2 );
503}
504
505
506bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
507{
508 // This is for python:
509 if( aFileSet.size() != 1 )
510 {
511 UTF8 msg = StrPrintf( "Pcbnew:%s() takes a single filename", __func__ );
512 DisplayError( this, msg );
513 return false;
514 }
515
516 wxString fullFileName( aFileSet[0] );
517 wxFileName wx_filename( fullFileName );
518 wxString msg;
519
520 if( Kiface().IsSingle() )
522
523 // We insist on caller sending us an absolute path, if it does not, we say it's a bug.
524 wxASSERT_MSG( wx_filename.IsAbsolute(), wxT( "Path is not absolute!" ) );
525
526 std::unique_ptr<LOCKFILE> lock = std::make_unique<LOCKFILE>( fullFileName );
527
528 if( !lock->Valid() && lock->IsLockedByMe() )
529 {
530 // If we cannot acquire the lock but we appear to be the one who locked it, check to
531 // see if there is another KiCad instance running. If not, then we can override the
532 // lock. This could happen if KiCad crashed or was interrupted.
533
534 if( !Pgm().SingleInstance()->IsAnotherRunning() )
535 lock->OverrideLock();
536 }
537
538 if( !lock->Valid() )
539 {
540 msg.Printf( _( "PCB '%s' is already open by '%s' at '%s'." ),
541 wx_filename.GetFullName(),
542 lock->GetUsername(),
543 lock->GetHostname() );
544
545 if( !AskOverrideLock( this, msg ) )
546 return false;
547
548 lock->OverrideLock();
549 }
550
551 if( IsContentModified() )
552 {
553 if( !HandleUnsavedChanges( this, _( "The current PCB has been modified. Save changes?" ),
554 [&]() -> bool
555 {
556 return SavePcbFile( GetBoard()->GetFileName() );
557 } ) )
558 {
559 return false;
560 }
561 }
562
563 wxFileName pro = fullFileName;
564 pro.SetExt( FILEEXT::ProjectFileExtension );
565
566 bool is_new = !wxFileName::IsFileReadable( fullFileName );
567
568 // If its a non-existent PCB and caller thinks it exists
569 if( is_new && !( aCtl & KICTL_CREATE ) )
570 {
571 // notify user that fullFileName does not exist, ask if user wants to create it.
572 msg.Printf( _( "PCB '%s' does not exist. Do you wish to create it?" ), fullFileName );
573
574 if( !IsOK( this, msg ) )
575 return false;
576 }
577
578 // Get rid of any existing warnings about the old board
579 GetInfoBar()->Dismiss();
580
581 WX_PROGRESS_REPORTER progressReporter( this, is_new ? _( "Creating PCB" )
582 : _( "Loading PCB" ), 1 );
583
584 // No save prompt (we already prompted above), and only reset to a new blank board if new
585 Clear_Pcb( false, !is_new );
586
588
589 if( !is_new )
590 pluginType = PCB_IO_MGR::FindPluginTypeFromBoardPath( fullFileName, aCtl );
591
592 if( pluginType == PCB_IO_MGR::FILE_TYPE_NONE )
593 {
594 progressReporter.Hide();
595 DisplayErrorMessage( this, _( "File format is not supported" ), wxEmptyString );
596 return false;
597 }
598
599 bool converted = pluginType != PCB_IO_MGR::LEGACY && pluginType != PCB_IO_MGR::KICAD_SEXP;
600
601 // Loading a project should only be done under carefully considered circumstances.
602
603 // The calling code should know not to ask me here to change projects unless
604 // it knows what consequences that will have on other KIFACEs running and using
605 // this same PROJECT. It can be very harmful if that calling code is stupid.
607 bool setProject;
608
609 if( Kiface().IsSingle() || !( aCtl & KICTL_NONKICAD_ONLY ) )
610 setProject = pro.GetFullPath() != mgr->Prj().GetProjectFullName();
611 else
612 setProject = Prj().GetProjectFullName().IsEmpty();
613
614 wxString path = wxPathOnly( Prj().GetProjectFullName() );
615
616 if( setProject )
617 {
618 // calls SaveProject
620
622 mgr->UnloadProject( &mgr->Prj() );
623
624 mgr->LoadProject( pro.GetFullPath() );
625
626 // Do not allow saving a project if one doesn't exist. This normally happens if we are
627 // opening a board that has been moved from its project folder.
628 // For converted projects, we don't want to set the read-only flag because we want a
629 // project to be saved for the new file in case things like netclasses got migrated.
630 Prj().SetReadOnly( !pro.Exists() && !converted );
631 }
632
633 // Clear the cache footprint list which may be project specific
635
636 if( is_new )
637 {
638 // Link the existing blank board to the new project
639 GetBoard()->SetProject( &Prj() );
640
641 GetBoard()->SetFileName( fullFileName );
642
643 OnModify();
644 }
645 else
646 {
647 BOARD* loadedBoard = nullptr; // it will be set to non-NULL if loaded OK
649
650 if( LAYER_MAPPABLE_PLUGIN* mappable_pi = dynamic_cast<LAYER_MAPPABLE_PLUGIN*>( pi.get() ) )
651 {
652 mappable_pi->RegisterCallback( std::bind( DIALOG_MAP_LAYERS::RunModal,
653 this, std::placeholders::_1 ) );
654 }
655
656 if( PROJECT_CHOOSER_PLUGIN* chooser_pi = dynamic_cast<PROJECT_CHOOSER_PLUGIN*>( pi.get() ) )
657 {
658 chooser_pi->RegisterCallback( std::bind( DIALOG_IMPORT_CHOOSE_PROJECT::RunModal,
659 this,
660 std::placeholders::_1 ) );
661 }
662
663 if( ( aCtl & KICTL_REVERT ) )
664 {
665 DeleteAutoSaveFile( fullFileName );
666 }
667 else
668 {
669 // This will rename the file if there is an autosave and the user wants to recover
670 CheckForAutoSaveFile( fullFileName );
671 }
672
673 DIALOG_HTML_REPORTER errorReporter( this );
674 bool failedLoad = false;
675
676 try
677 {
678 if( pi == nullptr )
679 {
680 // There was no plugin found, e.g. due to invalid file extension, file header,...
681 THROW_IO_ERROR( _( "File format is not supported" ) );
682 }
683
684 std::map<std::string, UTF8> props;
685
687 props.insert( m_importProperties->begin(), m_importProperties->end() );
688
689 // PCB_IO_EAGLE can use this info to center the BOARD, but it does not yet.
690 props["page_width"] = std::to_string( GetPageSizeIU().x );
691 props["page_height"] = std::to_string( GetPageSizeIU().y );
692
693 pi->SetQueryUserCallback(
694 [&]( wxString aTitle, int aIcon, wxString aMessage, wxString aAction ) -> bool
695 {
696 KIDIALOG dlg( nullptr, aMessage, aTitle, wxOK | wxCANCEL | aIcon );
697
698 if( !aAction.IsEmpty() )
699 dlg.SetOKLabel( aAction );
700
701 dlg.DoNotShowCheckbox( aMessage, 0 );
702
703 return dlg.ShowModal() == wxID_OK;
704 } );
705
706#if USE_INSTRUMENTATION
707 // measure the time to load a BOARD.
708 int64_t startTime = GetRunningMicroSecs();
709#endif
710 if( config()->m_System.show_import_issues )
711 pi->SetReporter( errorReporter.m_Reporter );
712 else
713 pi->SetReporter( &NULL_REPORTER::GetInstance() );
714
715 pi->SetProgressReporter( &progressReporter );
716 loadedBoard = pi->LoadBoard( fullFileName, nullptr, &props, &Prj() );
717
718#if USE_INSTRUMENTATION
719 int64_t stopTime = GetRunningMicroSecs();
720 printf( "PCB_IO::Load(): %u usecs\n", stopTime - startTime );
721#endif
722 }
723 catch( const FUTURE_FORMAT_ERROR& ffe )
724 {
725 msg.Printf( _( "Error loading PCB '%s'." ), fullFileName );
726 progressReporter.Hide();
727 DisplayErrorMessage( this, msg, ffe.Problem() );
728
729 failedLoad = true;
730 }
731 catch( const IO_ERROR& ioe )
732 {
733 if( ioe.Problem() != wxT( "CANCEL" ) )
734 {
735 msg.Printf( _( "Error loading PCB '%s'." ), fullFileName );
736 progressReporter.Hide();
737 DisplayErrorMessage( this, msg, ioe.What() );
738 }
739
740 failedLoad = true;
741 }
742 catch( const std::bad_alloc& )
743 {
744 msg.Printf( _( "Memory exhausted loading PCB '%s'" ), fullFileName );
745 progressReporter.Hide();
746 DisplayErrorMessage( this, msg, wxEmptyString );
747
748 failedLoad = true;
749 }
750
751 if( failedLoad || !loadedBoard )
752 {
753 // We didn't create a new blank board above, so do that now
754 Clear_Pcb( false );
755
756 return false;
757 }
758
759 // This fixes a focus issue after the progress reporter is done on GTK. It shouldn't
760 // cause any issues on macOS and Windows. If it does, it will have to be conditionally
761 // compiled.
762 Raise();
763
764 if( errorReporter.m_Reporter->HasMessage() )
765 {
766 errorReporter.m_Reporter->Flush(); // Build HTML messages
767 errorReporter.ShowModal();
768 }
769
770 // Skip (possibly expensive) connectivity build here; we build it below after load
771 SetBoard( loadedBoard, false, &progressReporter );
772
773 if( GFootprintList.GetCount() == 0 )
774 GFootprintList.ReadCacheFromFile( Prj().GetProjectPath() + wxT( "fp-info-cache" ) );
775
776 if( loadedBoard->m_LegacyDesignSettingsLoaded )
777 {
778 Prj().SetReadOnly( false );
779
780 // Before we had a copper edge clearance setting, the edge line widths could be used
781 // as a kludge to control them. So if there's no setting then infer it from the
782 // edge widths.
783 if( !loadedBoard->m_LegacyCopperEdgeClearanceLoaded )
784 {
785 // Do not show the inferred edge clearance warning dialog when loading third
786 // party boards. For some reason the dialog completely hangs all of KiCad and
787 // the imported board cannot be saved.
788 int edgeClearance = inferLegacyEdgeClearance( loadedBoard, !converted );
789 loadedBoard->GetDesignSettings().m_CopperEdgeClearance = edgeClearance;
790 }
791
792 // On save; design settings will be removed from the board
793 loadedBoard->SetModified();
794 }
795
796 // Move legacy view settings to local project settings
797 if( !loadedBoard->m_LegacyVisibleLayers.test( Rescue ) )
798 {
800 loadedBoard->SetModified();
801 }
802
804 {
806 loadedBoard->SetModified();
807 }
808
809 // we should not ask PCB_IOs to do these items:
810 loadedBoard->BuildListOfNets();
811 ResolveDRCExclusions( true );
813
814 if( loadedBoard->IsModified() )
815 OnModify();
816 else
817 GetScreen()->SetContentModified( false );
818
819 if( ( pluginType == PCB_IO_MGR::LEGACY )
820 || ( pluginType == PCB_IO_MGR::KICAD_SEXP
822 && loadedBoard->GetGenerator().Lower() != wxT( "gerbview" ) ) )
823 {
826 m_infoBar->ShowMessage( _( "This file was created by an older version of KiCad. "
827 "It will be converted to the new format when saved." ),
829 }
830
831 // Import footprints into a project-specific library
832 //==================================================
833 // TODO: This should be refactored out of here into somewhere specific to the Project Import
834 // E.g. KICAD_MANAGER_FRAME::ImportNonKiCadProject
835 if( aCtl & KICTL_IMPORT_LIB )
836 {
837 wxFileName loadedBoardFn( fullFileName );
838 wxString libNickName = loadedBoardFn.GetName();
839
840 // Extract a footprint library from the design and add it to the fp-lib-table
841 // The footprints are saved in a new .pretty library.
842 // If this library already exists, all previous footprints will be deleted
843 std::vector<FOOTPRINT*> loadedFootprints = pi->GetImportedCachedLibraryFootprints();
844 wxString newLibPath = CreateNewProjectLibrary( libNickName );
845
846 // Only create the new library if CreateNewLibrary succeeded (note that this fails if
847 // the library already exists and the user aborts after seeing the warning message
848 // which prompts the user to continue with overwrite or abort)
849 if( newLibPath.Length() > 0 )
850 {
852
853 for( FOOTPRINT* footprint : loadedFootprints )
854 {
855 try
856 {
857 if( !footprint->GetFPID().GetLibItemName().empty() ) // Handle old boards.
858 {
859 footprint->SetReference( "REF**" );
860 piSexpr->FootprintSave( newLibPath, footprint );
861 delete footprint;
862 }
863 }
864 catch( const IO_ERROR& ioe )
865 {
866 wxLogError( _( "Error saving footprint %s to project specific library." )
867 + wxS( "\n%s" ),
868 footprint->GetFPID().GetUniStringLibItemName(),
869 ioe.What() );
870 }
871 }
872
874 const wxString& project_env = PROJECT_VAR_NAME;
875 wxString rel_path, env_path;
876
877 wxASSERT_MSG( wxGetEnv( project_env, &env_path ),
878 wxT( "There is no project variable?" ) );
879
880 wxString result( newLibPath );
881
882 if( result.Replace( env_path, wxT( "$(" ) + project_env + wxT( ")" ) ) )
883 rel_path = result;
884
885 FP_LIB_TABLE_ROW* row = new FP_LIB_TABLE_ROW( libNickName, rel_path,
886 wxT( "KiCad" ), wxEmptyString );
887 prjlibtable->InsertRow( row );
888
889 wxString tblName = Prj().FootprintLibTblName();
890
891 try
892 {
893 PROJECT_PCB::PcbFootprintLibs( &Prj() )->Save( tblName );
894 }
895 catch( const IO_ERROR& ioe )
896 {
897 wxLogError( _( "Error saving project specific footprint library table." )
898 + wxS( "\n%s" ),
899 ioe.What() );
900 }
901
902 // Update footprint LIB_IDs to point to the just imported library
903 for( FOOTPRINT* footprint : GetBoard()->Footprints() )
904 {
905 LIB_ID libId = footprint->GetFPID();
906
907 if( libId.GetLibItemName().empty() )
908 continue;
909
910 libId.SetLibNickname( libNickName );
911 footprint->SetFPID( libId );
912 }
913 }
914 }
915 }
916
917 {
918 wxFileName fn;
919
920 fn.SetPath( Prj().GetProjectPath() );
921 fn.SetName( Prj().GetProjectName() );
923
924 wxString fname = fn.GetFullPath();
925
926 fname.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP );
927
928 GetBoard()->SetFileName( fname );
929 }
930
931 // Lock the file newly opened:
932 m_file_checker.reset( lock.release() );
933
934 if( !converted )
935 UpdateFileHistory( GetBoard()->GetFileName() );
936
937 std::vector<ZONE*> toFill;
938
939 // Rebuild list of nets (full ratsnest rebuild)
940 GetBoard()->BuildConnectivity( &progressReporter );
941
942 // Load project settings after setting up board; some of them depend on the nets list
945
946 // Syncs the UI (appearance panel, etc) with the loaded board and project
948
949 // Refresh the 3D view, if any
950 EDA_3D_VIEWER_FRAME* draw3DFrame = Get3DViewerFrame();
951
952 if( draw3DFrame )
953 draw3DFrame->NewDisplay();
954#if 0 && defined(DEBUG)
955 // Output the board object tree to stdout, but please run from command prompt:
956 GetBoard()->Show( 0, std::cout );
957#endif
958
959 // from EDA_APPL which was first loaded BOARD only:
960 {
961 /* For an obscure reason the focus is lost after loading a board file
962 * when starting up the process.
963 * (seems due to the recreation of the layer manager after loading the file)
964 * Give focus to main window and Drawpanel
965 * must be done for these 2 windows (for an obscure reason ...)
966 * Linux specific
967 * This is more a workaround than a fix.
968 */
969 SetFocus();
970 GetCanvas()->SetFocus();
971 }
972
973 return true;
974}
975
976
977bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool addToHistory,
978 bool aChangeProject )
979{
980 // please, keep it simple. prompting goes elsewhere.
981 wxFileName pcbFileName = aFileName;
982
983 if( pcbFileName.GetExt() == FILEEXT::LegacyPcbFileExtension )
984 pcbFileName.SetExt( FILEEXT::KiCadPcbFileExtension );
985
986 // Write through symlinks, don't replace them
988
989 if( !IsWritable( pcbFileName ) )
990 {
991 wxString msg = wxString::Format( _( "Insufficient permissions to write file '%s'." ),
992 pcbFileName.GetFullPath() );
993
994 DisplayError( this, msg );
995 return false;
996 }
997
998 // TODO: these will break if we ever go multi-board
999 wxFileName projectFile( pcbFileName );
1000 wxFileName rulesFile( pcbFileName );
1001 wxString msg;
1002
1003 projectFile.SetExt( FILEEXT::ProjectFileExtension );
1004 rulesFile.SetExt( FILEEXT::DesignRulesFileExtension );
1005
1006 if( projectFile.FileExists() )
1007 {
1009 }
1010 else if( aChangeProject )
1011 {
1012 Prj().SetReadOnly( false );
1013 GetSettingsManager()->SaveProjectAs( projectFile.GetFullPath() );
1014 }
1015
1016 wxFileName currentRules( GetDesignRulesPath() );
1017
1018 if( currentRules.FileExists() && !rulesFile.FileExists() && aChangeProject )
1019 KiCopyFile( currentRules.GetFullPath(), rulesFile.GetFullPath(), msg );
1020
1021 if( !msg.IsEmpty() )
1022 {
1023 DisplayError( this, wxString::Format( _( "Error saving custom rules file '%s'." ),
1024 rulesFile.GetFullPath() ) );
1025 }
1026
1027 if( projectFile.FileExists() )
1028 {
1029 // Save various DRC parameters, such as violation severities (which may have been
1030 // edited via the DRC dialog as well as the Board Setup dialog), DRC exclusions, etc.
1032
1035 }
1036
1037 wxString tempFile = wxFileName::CreateTempFileName( wxS( "pcbnew" ) );
1038 wxString upperTxt;
1039 wxString lowerTxt;
1040
1041 try
1042 {
1044
1045 pi->SaveBoard( tempFile, GetBoard(), nullptr );
1046 }
1047 catch( const IO_ERROR& ioe )
1048 {
1049 DisplayError( this, wxString::Format( _( "Error saving board file '%s'.\n%s" ),
1050 pcbFileName.GetFullPath(),
1051 ioe.What() ) );
1052
1053 lowerTxt.Printf( _( "Failed to create temporary file '%s'." ), tempFile );
1054
1055 SetMsgPanel( upperTxt, lowerTxt );
1056
1057 // In case we started a file but didn't fully write it, clean up
1058 wxRemoveFile( tempFile );
1059
1060 return false;
1061 }
1062
1063 // Preserve the permissions of the current file
1064 KIPLATFORM::IO::DuplicatePermissions( pcbFileName.GetFullPath(), tempFile );
1065
1066 // If save succeeded, replace the original with what we just wrote
1067 if( !wxRenameFile( tempFile, pcbFileName.GetFullPath() ) )
1068 {
1069 DisplayError( this, wxString::Format( _( "Error saving board file '%s'.\n"
1070 "Failed to rename temporary file '%s." ),
1071 pcbFileName.GetFullPath(),
1072 tempFile ) );
1073
1074 lowerTxt.Printf( _( "Failed to rename temporary file '%s'." ),
1075 tempFile );
1076
1077 SetMsgPanel( upperTxt, lowerTxt );
1078
1079 return false;
1080 }
1081
1082 if( !Kiface().IsSingle() )
1083 {
1084 WX_STRING_REPORTER backupReporter;
1085
1086 if( !GetSettingsManager()->TriggerBackupIfNeeded( backupReporter ) )
1087 upperTxt = backupReporter.GetMessages();
1088 }
1089
1090 GetBoard()->SetFileName( pcbFileName.GetFullPath() );
1091
1092 // Update the lock in case it was a Save As
1093 LockFile( pcbFileName.GetFullPath() );
1094
1095 // Put the saved file in File History if requested
1096 if( addToHistory )
1097 UpdateFileHistory( GetBoard()->GetFileName() );
1098
1099 // Delete auto save file on successful save.
1100 wxFileName autoSaveFileName = pcbFileName;
1101
1102 autoSaveFileName.SetName( FILEEXT::AutoSaveFilePrefix + pcbFileName.GetName() );
1103
1104 if( autoSaveFileName.FileExists() )
1105 wxRemoveFile( autoSaveFileName.GetFullPath() );
1106
1107 lowerTxt.Printf( _( "File '%s' saved." ), pcbFileName.GetFullPath() );
1108
1109 SetStatusText( lowerTxt, 0 );
1110
1111 // Get rid of the old version conversion warning, or any other dismissable warning :)
1113 m_infoBar->Dismiss();
1114
1115 if( m_infoBar->IsShownOnScreen() && m_infoBar->HasCloseButton() )
1116 m_infoBar->Dismiss();
1117
1118 GetScreen()->SetContentModified( false );
1119 UpdateTitle();
1120 return true;
1121}
1122
1123
1124bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName, bool aCreateProject )
1125{
1126 wxFileName pcbFileName( EnsureFileExtension( aFileName, FILEEXT::KiCadPcbFileExtension ) );
1127
1128 if( !IsWritable( pcbFileName ) )
1129 {
1130 DisplayError( this, wxString::Format( _( "Insufficient permissions to write file '%s'." ),
1131 pcbFileName.GetFullPath() ) );
1132 return false;
1133 }
1134
1135 // Save various DRC parameters, such as violation severities (which may have been
1136 // edited via the DRC dialog as well as the Board Setup dialog), DRC exclusions, etc.
1138
1140
1141 try
1142 {
1144
1145 wxASSERT( pcbFileName.IsAbsolute() );
1146
1147 pi->SaveBoard( pcbFileName.GetFullPath(), GetBoard(), nullptr );
1148 }
1149 catch( const IO_ERROR& ioe )
1150 {
1151 DisplayError( this, wxString::Format( _( "Error saving board file '%s'.\n%s" ),
1152 pcbFileName.GetFullPath(),
1153 ioe.What() ) );
1154
1155 return false;
1156 }
1157
1158 wxFileName projectFile( pcbFileName );
1159 wxFileName rulesFile( pcbFileName );
1160 wxString msg;
1161
1162 projectFile.SetExt( FILEEXT::ProjectFileExtension );
1163 rulesFile.SetExt( FILEEXT::DesignRulesFileExtension );
1164
1165 if( aCreateProject && !projectFile.FileExists() )
1166 GetSettingsManager()->SaveProjectCopy( projectFile.GetFullPath() );
1167
1168 wxFileName currentRules( GetDesignRulesPath() );
1169
1170 if( aCreateProject && currentRules.FileExists() && !rulesFile.FileExists() )
1171 KiCopyFile( currentRules.GetFullPath(), rulesFile.GetFullPath(), msg );
1172
1173 if( !msg.IsEmpty() )
1174 {
1175 DisplayError( this, wxString::Format( _( "Error saving custom rules file '%s'." ),
1176 rulesFile.GetFullPath() ) );
1177 }
1178
1179 DisplayInfoMessage( this, wxString::Format( _( "Board copied to:\n%s" ),
1180 pcbFileName.GetFullPath() ) );
1181
1182 return true;
1183}
1184
1185
1187{
1188 wxFileName tmpFileName;
1189
1190 // Don't run autosave if content has not been modified
1191 if( !IsContentModified() )
1192 return true;
1193
1194 wxString title = GetTitle(); // Save frame title, that can be modified by the save process
1195
1196 if( GetBoard()->GetFileName().IsEmpty() )
1197 {
1198 tmpFileName = wxFileName( PATHS::GetDefaultUserProjectsPath(), NAMELESS_PROJECT,
1200 GetBoard()->SetFileName( tmpFileName.GetFullPath() );
1201 }
1202 else
1203 {
1204 tmpFileName = Prj().AbsolutePath( GetBoard()->GetFileName() );
1205 }
1206
1207 wxFileName autoSaveFileName = tmpFileName;
1208
1209 // Auto save file name is the board file name prepended with autosaveFilePrefix string.
1210 autoSaveFileName.SetName( FILEEXT::AutoSaveFilePrefix + autoSaveFileName.GetName() );
1211
1212 if( !autoSaveFileName.IsOk() )
1213 return false;
1214
1215 // If the board file path is not writable, try writing to a platform specific temp file
1216 // path. If that path isn't writable, give up.
1217 if( !autoSaveFileName.IsDirWritable() )
1218 {
1219 autoSaveFileName.SetPath( wxFileName::GetTempDir() );
1220
1221 if( !autoSaveFileName.IsOk() || !autoSaveFileName.IsDirWritable() )
1222 return false;
1223 }
1224
1225 wxLogTrace( traceAutoSave,
1226 wxT( "Creating auto save file <" ) + autoSaveFileName.GetFullPath() + wxT( ">" ) );
1227
1228 if( SavePcbFile( autoSaveFileName.GetFullPath(), false, false ) )
1229 {
1231 GetBoard()->SetFileName( tmpFileName.GetFullPath() );
1232 UpdateTitle();
1233 m_autoSaveRequired = false;
1234 m_autoSavePending = false;
1235
1236 if( !Kiface().IsSingle() &&
1237 GetSettingsManager()->GetCommonSettings()->m_Backup.backup_on_autosave )
1238 {
1240 }
1241
1242 SetTitle( title ); // Restore initial frame title
1243
1244 return true;
1245 }
1246
1247 GetBoard()->SetFileName( tmpFileName.GetFullPath() );
1248
1249 SetTitle( title ); // Restore initial frame title
1250
1251 return false;
1252}
1253
1254
1255bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType,
1256 const std::map<std::string, UTF8>* aProperties )
1257{
1258 m_importProperties = aProperties;
1259
1260 switch( (PCB_IO_MGR::PCB_FILE_T) aFileType )
1261 {
1263 case PCB_IO_MGR::EAGLE:
1266 return OpenProjectFiles( std::vector<wxString>( 1, aFileName ),
1268
1269 default: break;
1270 }
1271
1272 m_importProperties = nullptr;
1273
1274 return false;
1275}
1276
1277
1278void PCB_EDIT_FRAME::GenIPC2581File( wxCommandEvent& event )
1279{
1280 DIALOG_EXPORT_2581 dlg( this );
1281
1282 if( dlg.ShowModal() != wxID_OK )
1283 return;
1284
1285 wxFileName pcbFileName = dlg.GetOutputPath();
1286
1287 // Write through symlinks, don't replace them
1289
1290 if( pcbFileName.GetName().empty() )
1291 {
1292 DisplayError( this, _( "The board must be saved before generating IPC2581 file." ) );
1293 return;
1294 }
1295
1296 if( !IsWritable( pcbFileName ) )
1297 {
1298 wxString msg = wxString::Format( _( "Insufficient permissions to write file '%s'." ),
1299 pcbFileName.GetFullPath() );
1300
1301 DisplayError( this, msg );
1302 return;
1303 }
1304
1305 wxString tempFile = wxFileName::CreateTempFileName( wxS( "pcbnew_ipc" ) );
1306 wxString upperTxt;
1307 wxString lowerTxt;
1308 WX_PROGRESS_REPORTER reporter( this, _( "Generating IPC2581 file" ), 5 );
1309 std::map<std::string, UTF8> props;
1310
1311 props["units"] = dlg.GetUnitsString();
1312 props["sigfig"] = dlg.GetPrecision();
1313 props["version"] = dlg.GetVersion();
1314 props["OEMRef"] = dlg.GetOEM();
1315 props["mpn"] = dlg.GetMPN();
1316 props["mfg"] = dlg.GetMfg();
1317 props["dist"] = dlg.GetDist();
1318 props["distpn"] = dlg.GetDistPN();
1319
1320 auto saveFile =
1321 [&]() -> bool
1322 {
1323 try
1324 {
1326 pi->SetProgressReporter( &reporter );
1327 pi->SaveBoard( tempFile, GetBoard(), &props );
1328 return true;
1329 }
1330 catch( const IO_ERROR& ioe )
1331 {
1332 DisplayError( this, wxString::Format( _( "Error generating IPC2581 file '%s'.\n%s" ),
1333 pcbFileName.GetFullPath(), ioe.What() ) );
1334
1335 lowerTxt.Printf( _( "Failed to create temporary file '%s'." ), tempFile );
1336
1337 SetMsgPanel( upperTxt, lowerTxt );
1338
1339 // In case we started a file but didn't fully write it, clean up
1340 wxRemoveFile( tempFile );
1341
1342 return false;
1343 }
1344 };
1345
1347 auto ret = tp.submit( saveFile );
1348
1349
1350 std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
1351
1352 while( status != std::future_status::ready )
1353 {
1354 reporter.KeepRefreshing();
1355 status = ret.wait_for( std::chrono::milliseconds( 250 ) );
1356 }
1357
1358 try
1359 {
1360 if( !ret.get() )
1361 return;
1362 }
1363 catch( const std::exception& e )
1364 {
1365 wxLogError( "Exception in IPC2581 generation: %s", e.what() );
1366 GetScreen()->SetContentModified( false );
1367 return;
1368 }
1369
1370 // Preserve the permissions of the current file
1371 KIPLATFORM::IO::DuplicatePermissions( pcbFileName.GetFullPath(), tempFile );
1372
1373 if( dlg.GetCompress() )
1374 {
1375 wxFileName tempfn = pcbFileName;
1376 tempfn.SetExt( FILEEXT::Ipc2581FileExtension );
1377 wxFileName zipfn = tempFile;
1378 zipfn.SetExt( "zip" );
1379
1380 wxFFileOutputStream fnout( zipfn.GetFullPath() );
1381 wxZipOutputStream zip( fnout );
1382 wxFFileInputStream fnin( tempFile );
1383
1384 zip.PutNextEntry( tempfn.GetFullName() );
1385 fnin.Read( zip );
1386 zip.Close();
1387 fnout.Close();
1388
1389 wxRemoveFile( tempFile );
1390 tempFile = zipfn.GetFullPath();
1391 }
1392
1393 // If save succeeded, replace the original with what we just wrote
1394 if( !wxRenameFile( tempFile, pcbFileName.GetFullPath() ) )
1395 {
1396 DisplayError( this, wxString::Format( _( "Error generating IPC2581 file '%s'.\n"
1397 "Failed to rename temporary file '%s." ),
1398 pcbFileName.GetFullPath(),
1399 tempFile ) );
1400
1401 lowerTxt.Printf( _( "Failed to rename temporary file '%s'." ),
1402 tempFile );
1403
1404 SetMsgPanel( upperTxt, lowerTxt );
1405 }
1406
1407 GetScreen()->SetContentModified( false );
1408}
1409
1410
1411void PCB_EDIT_FRAME::GenODBPPFiles( wxCommandEvent& event )
1412{
1413 DIALOG_EXPORT_ODBPP dlg( this );
1414
1415 if( dlg.ShowModal() != wxID_OK )
1416 return;
1417
1418 wxFileName pcbFileName = dlg.GetOutputPath();
1419
1420 // Write through symlinks, don't replace them
1422
1423 if( !IsWritable( pcbFileName ) )
1424 {
1425 wxString msg = wxString::Format( _( "Insufficient permissions to write file '%s'." ),
1426 pcbFileName.GetFullPath() );
1427
1428 DisplayErrorMessage( this, msg );
1429 return;
1430 }
1431
1432 if( !wxFileName::DirExists( pcbFileName.GetFullPath() ) )
1433 {
1434 // Make every directory provided when the provided path doesn't exist
1435 if( !wxFileName::Mkdir( pcbFileName.GetFullPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1436 {
1437 wxString msg;
1438
1439 msg.Printf( _( "Cannot create output directory '%s'." ), pcbFileName.GetFullPath() );
1440
1441 DisplayErrorMessage( this, msg );
1442 return;
1443 }
1444 }
1445
1446 wxFileName tempFile( pcbFileName.GetFullPath(), "" );
1447 tempFile.AppendDir( "odb" );
1448
1449 wxString upperTxt;
1450 wxString lowerTxt;
1451 std::map<std::string, UTF8> props;
1452
1453 props["units"] = dlg.GetUnitsString();
1454 props["sigfig"] = dlg.GetPrecision();
1455 WX_PROGRESS_REPORTER reporter( this, _( "Generating ODB++ output files" ), 5 );
1456
1457 auto saveFile = [&]() -> bool
1458 {
1459 try
1460 {
1462 pi->SetProgressReporter( &reporter );
1463 pi->SaveBoard( pcbFileName.GetFullPath(), GetBoard(), &props );
1464 return true;
1465 }
1466 catch( const IO_ERROR& ioe )
1467 {
1468 DisplayError( this, wxString::Format( _( "Error generating ODBPP files '%s'.\n%s" ),
1469 tempFile.GetFullPath(), ioe.What() ) );
1470
1471 lowerTxt.Printf( _( "Failed to create directory '%s'." ), tempFile.GetFullPath() );
1472
1473 SetMsgPanel( upperTxt, lowerTxt );
1474
1475 // In case we started a file but didn't fully write it, clean up
1476 wxFileName::Rmdir( tempFile.GetFullPath() );
1477 return false;
1478 }
1479 };
1480
1482 auto ret = tp.submit( saveFile );
1483
1484 std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
1485
1486 while( status != std::future_status::ready )
1487 {
1488 reporter.KeepRefreshing();
1489 status = ret.wait_for( std::chrono::milliseconds( 250 ) );
1490 }
1491
1492 try
1493 {
1494 if( !ret.get() )
1495 return;
1496 }
1497 catch( const std::exception& e )
1498 {
1499 wxLogError( "Exception in ODB++ generation: %s", e.what() );
1500 GetScreen()->SetContentModified( false );
1501 return;
1502 }
1503
1504 if( dlg.GetCompress() )
1505 {
1506 wxFileName zipFileName( pcbFileName.GetFullPath(), "odb.zip" );
1507
1508 wxFFileOutputStream fnout( zipFileName.GetFullPath() );
1509 wxZipOutputStream zipStream( fnout );
1510
1511 std::function<void( const wxString&, const wxString& )> addDirToZip =
1512 [&]( const wxString& dirPath, const wxString& parentPath )
1513 {
1514 wxDir dir( dirPath );
1515 wxString fileName;
1516
1517 bool cont = dir.GetFirst( &fileName, wxEmptyString, wxDIR_DEFAULT );
1518 while( cont )
1519 {
1520 wxFileName fileInZip( dirPath, fileName );
1521 wxString relativePath =
1522 parentPath.IsEmpty()
1523 ? fileName
1524 : parentPath + wxString( wxFileName::GetPathSeparator() )
1525 + fileName;
1526
1527 if( wxFileName::DirExists( fileInZip.GetFullPath() ) )
1528 {
1529 zipStream.PutNextDirEntry( relativePath );
1530 addDirToZip( fileInZip.GetFullPath(), relativePath );
1531 }
1532 else
1533 {
1534 wxFFileInputStream fileStream( fileInZip.GetFullPath() );
1535 zipStream.PutNextEntry( relativePath );
1536 fileStream.Read( zipStream );
1537 }
1538 cont = dir.GetNext( &fileName );
1539 }
1540 };
1541
1542 addDirToZip( tempFile.GetFullPath(), wxEmptyString );
1543
1544 zipStream.Close();
1545 fnout.Close();
1546 }
1547
1548 GetScreen()->SetContentModified( false );
1549}
const char * name
Definition: DXF_plotter.cpp:57
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
void SetContentModified(bool aModified=true)
Definition: base_screen.h:59
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:290
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition: board.h:373
GAL_SET m_LegacyVisibleItems
Definition: board.h:370
void BuildListOfNets()
Definition: board.h:828
void SetFileName(const wxString &aFileName)
Definition: board.h:325
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:187
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:2036
LSET m_LegacyVisibleLayers
Visibility settings stored in board prior to 6.0, only used for loading legacy files.
Definition: board.h:369
void SetProject(PROJECT *aProject, bool aReferenceOnly=false)
Link a board to a given project.
Definition: board.cpp:197
const wxString & GetFileName() const
Definition: board.h:327
int GetFileFormatVersionAtLoad() const
Definition: board.h:394
const wxString & GetGenerator() const
Adds an item to the container.
Definition: board.h:397
void ClearProject()
Definition: board.cpp:235
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:877
void SynchronizeProperties()
Copy the current project's text variables into the boards property cache.
Definition: board.cpp:2027
bool m_LegacyCopperEdgeClearanceLoaded
Definition: board.h:374
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
wxString GetOEM() const
wxString GetPrecision() const
bool GetCompress() const
wxString GetMPN() const
wxString GetDistPN() const
wxString GetDist() const
wxString GetMfg() const
wxString GetUnitsString() const
wxString GetOutputPath() const
wxString GetOutputPath() const
wxString GetPrecision() const
wxString GetUnitsString() const
Class DIALOG_HTML_REPORTER.
WX_HTML_REPORT_BOX * m_Reporter
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.
static std::map< wxString, PCB_LAYER_ID > RunModal(wxWindow *aParent, const std::vector< INPUT_LAYER_DESC > &aLayerDesc)
Create and show a dialog (modal) and returns the data from it after completion.
Create and handle a window for the 3d viewer connected to a Kiway and a pcbboard.
void NewDisplay(bool aForceImmediateRedraw=false)
Reload and refresh (rebuild) the 3D scene.
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
virtual void CheckForAutoSaveFile(const wxFileName &aFileName)
Check if an auto save file exists for aFileName and takes the appropriate action depending on the use...
WX_INFOBAR * m_infoBar
void UpdateFileHistory(const wxString &FullFileName, FILE_HISTORY *aFileHistory=nullptr)
Update the list of recently opened files.
void ClearFileHistory(FILE_HISTORY *aFileHistory=nullptr)
Removes all files from the file history.
wxString GetMruPath() const
virtual void DeleteAutoSaveFile(const wxFileName &aFileName)
bool IsWritable(const wxFileName &aFileName, bool aVerbose=true)
Checks if aFileName can be written.
wxString GetFileFromHistory(int cmdId, const wxString &type, FILE_HISTORY *aFileHistory=nullptr)
Fetches the file name from the file history list.
void SetMruPath(const wxString &aPath)
WX_INFOBAR * GetInfoBar()
void ReleaseFile()
Release the current file marked in use.
std::unique_ptr< LOCKFILE > m_file_checker
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
bool LockFile(const wxString &aFileName)
Mark a schematic file as being in use.
void SetFocus() override
void SetModified()
Definition: eda_item.cpp:67
bool IsModified() const
Definition: eda_item.h:106
bool GetCreateNewProject() const
Gets if this hook has attached controls to a dialog box.
void ReadCacheFromFile(const wxString &aFilePath) override
unsigned GetCount() const
Hold a record identifying a library accessed by the appropriate footprint library #PLUGIN object in t...
Definition: fp_lib_table.h:42
static const std::vector< KICAD_T > AllBoardItems
A scan list for all editable board items.
Definition: collectors.h:222
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
virtual const wxString Problem() const
what was the problem?
Definition: exceptions.cpp:46
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: kidialog.h:43
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: kidialog.cpp:51
int ShowModal() override
Definition: kidialog.cpp:95
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Plugin class for import plugins that support remappable layers.
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition: lib_id.cpp:99
const UTF8 & GetLibItemName() const
Definition: lib_id.h:102
bool InsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Adds aRow if it does not already exist or if doReplace is true.
void Save(const wxString &aFileName) const
Write this library table to aFileName in s-expression form.
static REPORTER & GetInstance()
Definition: reporter.cpp:115
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition: paths.cpp:140
static TOOL_ACTION repairBoard
Definition: pcb_actions.h:545
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
wxString CreateNewProjectLibrary(const wxString &aLibName=wxEmptyString, const wxString &aProposedName=wxEmptyString)
const VECTOR2I GetPageSizeIU() const override
Works off of GetPageSettings() to return the size of the paper page in the internal units of this par...
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
BOARD * GetBoard() const
EDA_3D_VIEWER_FRAME * Get3DViewerFrame()
The main frame for Pcbnew.
void LoadDrawingSheet()
Load the drawing sheet file.
void ResolveDRCExclusions(bool aCreateMarkers)
If aCreateMarkers then create DRC exclusion markers from the serialized data.
void SetBoard(BOARD *aBoard, PROGRESS_REPORTER *aReporter=nullptr) override
Set the #m_Pcb member in such as way as to ensure deleting any previous BOARD.
void GenIPC2581File(wxCommandEvent &event)
Create and IPC2581 output file.
bool doAutoSave() override
Perform auto save when the board has been modified and not saved within the auto save interval.
void OnModify() override
Must be called after a board change to set the modified flag.
void OnClearFileHistory(wxCommandEvent &aEvent)
bool SavePcbCopy(const wxString &aFileName, bool aCreateProject=false)
Write the board data structures to aFileName.
bool OpenProjectFiles(const std::vector< wxString > &aFileSet, int aCtl=0) override
Load a KiCad board (.kicad_pcb) from aFileName.
void onBoardLoaded()
Update the state of the GUI after a new board is loaded or created.
void SaveProjectLocalSettings() override
Save changes to the project local settings.
bool Files_io_from_id(int aId)
Read and write board files according to aId.
void GenODBPPFiles(wxCommandEvent &event)
Create and Generate ODB++ output files.
bool IsContentModified() const override
Get if the current board has been modified but not saved.
bool LoadProjectSettings()
Load the current project's file configuration settings which are pertinent to this PCB_EDIT_FRAME ins...
bool Clear_Pcb(bool doAskAboutUnsavedChanges, bool aFinal=false)
Delete all and reinitialize the current board.
Definition: initpcb.cpp:42
void Files_io(wxCommandEvent &event)
Call Files_io_from_id with the wxCommandEvent id.
void UpdateTitle()
Set the main window title bar text.
int inferLegacyEdgeClearance(BOARD *aBoard, bool aShowUserMsg=true)
const std::map< std::string, UTF8 > * m_importProperties
bool SavePcbFile(const wxString &aFileName, bool addToHistory=true, bool aChangeProject=true)
Write the board data structures to a aFileName.
bool importFile(const wxString &aFileName, int aFileType, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load the given filename but sets the path to the current project path.
void saveProjectSettings() override
Saves any design-related project settings associated with this frame.
void OnFileHistory(wxCommandEvent &event)
static PLUGIN_REGISTRY * Instance()
Definition: pcb_io_mgr.h:94
static PCB_IO * PluginFind(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
Definition: pcb_io_mgr.cpp:69
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition: pcb_io_mgr.h:56
@ FILE_TYPE_NONE
Definition: pcb_io_mgr.h:77
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition: pcb_io_mgr.h:58
@ LEGACY
Legacy Pcbnew file formats prior to s-expression.
Definition: pcb_io_mgr.h:59
@ CADSTAR_PCB_ARCHIVE
Definition: pcb_io_mgr.h:63
static PCB_FILE_T FindPluginTypeFromBoardPath(const wxString &aFileName, int aCtl=0)
Return a plugin type given a path for a board file.
Definition: pcb_io_mgr.cpp:112
Collect all BOARD_ITEM objects on a given layer.
Definition: collectors.h:539
void Collect(BOARD_ITEM *aBoard, const std::vector< KICAD_T > &aTypes)
Test a BOARD_ITEM using this class's Inspector method, which does the collection.
Definition: collectors.cpp:536
void SetLayerId(PCB_LAYER_ID aLayerId)
Definition: collectors.h:545
bool KeepRefreshing(bool aWait=false) override
Update the UI dialog.
Plugin class for import plugins that support choosing a project.
LSET m_VisibleLayers
Board settings.
GAL_SET m_VisibleItems
The GAL layers (aka items) that are turned on for viewing (.
static FP_LIB_TABLE * PcbFootprintLibs(PROJECT *aProject)
Return the table of footprint libraries without Kiway.
Definition: project_pcb.cpp:37
virtual void SetReadOnly(bool aReadOnly=true)
Definition: project.h:164
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition: project.cpp:129
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition: project.h:206
virtual const wxString FootprintLibTblName() const
Returns the path and filename of this project's footprint library table.
Definition: project.cpp:165
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition: project.cpp:359
virtual bool IsNullProject() const
Check if this project is a null project (i.e.
Definition: project.cpp:153
void SaveProjectAs(const wxString &aFullPath, PROJECT *aProject=nullptr)
Sets the currently loaded project path and saves it (pointers remain valid) Note that this will not m...
void SaveProjectCopy(const wxString &aFullPath, PROJECT *aProject=nullptr)
Saves a copy of the current project under the given path.
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Saves a loaded project.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Loads a project or sets up a new project with a specified path.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Saves, unloads and unregisters the given PROJECT.
bool TriggerBackupIfNeeded(REPORTER &aReporter) const
Calls BackupProject if a new backup is needed according to the current backup policy.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:167
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:72
bool empty() const
Definition: utf8.h:104
static void ResolvePossibleSymlinks(wxFileName &aFilename)
Definition: wx_filename.cpp:92
void Flush()
Build the HTML messages page.
bool HasMessage() const override
Returns true if the reporter client is non-empty.
void RemoveAllButtons()
Remove all the buttons that have been added by the user.
Definition: wx_infobar.cpp:304
bool HasCloseButton() const
Definition: wx_infobar.cpp:328
@ OUTDATED_SAVE
OUTDATED_SAVE Messages that should be cleared on save.
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: wx_infobar.cpp:190
void AddCloseButton(const wxString &aTooltip=_("Hide this message."))
Add the default close button to the infobar on the right side.
Definition: wx_infobar.cpp:294
MESSAGE_TYPE GetMessageType() const
Definition: wx_infobar.h:101
void ShowMessage(const wxString &aMessage, int aFlags=wxICON_INFORMATION) override
Show the info bar with the provided message and icon.
Definition: wx_infobar.cpp:154
Multi-thread safe progress reporter dialog, intended for use of tasks that parallel reporting back of...
A wrapper for reporting to a wxString object.
Definition: reporter.h:171
const wxString & GetMessages() const
Definition: reporter.cpp:84
wxString EnsureFileExtension(const wxString &aFilename, const wxString &aExtension)
It's annoying to throw up nag dialogs when the extension isn't right.
Definition: common.cpp:424
bool AskOverrideLock(wxWindow *aParent, const wxString &aMessage)
Display a dialog indicating the file is already open, with an option to reset the lock.
Definition: confirm.cpp:44
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:250
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:170
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:222
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition: confirm.cpp:130
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
This file is part of the common library.
#define _(s)
Declaration of the eda_3d_viewer class.
FOOTPRINT_LIST_IMPL GFootprintList
The global footprint info table.
Definition: cvpcb.cpp:156
void KiCopyFile(const wxString &aSrcPath, const wxString &aDestPath, wxString &aErrors)
Definition: gestfich.cpp:309
#define WIN_STRING_DIR_SEP
Definition: gestfich.h:38
#define UNIX_STRING_DIR_SEP
Definition: gestfich.h:37
static const std::string ProjectFileExtension
static const std::string LegacyPcbFileExtension
static const std::string Ipc2581FileExtension
static const std::string AutoSaveFilePrefix
static const std::string DesignRulesFileExtension
static const std::string KiCadPcbFileExtension
static wxString PcbFileWildcard()
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
@ ID_NEW_BOARD
Definition: id.h:75
@ ID_SAVE_BOARD
Definition: id.h:76
@ ID_LOAD_FILE
Definition: id.h:74
@ ID_SAVE_BOARD_AS
Definition: id.h:77
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition: io_mgr.h:33
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
PROJECT & Prj()
Definition: kicad.cpp:595
This file is part of the common library.
#define KICTL_CREATE
caller thinks requested project files may not exist.
Definition: kiway_player.h:76
#define KICTL_REVERT
reverting to a previously-saved (KiCad) file.
Definition: kiway_player.h:78
#define KICTL_IMPORT_LIB
import all footprints into a project library.
Definition: kiway_player.h:77
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
Definition: kiway_player.h:75
#define KICTL_NONKICAD_ONLY
chosen file is non-KiCad according to user
Definition: kiway_player.h:74
@ GAL_LAYER_ID_BITMASK_END
This is the end of the layers used for visibility bit masks in legacy board files.
Definition: layer_ids.h:228
@ Edge_Cuts
Definition: layer_ids.h:112
@ Rescue
Definition: layer_ids.h:121
#define GAL_LAYER_INDEX(x)
Use this macro to convert a GAL layer to a 0-indexed offset from LAYER_VIAS.
Definition: layer_ids.h:271
File locking utilities.
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
bool RegisterApplicationRestart(const wxString &aCommandLine)
Registers the application for restart with the OS with the given command line string to pass as args.
Definition: unix/app.cpp:65
bool DuplicatePermissions(const wxString &aSrc, const wxString &aDest)
Duplicates the file security data from one file to another ensuring that they are the same between bo...
Definition: unix/io.cpp:40
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
bool AskLoadBoardFileName(PCB_EDIT_FRAME *aParent, wxString *aFileName, int aCtl=0)
Show a wxFileDialog asking for a BOARD filename to open.
bool AskSaveBoardFileName(PCB_EDIT_FRAME *aParent, wxString *aFileName, bool *aCreateProject)
Put up a wxFileDialog asking for a BOARD filename to save.
@ ID_IMPORT_NON_KICAD_BOARD
Definition: pcbnew_id.h:19
@ ID_REVERT_BOARD
Definition: pcbnew_id.h:18
@ ID_MENU_RECOVER_BOARD_AUTOSAVE
Definition: pcbnew_id.h:81
@ ID_COPY_BOARD_AS
Definition: pcbnew_id.h:17
SETTINGS_MANAGER * GetSettingsManager()
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1060
see class PGM_BASE
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:40
#define NAMELESS_PROJECT
default name for nameless projects
Definition: project.h:43
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:68
bool show_import_issues
Stored value for "show import issues" when importing non-KiCad designs to this application.
Definition: app_settings.h:146
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:176
Container that describes file type info.
Definition: io_base.h:43
std::vector< std::string > m_FileExtensions
Filter used for file pickers if m_IsFile is true.
Definition: io_base.h:45
bool m_CanRead
Whether the IO can read this file type.
Definition: io_base.h:48
static thread_pool * tp
Definition: thread_pool.cpp:30
BS::thread_pool thread_pool
Definition: thread_pool.h:30
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:32
wxLogTrace helper definitions.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
wxString formatWildcardExt(const wxString &aWildcard)
Format wildcard extension to support case sensitive file dialogs.
Definition of file extensions used in Kicad.