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