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 m_frame->SaveFootprint( &aFootprint );
207 m_frame->ClearModify();
208 }
209 }
210}
211
212
214{
215 const LIB_ID selected = m_frame->GetTargetFPID();
216 const wxString libraryName = selected.GetUniStringLibNickname();
217
218 if( !m_frame->BeginNewFootprint( libraryName ) )
219 return 0;
220
221 FOOTPRINT* newFootprint = m_frame->CreateNewFootprint( wxEmptyString, libraryName );
222
223 if( !newFootprint )
224 return 0;
225
226 // Give the new footprint a resolvable identity so it opens in its own tab instead of
227 // overwriting the active one. The legacy single-board path leaves the nickname empty.
228 if( m_frame->GetTabsPanel() && !libraryName.IsEmpty() )
229 newFootprint->SetFPID( LIB_ID( libraryName, newFootprint->GetFPID().GetLibItemName() ) );
230
232 m_frame->AddFootprintToBoard( newFootprint );
233
234 // Initialize data relative to nets and netclasses (for a new footprint the defaults are
235 // used). This is mandatory to handle and draw pads.
237 newFootprint->SetPosition( VECTOR2I( 0, 0 ) );
238 newFootprint->ClearFlags();
239
240 m_frame->Zoom_Automatique( false );
241 m_frame->GetScreen()->SetContentModified();
242
243 tryToSaveFootprintInLibrary( *newFootprint, selected );
244
245 m_frame->UpdateView();
246 m_frame->GetCanvas()->ForceRefresh();
247 m_frame->Update3DView( true, true );
248
249 m_frame->SyncLibraryTree( false );
250 return 0;
251}
252
253
255{
256 LIB_ID selected = m_frame->GetLibTree()->GetSelectedLibId();
257
258 if( m_frame->IsContentModified() )
259 {
260 if( !HandleUnsavedChanges( m_frame, _( "The current footprint has been modified. "
261 "Save changes?" ),
262 [&]() -> bool
263 {
264 return m_frame->SaveFootprint( footprint() );
265 } ) )
266 {
267 return 0;
268 }
269 }
270
271 if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_FOOTPRINT_WIZARD, true, m_frame ) )
272 {
273 FOOTPRINT_WIZARD_FRAME* wizard = static_cast<FOOTPRINT_WIZARD_FRAME*>( frame );
274
275 if( wizard->ShowModal( nullptr, m_frame ) )
276 {
277 // Creates the new footprint from python script wizard
278 FOOTPRINT* newFootprint = wizard->GetBuiltFootprint();
279
280 if( newFootprint ) // i.e. if create footprint command is OK
281 {
282 m_frame->Clear_Pcb( false );
283
285 // Add the new object to board
286 m_frame->AddFootprintToBoard( newFootprint );
287
288 // Initialize data relative to nets and netclasses (for a new footprint the
289 // defaults are used). This is mandatory to handle and draw pads.
291 newFootprint->SetPosition( VECTOR2I( 0, 0 ) );
292 newFootprint->ClearFlags();
293
294 m_frame->Zoom_Automatique( false );
295 m_frame->GetScreen()->SetContentModified();
296 m_frame->OnModify();
297
298 tryToSaveFootprintInLibrary( *newFootprint, selected );
299
300 m_frame->UpdateView();
301 canvas()->Refresh();
302 m_frame->Update3DView( true, true );
303
304 m_frame->SyncLibraryTree( false );
305 }
306 }
307
308 wizard->Destroy();
309 }
310
311 return 0;
312}
313
314
316{
317 if( !footprint() ) // no loaded footprint
318 return 0;
319
320 if( m_frame->GetTargetFPID() == m_frame->GetLoadedFPID() )
321 {
322 if( m_frame->SaveFootprint( footprint() ) )
323 {
324 view()->Update( footprint() );
325
326 canvas()->ForceRefresh();
327 m_frame->ClearModify();
328 m_frame->UpdateTitle();
329 }
330 }
331
332 m_frame->RefreshLibraryTree();
333 return 0;
334}
335
336
338{
339 if( m_frame->GetTargetFPID().GetLibItemName().empty() )
340 {
342
343 // Save Library As
344 const wxString& src_libNickname = m_frame->GetTargetFPID().GetLibNickname();
345 std::optional<wxString> optUri = manager.GetFullURI( LIBRARY_TABLE_TYPE::FOOTPRINT, src_libNickname, true );
346 wxCHECK( optUri, 0 );
347
348 if( m_frame->SaveLibraryAs( *optUri ) )
349 m_frame->SyncLibraryTree( true );
350 }
351 else if( m_frame->GetTargetFPID() == m_frame->GetLoadedFPID() )
352 {
353 // Save Footprint As
354 if( footprint() && m_frame->SaveFootprintAs( footprint() ) )
355 {
356 view()->Update( footprint() );
357 m_frame->ClearModify();
358
359 // Get rid of the save-will-update-board-only (or any other dismissable warning)
360 WX_INFOBAR* infobar = m_frame->GetInfoBar();
361
362 if( infobar->IsShownOnScreen() && infobar->HasCloseButton() )
363 infobar->Dismiss();
364
365 canvas()->ForceRefresh();
366 m_frame->SyncLibraryTree( true );
367 }
368 }
369 else
370 {
371 // Save Selected Footprint As
372 FOOTPRINT* footprint = m_frame->LoadFootprint( m_frame->GetTargetFPID() );
373
374 if( footprint && m_frame->SaveFootprintAs( footprint ) )
375 {
376 m_frame->SyncLibraryTree( true );
377 m_frame->FocusOnLibID( footprint->GetFPID() );
378 }
379 }
380
381 m_frame->RefreshLibraryTree();
382 return 0;
383}
384
385
387{
388 getEditFrame<FOOTPRINT_EDIT_FRAME>()->RevertFootprint();
389 return 0;
390}
391
392
394{
395 LIB_ID fpID = m_frame->GetLibTree()->GetSelectedLibId();
396
397 if( fpID == m_frame->GetLoadedFPID() )
398 {
399 m_copiedFootprint = std::make_unique<FOOTPRINT>( *m_frame->GetBoard()->GetFirstFootprint() );
400 m_copiedFootprint->SetParent( nullptr );
401 }
402 else
403 {
404 m_copiedFootprint.reset( m_frame->LoadFootprint( fpID ) );
405 }
406
407 if( aEvent.IsAction( &PCB_ACTIONS::cutFootprint ) )
408 DeleteFootprint( aEvent );
409
410 return 0;
411}
412
413
415{
416 if( m_copiedFootprint && !m_frame->GetLibTree()->GetSelectedLibId().GetLibNickname().empty() )
417 {
418 wxString newLib = m_frame->GetLibTree()->GetSelectedLibId().GetLibNickname();
419 wxString newName = m_copiedFootprint->GetFPID().GetLibItemName();
420
421 while( PROJECT_PCB::FootprintLibAdapter( &m_frame->Prj() )->FootprintExists( newLib, newName ) )
422 newName += _( "_copy" );
423
424 m_copiedFootprint->SetFPID( LIB_ID( newLib, newName ) );
425 m_frame->SaveFootprintInLibrary( m_copiedFootprint.get(), newLib );
426
427 m_frame->SyncLibraryTree( true );
428 m_frame->LoadFootprintFromLibrary( m_copiedFootprint->GetFPID() );
429 m_frame->FocusOnLibID( m_copiedFootprint->GetFPID() );
430 m_frame->RefreshLibraryTree();
431 }
432
433 return 0;
434}
435
436
438{
439 LIB_ID fpID = m_frame->GetLibTree()->GetSelectedLibId();
441
442 if( fpID == m_frame->GetLoadedFPID() )
443 footprint = new FOOTPRINT( *m_frame->GetBoard()->GetFirstFootprint() );
444 else
445 footprint = m_frame->LoadFootprint( m_frame->GetTargetFPID() );
446
447 if( footprint && m_frame->DuplicateFootprint( footprint ) )
448 {
449 m_frame->SyncLibraryTree( true );
450 m_frame->LoadFootprintFromLibrary( footprint->GetFPID() );
451 m_frame->FocusOnLibID( footprint->GetFPID() );
452 m_frame->RefreshLibraryTree();
453 }
454
455 return 0;
456}
457
458
460{
463
464 LIB_ID fpID = m_frame->GetLibTree()->GetSelectedLibId();
465 wxString libraryName = fpID.GetLibNickname();
466 wxString oldName = fpID.GetLibItemName();
467 wxString newName;
468 wxString msg;
469
470 if( !libTool->RenameLibrary( _( "Change Footprint Name" ), oldName,
471 [&]( const wxString& aNewName )
472 {
473 newName = aNewName;
474
475 if( newName.IsEmpty() )
476 {
477 wxMessageBox( _( "Footprint must have a name." ) );
478 return false;
479 }
480
481 // If no change, accept it without prompting
482 if( oldName != newName && adapter->FootprintExists( libraryName, newName ) )
483 {
484 msg = wxString::Format( _( "Footprint '%s' already exists in library '%s'." ),
485 newName, libraryName );
486
487 KIDIALOG errorDlg( m_frame, msg, _( "Confirmation" ),
488 wxOK | wxCANCEL | wxICON_WARNING );
489 errorDlg.SetOKLabel( _( "Overwrite" ) );
490
491 return errorDlg.ShowModal() == wxID_OK;
492 }
493
494 return true;
495 } ) )
496 {
497 return 0; // canceled by user
498 }
499
500 if( newName == oldName )
501 return 0;
502
503 FOOTPRINT* footprint = nullptr;
504
505 if( fpID == m_frame->GetLoadedFPID() )
506 {
507 footprint = m_frame->GetBoard()->GetFirstFootprint();
508
509 if( footprint )
510 {
511 footprint->SetFPID( LIB_ID( libraryName, newName ) );
512
513 if( footprint->GetValue() == oldName )
514 footprint->SetValue( newName );
515
516 m_frame->OnModify();
517 m_frame->UpdateView();
518 }
519 }
520 else
521 {
522 footprint = m_frame->LoadFootprint( fpID );
523
524 if( footprint )
525 {
526 try
527 {
528 footprint->SetFPID( LIB_ID( libraryName, newName ) );
529
530 if( footprint->GetValue() == oldName )
531 footprint->SetValue( newName );
532
533 m_frame->SaveFootprintInLibrary( footprint, libraryName );
534
535 adapter->DeleteFootprint( libraryName, oldName );
536 }
537 catch( const IO_ERROR& ioe )
538 {
539 DisplayErrorMessage( m_frame, _( "Error renaming footprint" ), ioe.What() );
540 }
541 catch( ... )
542 {
543 // Best efforts...
544 }
545 }
546 }
547
548 if( footprint )
549 m_frame->RenameFootprintTab( fpID, LIB_ID( libraryName, newName ) );
550
551 wxDataViewItem treeItem = m_frame->GetLibTreeAdapter()->FindItem( fpID );
552
553 if( footprint )
554 {
555 m_frame->UpdateLibraryTree( treeItem, footprint );
556 m_frame->FocusOnLibID( LIB_ID( libraryName, newName ) );
557 }
558
559 return 0;
560}
561
562
564{
566 const LIB_ID fpID = frame->GetTargetFPID();
567
568 if( frame->DeleteFootprintFromLibrary( fpID, true ) )
569 {
570 // Close only the deleted footprint's tab, leaving the others open. Without a tab strip, fall
571 // back to clearing the shared board when the deleted footprint is the one on screen.
572 if( frame->GetTabsPanel() )
573 frame->CloseFootprintTab( fpID );
574 else if( fpID == frame->GetLoadedFPID() )
575 frame->Clear_Pcb( false );
576
577 frame->SyncLibraryTree( true );
578 }
579
580 return 0;
581}
582
583
585{
586 bool is_last_fp_from_brd = m_frame->IsCurrentFPFromBoard();
587
588 if( !m_frame->Clear_Pcb( true ) )
589 return -1; // this command is aborted
590
592 m_frame->ImportFootprint();
593
594 if( m_frame->GetBoard()->GetFirstFootprint() )
595 m_frame->GetBoard()->GetFirstFootprint()->ClearFlags();
596
597 frame()->ClearUndoRedoList();
598
599 // Update the save items if needed.
600 if( is_last_fp_from_brd )
601 {
602 m_frame->ReCreateMenuBar();
603 m_frame->ReCreateHToolbar();
604 }
605
606 m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
607 m_frame->OnModify();
608 return 0;
609}
610
611
613{
614 if( FOOTPRINT* fp = m_frame->GetBoard()->GetFirstFootprint() )
615 m_frame->ExportFootprint( fp );
616
617 return 0;
618}
619
620
622{
623 wxCHECK( m_frame, 0 );
624
625 const wxString libNickname = m_frame->GetTargetFPID().GetUniStringLibNickname();
626
627 if( libNickname.IsEmpty() )
628 {
629 m_frame->ShowInfoBarError( _( "Select a library to compare against another." ) );
630 return 0;
631 }
632
634
635 wxCHECK( adapter, 0 );
636
637 std::optional<LIBRARY_TABLE_ROW*> row = adapter->GetRow( libNickname );
638
639 if( !row )
640 {
641 m_frame->ShowInfoBarError( wxString::Format( _( "Library '%s' is not in the footprint "
642 "library table." ),
643 libNickname ) );
644 return 0;
645 }
646
647 // MakeAbsolute so relative table URIs resolve from the project dir, not
648 // the process cwd.
649 wxFileName currentFn = wxFileName::DirName(
650 LIBRARY_MANAGER::GetFullURI( *row, /*aSubstituted=*/true ) );
651 currentFn.MakeAbsolute();
652 const wxString currentPath = currentFn.GetPath();
653
654 wxDirDialog dlg( m_frame, _( "Choose .pretty Folder to Compare With" ), wxEmptyString,
655 wxDD_DIR_MUST_EXIST );
656
657 if( dlg.ShowModal() != wxID_OK )
658 return 0;
659
660 wxFileName otherFn = wxFileName::DirName( dlg.GetPath() );
661 otherFn.MakeAbsolute();
662
663 if( otherFn.GetDirs().IsEmpty()
664 || !otherFn.GetDirs().Last().EndsWith( wxS( ".pretty" ) ) )
665 {
666 m_frame->ShowInfoBarError(
667 _( "Select a KiCad footprint library folder (ends with .pretty)." ) );
668 return 0;
669 }
670
671 if( otherFn.SameAs( currentFn ) )
672 {
673 m_frame->ShowInfoBarError(
674 _( "Select a different .pretty folder than the active library." ) );
675 return 0;
676 }
677
678 const wxString otherPath = otherFn.GetPath();
679
680 std::vector<std::unique_ptr<FOOTPRINT>> beforeStorage;
681 std::vector<std::unique_ptr<FOOTPRINT>> afterStorage;
684
685 auto load =
686 [&]( const wxString& aPath,
687 std::vector<std::unique_ptr<FOOTPRINT>>& aOwners,
689 {
690 try
691 {
692 auto loaded = KICAD_DIFF::FP_LIB_DIFFER::LoadLibrary( aPath );
693 aOwners = std::move( loaded.first );
694 aMap = std::move( loaded.second );
695 return true;
696 }
697 catch( const IO_ERROR& ioe )
698 {
699 m_frame->ShowInfoBarError(
700 wxString::Format( _( "Failed to load %s: %s" ), aPath, ioe.What() ) );
701 }
702 catch( const std::exception& e )
703 {
704 m_frame->ShowInfoBarError(
705 wxString::Format( _( "Failed to load %s: %s" ), aPath,
706 wxString::FromUTF8( e.what() ) ) );
707 }
708
709 return false;
710 };
711
712 if( !load( currentPath, beforeStorage, beforeMap ) )
713 return 0;
714
715 if( !load( otherPath, afterStorage, afterMap ) )
716 return 0;
717
718 KICAD_DIFF::FP_LIB_DIFFER differ( beforeMap, afterMap, otherPath );
720
721 DIALOG_KICAD_DIFF dlgDiff( m_frame, currentPath, otherPath, result );
722 dlgDiff.ShowModal();
723
724 return 0;
725}
726
727
729{
730 // No check for multi selection since the context menu option must be hidden in that case
731 LIB_ID libId = m_frame->GetTargetFPID();
732
733 wxString libName = libId.GetLibNickname();
734 wxString libItemName = libId.GetLibItemName();
735 wxString path = wxEmptyString;
736
738 std::optional<wxString> optUri = manager.GetFullURI( LIBRARY_TABLE_TYPE::FOOTPRINT, libName, true );
739
740 if( !optUri )
741 return 0;
742
743 path = *optUri;
744
745 wxString fileExt = wxEmptyString;
746
747 // If selection is footprint
748 if( !libItemName.IsEmpty() )
750
751 wxFileName fileName( path, libItemName, fileExt );
752 wxString explorerCommand;
753
754 if( COMMON_SETTINGS* cfg = Pgm().GetCommonSettings() )
755 explorerCommand = cfg->m_System.file_explorer;
756
757 if( explorerCommand.IsEmpty() )
758 {
759 path = fileName.GetFullPath().BeforeLast( wxFileName::GetPathSeparator() );
760
761 if( !path.IsEmpty() && wxDirExists( path ) )
763
764 return 0;
765 }
766
767 if( !explorerCommand.EndsWith( "%F" ) )
768 {
769 wxMessageBox( _( "Missing/malformed file explorer argument '%F' in common settings." ) );
770 return 0;
771 }
772
773 wxString escapedFilePath = fileName.GetFullPath();
774 escapedFilePath.Replace( wxS( "\"" ), wxS( "_" ) );
775
776 wxString fileArg = wxEmptyString;
777 fileArg << '"' << escapedFilePath << '"';
778
779 explorerCommand.Replace( wxT( "%F" ), fileArg );
780
781 if( !explorerCommand.IsEmpty() )
782 wxExecute( explorerCommand );
783
784 return 0;
785}
786
787
789{
790 wxString fullEditorName = Pgm().GetTextEditor();
791
792 if( fullEditorName.IsEmpty() )
793 {
794 wxMessageBox( _( "No text editor selected in KiCad. Please choose one." ) );
795 return 0;
796 }
797
798 // No check for multi selection since the context menu option must be hidden in that case
799 LIB_ID libId = m_frame->GetLibTree()->GetSelectedLibId();
800
802
803 wxString libName = libId.GetLibNickname();
804 wxString libItemName = wxEmptyString;
805
806 std::optional<wxString> optUri = manager.GetFullURI( LIBRARY_TABLE_TYPE::FOOTPRINT, libName, true );
807
808 if( !optUri )
809 return 0;
810
811 libItemName = *optUri;
812 libItemName << wxFileName::GetPathSeparator();
813 libItemName << libId.GetLibItemName();
814 libItemName << '.' + FILEEXT::KiCadFootprintFileExtension;
815
816 if( !wxFileName::FileExists( libItemName ) )
817 return 0;
818
819 ExecuteFile( fullEditorName, libItemName.wc_str(), nullptr, false );
820
821 return 0;
822}
823
824
826{
827 if( FOOTPRINT* footprint = m_frame->GetBoard()->GetFirstFootprint() )
828 {
829 std::optional<wxString> url = GetFootprintDocumentationURL( *footprint );
830
831 if( !url.has_value() )
832 {
833 frame()->ShowInfoBarMsg( _( "No datasheet found in the footprint." ) );
834 }
835 else
836 {
837 // Only absolute URLs are supported
838 SEARCH_STACK* searchStack = nullptr;
839 GetAssociatedDocument( m_frame, *url, &m_frame->Prj(), searchStack,
840 { m_frame->GetBoard(), footprint } );
841 }
842 }
843 return 0;
844}
845
846
848{
849 m_frame->LoadFootprintFromLibrary( m_frame->GetLibTree()->GetSelectedLibId() );
850 return 0;
851}
852
853
855{
856 FOOTPRINT* footprint = m_frame->GetBoard()->GetFirstFootprint();
857
858 if( !footprint || !m_frame->IsCurrentFPFromBoard() )
859 {
860 wxBell();
861 return 0;
862 }
863
864 m_frame->LoadFootprintFromLibrary( footprint->GetFPID() );
865
866 if( !m_frame->IsLibraryTreeShown() )
867 m_frame->ToggleLibraryTree();
868
869 return 0;
870}
871
872
874{
875 m_frame->ToggleLayersManager();
876 return 0;
877}
878
879
881{
882 m_frame->ToggleProperties();
883 return 0;
884}
885
886
888{
889 // Check if called from tree context menu
891 {
892 LIB_ID treeLibId = m_frame->GetLibTree()->GetSelectedLibId();
893
894 // Check if a different footprint is selected in the tree
895 if( treeLibId.IsValid()
896 && ( !m_frame->GetBoard()->GetFirstFootprint()
897 || m_frame->GetBoard()->GetFirstFootprint()->GetFPID() != treeLibId ) )
898 {
899 // Edit properties directly from library without loading to canvas
901 return 0;
902 }
903 }
904
905 if( FOOTPRINT* footprint = m_frame->GetBoard()->GetFirstFootprint() )
906 {
907 getEditFrame<FOOTPRINT_EDIT_FRAME>()->OnEditItemRequest( footprint );
908 m_frame->GetCanvas()->Refresh();
909 }
910
911 return 0;
912}
913
914
916{
917 // Load the footprint from the library (without adding it to the canvas)
918 FOOTPRINT* libraryFootprint = m_frame->LoadFootprint( aLibId );
919
920 if( !libraryFootprint )
921 return;
922
923 // Create a temporary board to hold the footprint (required by the dialog)
924 std::unique_ptr<BOARD> tempBoard( new BOARD() );
925
926 // Set up the temp board with the current project and board settings.
927 // Use reference-only mode to avoid modifying the project's settings.
928 tempBoard->SetDesignSettings( m_frame->GetBoard()->GetDesignSettings() );
929 tempBoard->SetProject( &m_frame->Prj(), true );
930 tempBoard->SetBoardUse( BOARD_USE::FPHOLDER );
931 tempBoard->SynchronizeProperties();
932
933 // Create a copy to work with and add it to the temporary board
934 FOOTPRINT* tempFootprint = static_cast<FOOTPRINT*>( libraryFootprint->Clone() );
935 delete libraryFootprint;
936
937 tempBoard->Add( tempFootprint );
938 tempFootprint->SetParent( tempBoard.get() );
939
940 LIB_ID oldFPID = tempFootprint->GetFPID();
941
942 // Open the properties dialog
943 DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR dialog( m_frame, tempFootprint );
944
945 if( dialog.ShowQuasiModal() != wxID_OK )
946 return;
947
948 // Remove from temporary board before saving (to avoid double-delete)
949 tempBoard->Remove( tempFootprint );
950
951 // Save the modified footprint back to the library
953 wxString libName = aLibId.GetLibNickname();
954
955 try
956 {
957 adapter->SaveFootprint( libName, tempFootprint, true );
958
959 // Update the tree view
960 wxDataViewItem treeItem = m_frame->GetLibTreeAdapter()->FindItem( oldFPID );
961 m_frame->UpdateLibraryTree( treeItem, tempFootprint );
962 m_frame->SyncLibraryTree( true );
963
964 // Clean up
965 delete tempFootprint;
966 }
967 catch( const IO_ERROR& ioe )
968 {
969 delete tempFootprint;
970 DisplayError( m_frame, ioe.What() );
971 }
972}
973
974
976{
977 getEditFrame<FOOTPRINT_EDIT_FRAME>()->ShowPadPropertiesDialog( nullptr );
978 return 0;
979}
980
981
983{
985 DIALOG_CLEANUP_GRAPHICS dlg( editFrame, true );
986
987 dlg.ShowModal();
988 return 0;
989}
990
991
993{
994 if( !m_checkerDialog )
995 {
997 m_checkerDialog->Show( true );
998 }
999 else // The dialog is just not visible (because the user has double clicked on an error item)
1000 {
1001 m_checkerDialog->Show( true );
1002 }
1003
1004 return 0;
1005}
1006
1007
1009{
1010 if( !m_checkerDialog )
1012
1013 if( !m_checkerDialog->IsShownOnScreen() )
1014 m_checkerDialog->Show( true );
1015
1016 m_checkerDialog->SelectMarker( aMarker );
1017}
1018
1019
1021{
1022 if( m_checkerDialog )
1023 {
1024 m_checkerDialog->Destroy();
1025 m_checkerDialog = nullptr;
1026 }
1027}
1028
1029
1031{
1032 FOOTPRINT* footprint = board()->Footprints().front();
1033 int errors = 0;
1034 wxString details;
1035
1036 // Repair duplicate IDs and missing nets.
1037 std::set<KIID> ids;
1038 int duplicates = 0;
1039
1040 auto processItem =
1041 [&]( EDA_ITEM* aItem )
1042 {
1043 if( ids.count( aItem->m_Uuid ) )
1044 {
1045 duplicates++;
1046 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( aItem ) )
1047 boardItem->ResetUuid();
1048 }
1049
1050 ids.insert( aItem->m_Uuid );
1051 };
1052
1053 // Footprint IDs are the most important, so give them the first crack at "claiming" a
1054 // particular KIID.
1055
1056 processItem( footprint );
1057
1058 // After that the principal use is for DRC marker pointers, which are most likely to pads.
1059
1060 for( PAD* pad : footprint->Pads() )
1061 processItem( pad );
1062
1063 // From here out I don't think order matters much.
1064
1065 processItem( &footprint->Reference() );
1066 processItem( &footprint->Value() );
1067
1068 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1069 processItem( item );
1070
1071 for( ZONE* zone : footprint->Zones() )
1072 processItem( zone );
1073
1074 for( PCB_GROUP* group : footprint->Groups() )
1075 processItem( group );
1076
1077 if( duplicates )
1078 {
1079 errors += duplicates;
1080 details += wxString::Format( _( "%d duplicate IDs replaced.\n" ), duplicates );
1081 }
1082
1083 if( errors )
1084 {
1085 m_frame->OnModify();
1086
1087 wxString msg = wxString::Format( _( "%d potential problems repaired." ), errors );
1088 DisplayInfoMessage( m_frame, msg, details );
1089 }
1090 else
1091 {
1092 DisplayInfoMessage( m_frame, _( "No footprint problems found." ) );
1093 }
1094
1095 return 0;
1096}
1097
1098
1099// The appearance panel also claims Ctrl+Tab for layer-preset cycling, so it wins when focused.
1101{
1102 wxWindow* appearance = aFrame->GetAppearancePanel();
1103
1104 if( !appearance )
1105 return false;
1106
1107 for( wxWindow* focus = wxWindow::FindFocus(); focus; focus = focus->GetParent() )
1108 {
1109 if( focus == appearance )
1110 return true;
1111 }
1112
1113 return false;
1114}
1115
1116
1118{
1120 return 0;
1121
1122 m_frame->AdvanceFootprintTab( true );
1123 return 0;
1124}
1125
1126
1128{
1130 return 0;
1131
1132 m_frame->AdvanceFootprintTab( false );
1133 return 0;
1134}
1135
1136
1138{
1139 m_frame->CloseActiveFootprintTab();
1140 return 0;
1141}
1142
1143
1145{
1146 // clang-format off
1155
1161
1166
1170
1173
1176
1180
1185 // clang-format on
1186
1187 // Line modes for the footprint editor: explicit modes, next-mode, and toolbar sync
1193}
1194
1196{
1197 LEADER_MODE mode = aEvent.Parameter<LEADER_MODE>();
1198 GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode = mode;
1199 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1201 return 0;
1202}
1203
1205{
1207
1208 if( !f )
1209 return 0;
1210
1211 LEADER_MODE mode = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
1212
1213 switch( mode )
1214 {
1217 default:
1219 }
1220
1221 return 0;
1222}
@ FPHOLDER
Definition board.h:364
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:81
void BuildListOfNets()
Definition board.h:1049
const FOOTPRINTS & Footprints() const
Definition board.h:420
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:442
EDA_ITEM * Clone() const override
Invoke a function on all children.
const LIB_ID & GetFPID() const
Definition footprint.h:441
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:96
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:49
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:218
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:160
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.