KiCad PCB EDA Suite
Loading...
Searching...
No Matches
footprint_editor_control.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) 2014-2019 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <advanced_config.h>
23#include <string_utils.h>
24#include <pgm_base.h>
26#include <tool/tool_manager.h>
28#include <tools/pcb_actions.h>
30#include <eda_doc.h>
33#include <pcbnew_settings.h>
34#include <pcbnew_id.h>
35#include <confirm.h>
36#include <kidialog.h>
37#include <wx/filename.h>
39#include <launch_ext.h> // To default when file manager setting is empty
40#include <gestfich.h> // To open with a text editor
41#include <widgets/wx_infobar.h>
42#include <footprint.h>
43#include <pad.h>
44#include <pcb_group.h>
45#include <zone.h>
51#include <kiway.h>
52#include <project_pcb.h>
53#include <view/view_controls.h>
58#include <wx/dirdlg.h>
59
60#include <memory>
61
63
64
66 PCB_TOOL_BASE( "pcbnew.ModuleEditor" ),
67 m_frame( nullptr ),
68 m_checkerDialog( nullptr )
69{
70}
71
72
80
81
83{
84 LIBRARY_EDITOR_CONTROL* libraryTreeTool = m_toolMgr->GetTool<LIBRARY_EDITOR_CONTROL>();
85
86 // Build a context menu for the footprint tree
87 //
88 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
89
90 auto libSelectedCondition =
91 [this]( const SELECTION& aSel )
92 {
93 LIB_ID sel = m_frame->GetLibTree()->GetSelectedLibId();
94 return !sel.GetLibNickname().empty() && sel.GetLibItemName().empty();
95 };
96
97 // The libInferredCondition allows you to do things like New Symbol and Paste with a
98 // symbol selected (in other words, when we know the library context even if the library
99 // itself isn't selected.
100 auto libInferredCondition =
101 [this]( const SELECTION& aSel )
102 {
103 LIB_ID sel = m_frame->GetLibTree()->GetSelectedLibId();
104 return !sel.GetLibNickname().empty();
105 };
106
107 auto fpSelectedCondition =
108 [this]( const SELECTION& aSel )
109 {
110 LIB_ID sel = m_frame->GetLibTree()->GetSelectedLibId();
111 return !sel.GetLibNickname().empty() && !sel.GetLibItemName().empty();
112 };
113
114 auto fpExportCondition =
115 [this]( const SELECTION& aSel )
116 {
117 FOOTPRINT* fp = m_frame->GetBoard()->GetFirstFootprint();
118 return fp != nullptr;
119 };
120
121 auto canOpenExternally =
122 [this]( const SELECTION& aSel )
123 {
124 // The option is shown if the editor has no current edits,
125 // dumb/simple guard against opening a new file that does not exist on disk
126 bool ret = !m_frame->IsContentModified();
127 return ret;
128 };
129
130// clang-format off
131 ctxMenu.AddItem( PCB_ACTIONS::newFootprint, libSelectedCondition, 10 );
132 ctxMenu.AddItem( PCB_ACTIONS::createFootprint, libSelectedCondition, 10 );
133
134 ctxMenu.AddSeparator( 10 );
135 ctxMenu.AddItem( ACTIONS::save, SELECTION_CONDITIONS::ShowAlways, 10 );
136 ctxMenu.AddItem( ACTIONS::saveAs, libSelectedCondition || fpSelectedCondition, 10 );
137 ctxMenu.AddItem( ACTIONS::revert, libSelectedCondition || libInferredCondition, 10 );
138
139 ctxMenu.AddSeparator( 10 );
140 ctxMenu.AddItem( PCB_ACTIONS::cutFootprint, fpSelectedCondition, 10 );
141 ctxMenu.AddItem( PCB_ACTIONS::copyFootprint, fpSelectedCondition, 10 );
142 ctxMenu.AddItem( PCB_ACTIONS::pasteFootprint, libInferredCondition, 10 );
143 ctxMenu.AddItem( PCB_ACTIONS::duplicateFootprint, fpSelectedCondition, 10 );
144 ctxMenu.AddItem( PCB_ACTIONS::renameFootprint, fpSelectedCondition, 10 );
145 ctxMenu.AddItem( PCB_ACTIONS::deleteFootprint, fpSelectedCondition, 10 );
146 ctxMenu.AddItem( PCB_ACTIONS::footprintProperties, fpSelectedCondition, 10 );
147
148 ctxMenu.AddSeparator( 100 );
149 ctxMenu.AddItem( PCB_ACTIONS::importFootprint, libInferredCondition, 100 );
150 ctxMenu.AddItem( PCB_ACTIONS::exportFootprint, fpExportCondition, 100 );
151
152 if( ADVANCED_CFG::GetCfg().m_EnableLibWithText )
153 {
154 ctxMenu.AddSeparator( 200 );
155 ctxMenu.AddItem( ACTIONS::openWithTextEditor, canOpenExternally && fpSelectedCondition, 200 );
156 }
157
158 if( ADVANCED_CFG::GetCfg().m_EnableLibDir )
159 {
160 ctxMenu.AddSeparator( 200 );
161 ctxMenu.AddItem( ACTIONS::openDirectory, canOpenExternally && ( libSelectedCondition || fpSelectedCondition ), 200 );
162 }
163// clang-format on
164
165 libraryTreeTool->AddContextMenuItems( &ctxMenu );
166
167 // Ensure the left toolbar's Line modes group reflects the current setting at startup
168 if( m_toolMgr )
170
171 return true;
172}
173
174
176 const LIB_ID& aTargetLib )
177{
178 const wxString libraryName = aTargetLib.GetUniStringLibNickname();
179
180 if( aTargetLib.GetLibNickname().empty() )
181 {
182 // Do nothing - the footprint will need to be saved manually to assign
183 // to a library.
184 }
185 else
186 {
187 if( !PROJECT_PCB::FootprintLibAdapter( &m_frame->Prj() )->IsFootprintLibWritable( libraryName ) )
188 {
189 // If the library is not writeable, we'll give the user a
190 // footprint not in a library. But add a warning to let them know
191 // they didn't quite get what they wanted.
192 m_frame->ShowInfoBarWarning(
193 wxString::Format(
194 _( "The footprint could not be added to the selected library ('%s'). "
195 "This library is read-only." ),
196 libraryName ),
197 false );
198 // And the footprint will need to be saved manually
199 }
200 else
201 {
202 // Go ahead and save it to the library
203 LIB_ID fpid = aFootprint.GetFPID();
204 fpid.SetLibNickname( aTargetLib.GetLibNickname() );
205 aFootprint.SetFPID( fpid );
206
207 // Clear the modified flag only on a successful save, else the edits are silently
208 // lost (issue #23850).
209 if( m_frame->SaveFootprint( &aFootprint ) )
210 m_frame->ClearModify();
211 }
212 }
213}
214
215
217{
218 const LIB_ID selected = m_frame->GetTargetFPID();
219 const wxString libraryName = selected.GetUniStringLibNickname();
220
221 if( !m_frame->BeginNewFootprint( libraryName ) )
222 return 0;
223
224 FOOTPRINT* newFootprint = m_frame->CreateNewFootprint( wxEmptyString, libraryName );
225
226 if( !newFootprint )
227 return 0;
228
229 // Give the new footprint a resolvable identity so it opens in its own tab instead of
230 // overwriting the active one. The legacy single-board path leaves the nickname empty.
231 if( m_frame->GetTabsPanel() && !libraryName.IsEmpty() )
232 newFootprint->SetFPID( LIB_ID( libraryName, newFootprint->GetFPID().GetLibItemName() ) );
233
235 m_frame->AddFootprintToBoard( newFootprint );
236
237 // Initialize data relative to nets and netclasses (for a new footprint the defaults are
238 // used). This is mandatory to handle and draw pads.
240 newFootprint->SetPosition( VECTOR2I( 0, 0 ) );
241 newFootprint->ClearFlags();
242
243 m_frame->Zoom_Automatique( false );
244 m_frame->GetScreen()->SetContentModified();
245
246 tryToSaveFootprintInLibrary( *newFootprint, selected );
247
248 m_frame->UpdateView();
249 m_frame->GetCanvas()->ForceRefresh();
250 m_frame->Update3DView( true, true );
251
252 m_frame->SyncLibraryTree( false );
253 return 0;
254}
255
256
258{
259 LIB_ID selected = m_frame->GetLibTree()->GetSelectedLibId();
260
261 if( m_frame->IsContentModified() )
262 {
263 if( !HandleUnsavedChanges( m_frame, _( "The current footprint has been modified. "
264 "Save changes?" ),
265 [&]() -> bool
266 {
267 return m_frame->SaveFootprint( footprint() );
268 } ) )
269 {
270 return 0;
271 }
272 }
273
274 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_FOOTPRINT_WIZARD, true, m_frame ) )
275 {
276 FOOTPRINT_WIZARD_FRAME* wizard = static_cast<FOOTPRINT_WIZARD_FRAME*>( frame );
277
278 if( wizard->ShowModal( nullptr, m_frame ) )
279 {
280 // Creates the new footprint from python script wizard
281 FOOTPRINT* newFootprint = wizard->GetBuiltFootprint();
282
283 if( newFootprint ) // i.e. if create footprint command is OK
284 {
285 m_frame->Clear_Pcb( false );
286
288 // Add the new object to board
289 m_frame->AddFootprintToBoard( newFootprint );
290
291 // Initialize data relative to nets and netclasses (for a new footprint the
292 // defaults are used). This is mandatory to handle and draw pads.
294 newFootprint->SetPosition( VECTOR2I( 0, 0 ) );
295 newFootprint->ClearFlags();
296
297 m_frame->Zoom_Automatique( false );
298 m_frame->GetScreen()->SetContentModified();
299 m_frame->OnModify();
300
301 tryToSaveFootprintInLibrary( *newFootprint, selected );
302
303 m_frame->UpdateView();
304 canvas()->Refresh();
305 m_frame->Update3DView( true, true );
306
307 m_frame->SyncLibraryTree( false );
308 }
309 }
310
311 wizard->Destroy();
312 }
313
314 return 0;
315}
316
317
319{
320 if( !footprint() ) // no loaded footprint
321 return 0;
322
323 const LIB_ID loadedId = m_frame->GetLoadedFPID();
324
325 // An unsaved import has no library identity for the tree selection to match, and save-as is the only
326 // way to give it one, so never let an unrelated selection swallow its save
327 if( m_frame->GetTargetFPID() == loadedId || loadedId.GetLibNickname().empty() )
328 {
329 if( m_frame->SaveFootprint( footprint() ) )
330 {
331 view()->Update( footprint() );
332
333 canvas()->ForceRefresh();
334 m_frame->ClearModify();
335 }
336 }
337
338 m_frame->RefreshLibraryTree();
339 return 0;
340}
341
342
344{
345 if( m_frame->GetTargetFPID().GetLibItemName().empty() )
346 {
348
349 // Save Library As
350 const wxString& src_libNickname = m_frame->GetTargetFPID().GetLibNickname();
351 std::optional<wxString> optUri = manager.GetFullURI( LIBRARY_TABLE_TYPE::FOOTPRINT, src_libNickname, true );
352 wxCHECK( optUri, 0 );
353
354 if( m_frame->SaveLibraryAs( *optUri ) )
355 m_frame->SyncLibraryTree( true );
356 }
357 else if( m_frame->GetTargetFPID() == m_frame->GetLoadedFPID() )
358 {
359 // Save Footprint As
360 if( footprint() && m_frame->SaveFootprintAs( footprint() ) )
361 {
362 view()->Update( footprint() );
363 m_frame->ClearModify();
364
365 // Get rid of the save-will-update-board-only (or any other dismissable warning)
366 WX_INFOBAR* infobar = m_frame->GetInfoBar();
367
368 if( infobar->IsShownOnScreen() && infobar->HasCloseButton() )
369 infobar->Dismiss();
370
371 canvas()->ForceRefresh();
372 m_frame->SyncLibraryTree( true );
373 }
374 }
375 else
376 {
377 // Save Selected Footprint As
378 FOOTPRINT* footprint = m_frame->LoadFootprint( m_frame->GetTargetFPID() );
379
380 if( footprint && m_frame->SaveFootprintAs( footprint ) )
381 {
382 m_frame->SyncLibraryTree( true );
383 m_frame->FocusOnLibID( footprint->GetFPID() );
384 }
385 }
386
387 m_frame->RefreshLibraryTree();
388 return 0;
389}
390
391
393{
394 getEditFrame<FOOTPRINT_EDIT_FRAME>()->RevertFootprint();
395 return 0;
396}
397
398
400{
401 LIB_ID fpID = m_frame->GetLibTree()->GetSelectedLibId();
402
403 if( fpID == m_frame->GetLoadedFPID() )
404 {
405 m_copiedFootprint = std::make_unique<FOOTPRINT>( *m_frame->GetBoard()->GetFirstFootprint() );
406 m_copiedFootprint->SetParent( nullptr );
407 }
408 else
409 {
410 m_copiedFootprint.reset( m_frame->LoadFootprint( fpID ) );
411 }
412
413 if( aEvent.IsAction( &PCB_ACTIONS::cutFootprint ) )
414 DeleteFootprint( aEvent );
415
416 return 0;
417}
418
419
421{
422 if( m_copiedFootprint && !m_frame->GetLibTree()->GetSelectedLibId().GetLibNickname().empty() )
423 {
424 wxString newLib = m_frame->GetLibTree()->GetSelectedLibId().GetLibNickname();
425 wxString newName = m_copiedFootprint->GetFPID().GetLibItemName();
426
427 while( PROJECT_PCB::FootprintLibAdapter( &m_frame->Prj() )->FootprintExists( newLib, newName ) )
428 newName += _( "_copy" );
429
430 m_copiedFootprint->SetFPID( LIB_ID( newLib, newName ) );
431 m_frame->SaveFootprintInLibrary( m_copiedFootprint.get(), newLib );
432
433 m_frame->SyncLibraryTree( true );
434 m_frame->LoadFootprintFromLibrary( m_copiedFootprint->GetFPID() );
435 m_frame->FocusOnLibID( m_copiedFootprint->GetFPID() );
436 m_frame->RefreshLibraryTree();
437 }
438
439 return 0;
440}
441
442
444{
445 LIB_ID fpID = m_frame->GetLibTree()->GetSelectedLibId();
447
448 if( fpID == m_frame->GetLoadedFPID() )
449 footprint = new FOOTPRINT( *m_frame->GetBoard()->GetFirstFootprint() );
450 else
451 footprint = m_frame->LoadFootprint( m_frame->GetTargetFPID() );
452
453 if( footprint && m_frame->DuplicateFootprint( footprint ) )
454 {
455 m_frame->SyncLibraryTree( true );
456 m_frame->LoadFootprintFromLibrary( footprint->GetFPID() );
457 m_frame->FocusOnLibID( footprint->GetFPID() );
458 m_frame->RefreshLibraryTree();
459 }
460
461 return 0;
462}
463
464
466{
469
470 LIB_ID fpID = m_frame->GetLibTree()->GetSelectedLibId();
471 wxString libraryName = fpID.GetLibNickname();
472 wxString oldName = fpID.GetLibItemName();
473 wxString newName;
474 wxString msg;
475
476 if( !libTool->RenameLibrary( _( "Change Footprint Name" ), oldName,
477 [&]( const wxString& aNewName )
478 {
479 newName = aNewName;
480
481 if( newName.IsEmpty() )
482 {
483 wxMessageBox( _( "Footprint must have a name." ) );
484 return false;
485 }
486
487 // If no change, accept it without prompting
488 if( oldName != newName && adapter->FootprintExists( libraryName, newName ) )
489 {
490 msg = wxString::Format( _( "Footprint '%s' already exists in library '%s'." ),
491 newName, libraryName );
492
493 KIDIALOG errorDlg( m_frame, msg, _( "Confirmation" ),
494 wxOK | wxCANCEL | wxICON_WARNING );
495 errorDlg.SetOKLabel( _( "Overwrite" ) );
496
497 return errorDlg.ShowModal() == wxID_OK;
498 }
499
500 return true;
501 } ) )
502 {
503 return 0; // canceled by user
504 }
505
506 if( newName == oldName )
507 return 0;
508
509 FOOTPRINT* footprint = nullptr;
510
511 if( fpID == m_frame->GetLoadedFPID() )
512 {
513 footprint = m_frame->GetBoard()->GetFirstFootprint();
514
515 if( footprint )
516 {
517 footprint->SetFPID( LIB_ID( libraryName, newName ) );
518
519 if( footprint->GetValue() == oldName )
520 footprint->SetValue( newName );
521
522 m_frame->OnModify();
523 m_frame->UpdateView();
524 }
525 }
526 else
527 {
528 footprint = m_frame->LoadFootprint( fpID );
529
530 if( footprint )
531 {
532 try
533 {
534 footprint->SetFPID( LIB_ID( libraryName, newName ) );
535
536 if( footprint->GetValue() == oldName )
537 footprint->SetValue( newName );
538
539 m_frame->SaveFootprintInLibrary( footprint, libraryName );
540
541 adapter->DeleteFootprint( libraryName, oldName );
542 }
543 catch( const IO_ERROR& ioe )
544 {
545 DisplayErrorMessage( m_frame, _( "Error renaming footprint" ), ioe.What() );
546 }
547 catch( ... )
548 {
549 // Best efforts...
550 }
551 }
552 }
553
554 if( footprint )
555 m_frame->RenameFootprintTab( fpID, LIB_ID( libraryName, newName ) );
556
557 wxDataViewItem treeItem = m_frame->GetLibTreeAdapter()->FindItem( fpID );
558
559 if( footprint )
560 {
561 m_frame->UpdateLibraryTree( treeItem, footprint );
562 m_frame->FocusOnLibID( LIB_ID( libraryName, newName ) );
563 }
564
565 return 0;
566}
567
568
570{
572 const LIB_ID fpID = frame->GetTargetFPID();
573
574 if( frame->DeleteFootprintFromLibrary( fpID, true ) )
575 {
576 // Close only the deleted footprint's tab, leaving the others open. Without a tab strip, fall
577 // back to clearing the shared board when the deleted footprint is the one on screen.
578 if( frame->GetTabsPanel() )
579 frame->CloseFootprintTab( fpID );
580 else if( fpID == frame->GetLoadedFPID() )
581 frame->Clear_Pcb( false );
582
583 frame->SyncLibraryTree( true );
584 }
585
586 return 0;
587}
588
589
591{
592 bool is_last_fp_from_brd = m_frame->IsCurrentFPFromBoard();
593
594 // The import opens in its own tab, leaving the open documents alone; only the legacy single-board
595 // path has to clear first
596 if( !m_frame->GetTabsPanel() && !m_frame->Clear_Pcb( true ) )
597 return -1; // this command is aborted
598
600
601 // A cancelled file dialog must leave the editor exactly as it was
602 if( !m_frame->ImportFootprint() )
603 return 0;
604
605 if( m_frame->GetBoard()->GetFirstFootprint() )
606 m_frame->GetBoard()->GetFirstFootprint()->ClearFlags();
607
608 frame()->ClearUndoRedoList();
609
610 // Update the save items if needed.
611 if( is_last_fp_from_brd )
612 {
613 m_frame->ReCreateMenuBar();
614 m_frame->ReCreateHToolbar();
615 }
616
617 m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
618 m_frame->OnModify();
619 return 0;
620}
621
622
624{
625 if( FOOTPRINT* fp = m_frame->GetBoard()->GetFirstFootprint() )
626 m_frame->ExportFootprint( fp );
627
628 return 0;
629}
630
631
633{
634 wxCHECK( m_frame, 0 );
635
636 const wxString libNickname = m_frame->GetTargetFPID().GetUniStringLibNickname();
637
638 if( libNickname.IsEmpty() )
639 {
640 m_frame->ShowInfoBarError( _( "Select a library to compare against another." ) );
641 return 0;
642 }
643
645
646 wxCHECK( adapter, 0 );
647
648 std::optional<LIBRARY_TABLE_ROW*> row = adapter->GetRow( libNickname );
649
650 if( !row )
651 {
652 m_frame->ShowInfoBarError( wxString::Format( _( "Library '%s' is not in the footprint "
653 "library table." ),
654 libNickname ) );
655 return 0;
656 }
657
658 // MakeAbsolute so relative table URIs resolve from the project dir, not
659 // the process cwd.
660 wxFileName currentFn = wxFileName::DirName(
661 LIBRARY_MANAGER::GetFullURI( *row, /*aSubstituted=*/true ) );
662 currentFn.MakeAbsolute();
663 const wxString currentPath = currentFn.GetPath();
664
665 wxDirDialog dlg( m_frame, _( "Choose .pretty Folder to Compare With" ), wxEmptyString,
666 wxDD_DIR_MUST_EXIST );
667
668 if( dlg.ShowModal() != wxID_OK )
669 return 0;
670
671 wxFileName otherFn = wxFileName::DirName( dlg.GetPath() );
672 otherFn.MakeAbsolute();
673
674 if( otherFn.GetDirs().IsEmpty()
675 || !otherFn.GetDirs().Last().EndsWith( wxS( ".pretty" ) ) )
676 {
677 m_frame->ShowInfoBarError(
678 _( "Select a KiCad footprint library folder (ends with .pretty)." ) );
679 return 0;
680 }
681
682 if( otherFn.SameAs( currentFn ) )
683 {
684 m_frame->ShowInfoBarError(
685 _( "Select a different .pretty folder than the active library." ) );
686 return 0;
687 }
688
689 const wxString otherPath = otherFn.GetPath();
690
691 std::vector<std::unique_ptr<FOOTPRINT>> beforeStorage;
692 std::vector<std::unique_ptr<FOOTPRINT>> afterStorage;
695
696 auto load =
697 [&]( const wxString& aPath,
698 std::vector<std::unique_ptr<FOOTPRINT>>& aOwners,
700 {
701 try
702 {
703 auto loaded = KICAD_DIFF::FP_LIB_DIFFER::LoadLibrary( aPath );
704 aOwners = std::move( loaded.first );
705 aMap = std::move( loaded.second );
706 return true;
707 }
708 catch( const IO_ERROR& ioe )
709 {
710 m_frame->ShowInfoBarError(
711 wxString::Format( _( "Failed to load %s: %s" ), aPath, ioe.What() ) );
712 }
713 catch( const std::exception& e )
714 {
715 m_frame->ShowInfoBarError(
716 wxString::Format( _( "Failed to load %s: %s" ), aPath,
717 wxString::FromUTF8( e.what() ) ) );
718 }
719
720 return false;
721 };
722
723 if( !load( currentPath, beforeStorage, beforeMap ) )
724 return 0;
725
726 if( !load( otherPath, afterStorage, afterMap ) )
727 return 0;
728
729 KICAD_DIFF::FP_LIB_DIFFER differ( beforeMap, afterMap, otherPath );
731
732 DIALOG_KICAD_DIFF dlgDiff( m_frame, currentPath, otherPath, result );
733 dlgDiff.ShowModal();
734
735 return 0;
736}
737
738
740{
741 // No check for multi selection since the context menu option must be hidden in that case
742 LIB_ID libId = m_frame->GetTargetFPID();
743
744 wxString libName = libId.GetLibNickname();
745 wxString libItemName = libId.GetLibItemName();
746 wxString path = wxEmptyString;
747
749 std::optional<wxString> optUri = manager.GetFullURI( LIBRARY_TABLE_TYPE::FOOTPRINT, libName, true );
750
751 if( !optUri )
752 return 0;
753
754 path = *optUri;
755
756 wxString fileExt = wxEmptyString;
757
758 // If selection is footprint
759 if( !libItemName.IsEmpty() )
761
762 wxFileName fileName( path, libItemName, fileExt );
763 wxString explorerCommand;
764
765 if( COMMON_SETTINGS* cfg = Pgm().GetCommonSettings() )
766 explorerCommand = cfg->m_System.file_explorer;
767
768 if( explorerCommand.IsEmpty() )
769 {
770 path = fileName.GetFullPath().BeforeLast( wxFileName::GetPathSeparator() );
771
772 if( !path.IsEmpty() && wxDirExists( path ) )
774
775 return 0;
776 }
777
778 if( !explorerCommand.EndsWith( "%F" ) )
779 {
780 wxMessageBox( _( "Missing/malformed file explorer argument '%F' in common settings." ) );
781 return 0;
782 }
783
784 wxString escapedFilePath = fileName.GetFullPath();
785 escapedFilePath.Replace( wxS( "\"" ), wxS( "_" ) );
786
787 wxString fileArg = wxEmptyString;
788 fileArg << '"' << escapedFilePath << '"';
789
790 explorerCommand.Replace( wxT( "%F" ), fileArg );
791
792 if( !explorerCommand.IsEmpty() )
793 wxExecute( explorerCommand );
794
795 return 0;
796}
797
798
800{
801 wxString fullEditorName = Pgm().GetTextEditor();
802
803 if( fullEditorName.IsEmpty() )
804 {
805 wxMessageBox( _( "No text editor selected in KiCad. Please choose one." ) );
806 return 0;
807 }
808
809 // No check for multi selection since the context menu option must be hidden in that case
810 LIB_ID libId = m_frame->GetLibTree()->GetSelectedLibId();
811
813
814 wxString libName = libId.GetLibNickname();
815 wxString libItemName = wxEmptyString;
816
817 std::optional<wxString> optUri = manager.GetFullURI( LIBRARY_TABLE_TYPE::FOOTPRINT, libName, true );
818
819 if( !optUri )
820 return 0;
821
822 libItemName = *optUri;
823 libItemName << wxFileName::GetPathSeparator();
824 libItemName << libId.GetLibItemName();
825 libItemName << '.' + FILEEXT::KiCadFootprintFileExtension;
826
827 if( !wxFileName::FileExists( libItemName ) )
828 return 0;
829
830 ExecuteFile( fullEditorName, libItemName.wc_str(), nullptr, false );
831
832 return 0;
833}
834
835
837{
838 if( FOOTPRINT* footprint = m_frame->GetBoard()->GetFirstFootprint() )
839 {
840 std::optional<wxString> url = GetFootprintDocumentationURL( *footprint );
841
842 if( !url.has_value() )
843 {
844 frame()->ShowInfoBarMsg( _( "No datasheet found in the footprint." ) );
845 }
846 else
847 {
848 // Only absolute URLs are supported
849 SEARCH_STACK* searchStack = nullptr;
850 GetAssociatedDocument( m_frame, *url, &m_frame->Prj(), searchStack,
851 { m_frame->GetBoard(), footprint } );
852 }
853 }
854 return 0;
855}
856
857
859{
860 m_frame->LoadFootprintFromLibrary( m_frame->GetLibTree()->GetSelectedLibId() );
861 return 0;
862}
863
864
866{
867 FOOTPRINT* footprint = m_frame->GetBoard()->GetFirstFootprint();
868
869 if( !footprint || !m_frame->IsCurrentFPFromBoard() )
870 {
871 wxBell();
872 return 0;
873 }
874
875 m_frame->LoadFootprintFromLibrary( footprint->GetFPID() );
876
877 if( !m_frame->IsLibraryTreeShown() )
878 m_frame->ToggleLibraryTree();
879
880 return 0;
881}
882
883
885{
886 m_frame->ToggleLayersManager();
887 return 0;
888}
889
890
892{
893 m_frame->ToggleProperties();
894 return 0;
895}
896
897
899{
900 // Check if called from tree context menu
902 {
903 LIB_ID treeLibId = m_frame->GetLibTree()->GetSelectedLibId();
904
905 // Check if a different footprint is selected in the tree
906 if( treeLibId.IsValid()
907 && ( !m_frame->GetBoard()->GetFirstFootprint()
908 || m_frame->GetBoard()->GetFirstFootprint()->GetFPID() != treeLibId ) )
909 {
910 // Edit properties directly from library without loading to canvas
912 return 0;
913 }
914 }
915
916 if( FOOTPRINT* footprint = m_frame->GetBoard()->GetFirstFootprint() )
917 {
918 getEditFrame<FOOTPRINT_EDIT_FRAME>()->OnEditItemRequest( footprint );
919 m_frame->GetCanvas()->Refresh();
920 }
921
922 return 0;
923}
924
925
927{
928 // Load the footprint from the library (without adding it to the canvas)
929 FOOTPRINT* libraryFootprint = m_frame->LoadFootprint( aLibId );
930
931 if( !libraryFootprint )
932 return;
933
934 // Create a temporary board to hold the footprint (required by the dialog)
935 std::unique_ptr<BOARD> tempBoard( new BOARD() );
936
937 // Set up the temp board with the current project and board settings.
938 // Use reference-only mode to avoid modifying the project's settings.
939 tempBoard->SetDesignSettings( m_frame->GetBoard()->GetDesignSettings() );
940 tempBoard->SetProject( &m_frame->Prj(), true );
941 tempBoard->SetBoardUse( BOARD_USE::FPHOLDER );
942 tempBoard->SynchronizeProperties();
943
944 // Create a copy to work with and add it to the temporary board
945 FOOTPRINT* tempFootprint = static_cast<FOOTPRINT*>( libraryFootprint->Clone() );
946 delete libraryFootprint;
947
948 tempBoard->Add( tempFootprint );
949 tempFootprint->SetParent( tempBoard.get() );
950
951 LIB_ID oldFPID = tempFootprint->GetFPID();
952
953 // Open the properties dialog
954 DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR dialog( m_frame, tempFootprint );
955
956 if( dialog.ShowQuasiModal() != wxID_OK )
957 return;
958
959 // Remove from temporary board before saving (to avoid double-delete)
960 tempBoard->Remove( tempFootprint );
961
962 // Save the modified footprint back to the library
964 wxString libName = aLibId.GetLibNickname();
965
966 try
967 {
968 adapter->SaveFootprint( libName, tempFootprint, true );
969
970 // Update the tree view
971 wxDataViewItem treeItem = m_frame->GetLibTreeAdapter()->FindItem( oldFPID );
972 m_frame->UpdateLibraryTree( treeItem, tempFootprint );
973 m_frame->SyncLibraryTree( true );
974
975 // Clean up
976 delete tempFootprint;
977 }
978 catch( const IO_ERROR& ioe )
979 {
980 delete tempFootprint;
981 DisplayError( m_frame, ioe.What() );
982 }
983}
984
985
987{
988 getEditFrame<FOOTPRINT_EDIT_FRAME>()->ShowPadPropertiesDialog( nullptr );
989 return 0;
990}
991
992
994{
996 DIALOG_CLEANUP_GRAPHICS dlg( editFrame, true );
997
998 dlg.ShowModal();
999 return 0;
1000}
1001
1002
1004{
1005 if( !m_checkerDialog )
1006 {
1008 m_checkerDialog->Show( true );
1009 }
1010 else // The dialog is just not visible (because the user has double clicked on an error item)
1011 {
1012 m_checkerDialog->Show( true );
1013 }
1014
1015 return 0;
1016}
1017
1018
1020{
1021 if( !m_checkerDialog )
1023
1024 if( !m_checkerDialog->IsShownOnScreen() )
1025 m_checkerDialog->Show( true );
1026
1027 m_checkerDialog->SelectMarker( aMarker );
1028}
1029
1030
1032{
1033 if( m_checkerDialog )
1034 {
1035 m_checkerDialog->Destroy();
1036 m_checkerDialog = nullptr;
1037 }
1038}
1039
1040
1042{
1043 FOOTPRINT* footprint = board()->Footprints().front();
1044 int errors = 0;
1045 wxString details;
1046
1047 // Repair duplicate IDs and missing nets.
1048 std::set<KIID> ids;
1049 int duplicates = 0;
1050
1051 auto processItem =
1052 [&]( EDA_ITEM* aItem )
1053 {
1054 if( ids.count( aItem->m_Uuid ) )
1055 {
1056 duplicates++;
1057 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( aItem ) )
1058 boardItem->ResetUuid();
1059 }
1060
1061 ids.insert( aItem->m_Uuid );
1062 };
1063
1064 // Footprint IDs are the most important, so give them the first crack at "claiming" a
1065 // particular KIID.
1066
1067 processItem( footprint );
1068
1069 // After that the principal use is for DRC marker pointers, which are most likely to pads.
1070
1071 for( PAD* pad : footprint->Pads() )
1072 processItem( pad );
1073
1074 // From here out I don't think order matters much.
1075
1076 processItem( &footprint->Reference() );
1077 processItem( &footprint->Value() );
1078
1079 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1080 processItem( item );
1081
1082 for( ZONE* zone : footprint->Zones() )
1083 processItem( zone );
1084
1085 for( PCB_GROUP* group : footprint->Groups() )
1086 processItem( group );
1087
1088 if( duplicates )
1089 {
1090 errors += duplicates;
1091 details += wxString::Format( _( "%d duplicate IDs replaced.\n" ), duplicates );
1092 }
1093
1094 if( errors )
1095 {
1096 m_frame->OnModify();
1097
1098 wxString msg = wxString::Format( _( "%d potential problems repaired." ), errors );
1099 DisplayInfoMessage( m_frame, msg, details );
1100 }
1101 else
1102 {
1103 DisplayInfoMessage( m_frame, _( "No footprint problems found." ) );
1104 }
1105
1106 return 0;
1107}
1108
1109
1110// The appearance panel also claims Ctrl+Tab for layer-preset cycling, so it wins when focused.
1112{
1113 wxWindow* appearance = aFrame->GetAppearancePanel();
1114
1115 if( !appearance )
1116 return false;
1117
1118 for( wxWindow* focus = wxWindow::FindFocus(); focus; focus = focus->GetParent() )
1119 {
1120 if( focus == appearance )
1121 return true;
1122 }
1123
1124 return false;
1125}
1126
1127
1129{
1131 return 0;
1132
1133 m_frame->AdvanceFootprintTab( true );
1134 return 0;
1135}
1136
1137
1139{
1141 return 0;
1142
1143 m_frame->AdvanceFootprintTab( false );
1144 return 0;
1145}
1146
1147
1149{
1150 m_frame->CloseActiveFootprintTab();
1151 return 0;
1152}
1153
1154
1156{
1157 // clang-format off
1166
1172
1177
1181
1184
1187
1191
1196 // clang-format on
1197
1198 // Line modes for the footprint editor: explicit modes, next-mode, and toolbar sync
1204}
1205
1207{
1208 LEADER_MODE mode = aEvent.Parameter<LEADER_MODE>();
1209 GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode = mode;
1210 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1212 return 0;
1213}
1214
1216{
1218
1219 if( !f )
1220 return 0;
1221
1222 LEADER_MODE mode = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
1223
1224 switch( mode )
1225 {
1228 default:
1230 }
1231
1232 return 0;
1233}
@ FPHOLDER
Definition board.h:365
static TOOL_ACTION openWithTextEditor
Definition actions.h:64
static TOOL_ACTION revert
Definition actions.h:58
static TOOL_ACTION saveAs
Definition actions.h:55
static TOOL_ACTION openDirectory
Definition actions.h:65
static TOOL_ACTION showDatasheet
Definition actions.h:263
static TOOL_ACTION save
Definition actions.h:54
static TOOL_ACTION zoomFitScreen
Definition actions.h:138
static TOOL_ACTION showProperties
Definition actions.h:262
static TOOL_ACTION refreshPreview
Definition actions.h:155
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
void BuildListOfNets()
Definition board.h:1061
const FOOTPRINTS & Footprints() const
Definition board.h:421
File-compare dialog (Phase 7).
int ShowModal() override
void SelectToolbarAction(const TOOL_ACTION &aAction)
Select the given action in the toolbar group which contains it, if any.
KIGFX::VIEW_CONTROLS * GetViewControls() const
Return a pointer to the #VIEW_CONTROLS instance used in the panel.
void ForceRefresh()
Force a redraw.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:154
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
int Revert(const TOOL_EVENT &aEvent)
int OpenWithTextEditor(const TOOL_EVENT &aEvent)
int PasteFootprint(const TOOL_EVENT &aEvent)
DIALOG_FOOTPRINT_CHECKER * m_checkerDialog
int CutCopyFootprint(const TOOL_EVENT &aEvent)
int EditFootprint(const TOOL_EVENT &aEvent)
int CloseTab(const TOOL_EVENT &aEvent)
int ToggleProperties(const TOOL_EVENT &aEvent)
int Save(const TOOL_EVENT &aEvent)
int OpenDirectory(const TOOL_EVENT &aEvent)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int CreateFootprint(const TOOL_EVENT &aEvent)
int NextTab(const TOOL_EVENT &aEvent)
int NewFootprint(const TOOL_EVENT &aEvent)
int CompareLibraryWithFile(const TOOL_EVENT &aEvent)
Diff the currently-selected footprint library against another .pretty directory.
int DefaultPadProperties(const TOOL_EVENT &aEvent)
Edit the properties used for new pad creation.
void CrossProbe(const PCB_MARKER *aMarker)
int ChangeLineMode(const TOOL_EVENT &aEvent)
int ImportFootprint(const TOOL_EVENT &aEvent)
int CleanupGraphics(const TOOL_EVENT &aEvent)
int ShowDatasheet(const TOOL_EVENT &aEvent)
void tryToSaveFootprintInLibrary(FOOTPRINT &aFootprint, const LIB_ID &aLibId)
Try to save the footprint in the library, if it is valid and writable.
int PrevTab(const TOOL_EVENT &aEvent)
int Properties(const TOOL_EVENT &aEvent)
int ToggleLayersManager(const TOOL_EVENT &aEvent)
int RepairFootprint(const TOOL_EVENT &aEvent)
int RenameFootprint(const TOOL_EVENT &aEvent)
int DuplicateFootprint(const TOOL_EVENT &aEvent)
std::unique_ptr< FOOTPRINT > m_copiedFootprint
bool Init() override
Init() is called once upon a registration of the tool.
int OnAngleSnapModeChanged(const TOOL_EVENT &aEvent)
int ExportFootprint(const TOOL_EVENT &aEvent)
int EditLibraryFootprint(const TOOL_EVENT &aEvent)
void editFootprintPropertiesFromLibrary(const LIB_ID &aLibId)
int DeleteFootprint(const TOOL_EVENT &aEvent)
int SaveAs(const TOOL_EVENT &aEvent)
void setTransitions() override
< Set up handlers for various events.
int CheckFootprint(const TOOL_EVENT &aEvent)
An interface to the global shared library manager that is schematic-specific and linked to one projec...
SAVE_T SaveFootprint(const wxString &aNickname, const FOOTPRINT *aFootprint, bool aOverwrite=true)
Write aFootprint to an existing library given by aNickname.
void SetPosition(const VECTOR2I &aPos) override
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:445
EDA_ITEM * Clone() const override
Invoke a function on all children.
const LIB_ID & GetFPID() const
Definition footprint.h:444
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()
Diff two .pretty footprint library directories.
static std::pair< std::vector< std::unique_ptr< FOOTPRINT > >, FOOTPRINT_MAP > LoadLibrary(const wxString &aPrettyPath)
Load a .pretty directory into a FOOTPRINT_MAP.
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
std::map< wxString, const FOOTPRINT * > FOOTPRINT_MAP
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:38
int ShowModal() override
Definition kidialog.cpp:89
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:87
virtual void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true)=0
Move the graphic crosshair cursor to the requested position expressed in world coordinates.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
virtual bool ShowModal(wxString *aResult=nullptr, wxWindow *aResultantFocusWindow=nullptr)
Show this wxFrame as if it were a modal dialog, with all other instantiated wxFrames disabled until t...
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
Module editor specific tools.
bool RenameLibrary(const wxString &aTitle, const wxString &aName, std::function< bool(const wxString &aNewName)> aValidator)
void AddContextMenuItems(CONDITIONAL_MENU *aMenu)
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false)
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
const wxString GetUniStringLibNickname() const
Definition lib_id.h:84
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
Definition pad.h:61
static TOOL_ACTION lineModeFree
Unconstrained angle mode (icon lines_any)
static TOOL_ACTION deleteFootprint
static TOOL_ACTION renameFootprint
static TOOL_ACTION showLayersManager
static TOOL_ACTION createFootprint
static TOOL_ACTION editFootprint
static TOOL_ACTION exportFootprint
static TOOL_ACTION editTextAndGraphics
static TOOL_ACTION lineMode45
45-degree-or-orthogonal mode (icon hv45mode)
static TOOL_ACTION angleSnapModeChanged
Notification event when angle mode changes.
static TOOL_ACTION newFootprint
static TOOL_ACTION defaultPadProperties
static TOOL_ACTION prevFootprintTab
static TOOL_ACTION importFootprint
static TOOL_ACTION pasteFootprint
static TOOL_ACTION footprintProperties
static TOOL_ACTION lineMode90
90-degree-only mode (icon lines90)
static TOOL_ACTION closeFootprintTab
static TOOL_ACTION nextFootprintTab
static TOOL_ACTION checkFootprint
static TOOL_ACTION editLibFpInFpEditor
static TOOL_ACTION duplicateFootprint
static TOOL_ACTION cutFootprint
static TOOL_ACTION repairFootprint
static TOOL_ACTION compareFpLibraryWithFile
static TOOL_ACTION copyFootprint
static TOOL_ACTION cleanupGraphics
APPEARANCE_CONTROLS * GetAppearancePanel()
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
T * frame() const
KIGFX::PCB_VIEW * view() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
FOOTPRINT * footprint() const
virtual const wxString & GetTextEditor(bool aCanShowFileChooser=true)
Return the path to the preferred text editor application.
Definition pgm_base.cpp:219
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:126
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Look for files in a number of paths.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
Generic, UI-independent tool event.
Definition tool_event.h:167
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
bool empty() const
Definition utf8.h:105
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:77
bool HasCloseButton() const
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:245
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:146
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
#define _(s)
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject, SEARCH_STACK *aPaths, std::vector< EMBEDDED_FILES * > aFilesStack)
Open a document (file) with the suitable browser.
Definition eda_doc.cpp:59
This file is part of the common library.
static bool appearancePanelHasFocus(FOOTPRINT_EDIT_FRAME *aFrame)
@ FRAME_FOOTPRINT_WIZARD
Definition frame_type.h:42
std::optional< wxString > GetFootprintDocumentationURL(const FOOTPRINT &aFootprint)
Get a URL to the documentation for a LIB_ID in a #FP_LIB_TABLE.
LEADER_MODE
The kind of the leader line.
@ DEG45
45 Degree only
@ DIRECT
Unconstrained point-to-point.
@ DEG90
90 Degree only
int ExecuteFile(const wxString &aEditorName, const wxString &aFileName, wxProcess *aCallback, bool aFileForKicad)
Call the executable file aEditorName with the parameter aFileName.
Definition gestfich.cpp:161
static const std::string KiCadFootprintFileExtension
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
Class to handle a set of BOARD_ITEMs.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
T * GetAppSettings(const char *aFilename)
The full set of changes between two parsed documents of one type.
std::string path
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
Definition of file extensions used in Kicad.