/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include using namespace std::placeholders; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static wxArrayString s_FootprintHistoryList; static unsigned s_FootprintHistoryMaxCount = 8; static void AddFootprintToHistory( const wxString& aName ) { // Remove duplicates for( int ii = s_FootprintHistoryList.GetCount() - 1; ii >= 0; --ii ) { if( s_FootprintHistoryList[ ii ] == aName ) s_FootprintHistoryList.RemoveAt((size_t) ii ); } // Add the new name at the beginning of the history list s_FootprintHistoryList.Insert( aName, 0 ); // Remove extra names while( s_FootprintHistoryList.GetCount() >= s_FootprintHistoryMaxCount ) s_FootprintHistoryList.RemoveAt( s_FootprintHistoryList.GetCount() - 1 ); } #include bool FOOTPRINT_EDIT_FRAME::LoadFootprintFromBoard( FOOTPRINT* aFootprint ) { bool is_last_fp_from_brd = IsCurrentFPFromBoard(); FOOTPRINT* newFootprint = nullptr; PCB_EDIT_FRAME* frame = (PCB_EDIT_FRAME*) Kiway().Player( FRAME_PCB_EDITOR, false ); if( frame == nullptr ) // happens if no board editor opened return false; if( aFootprint == nullptr ) { if( !frame->GetBoard() || !frame->GetBoard()->GetFirstFootprint() ) return false; aFootprint = SelectFootprintFromBoard( frame->GetBoard() ); } if( aFootprint == nullptr ) return false; // Ensure we do not have the pad editor open (that is apseudo modal dlg). // LoadFootprintFromBoard() can be called from the board editor, and we must ensure // no footprint item is currently in edit if( wxWindow::FindWindowByName( PAD_PROPERTIES_DLG_NAME ) ) wxWindow::FindWindowByName( PAD_PROPERTIES_DLG_NAME )->Close(); if( !Clear_Pcb( true ) ) return false; m_boardFootprintUuids.clear(); auto recordAndUpdateUuid = [&]( BOARD_ITEM* aItem ) { KIID newId; m_boardFootprintUuids[ newId ] = aItem->m_Uuid; const_cast( aItem->m_Uuid ) = newId; }; newFootprint = (FOOTPRINT*) aFootprint->Clone(); // Keep existing uuids newFootprint->SetParent( GetBoard() ); newFootprint->SetParentGroup( nullptr ); newFootprint->SetLink( aFootprint->m_Uuid ); newFootprint->ClearFlags(); recordAndUpdateUuid( newFootprint ); newFootprint->RunOnDescendants( [&]( BOARD_ITEM* aItem ) { if( aItem->Type() == PCB_PAD_T ) aItem->SetLocked( false ); aItem->ClearFlags(); recordAndUpdateUuid( aItem ); } ); AddFootprintToBoard( newFootprint ); // Clear references to any net info, because the footprint editor does know any thing about // nets handled by the current edited board. // Moreover we do not want to save any reference to an unknown net when saving the footprint // in lib cache so we force the ORPHANED dummy net info for all pads. newFootprint->ClearAllNets(); GetCanvas()->GetViewControls()->SetCrossHairCursorPosition( VECTOR2D( 0, 0 ), false ); PlaceFootprint( newFootprint ); newFootprint->SetPosition( VECTOR2I( 0, 0 ) ); // cursor in GAL may not yet be initialized // Put it on FRONT layer, // because this is the default in Footprint Editor, and in libs if( newFootprint->GetLayer() != F_Cu ) newFootprint->Flip( newFootprint->GetPosition(), frame->GetPcbNewSettings()->m_FlipLeftRight ); // Put it in orientation 0, // because this is the default orientation in Footprint Editor, and in libs newFootprint->SetOrientation( ANGLE_0 ); Zoom_Automatique( false ); m_adapter->SetPreselectNode( newFootprint->GetFPID(), 0 ); ClearUndoRedoList(); GetScreen()->SetContentModified( false ); // Update the save items if needed. if( !is_last_fp_from_brd ) { ReCreateMenuBar(); ReCreateHToolbar(); if( IsSearchTreeShown() ) ToggleSearchTree(); } Update3DView( true, true ); UpdateView(); GetCanvas()->Refresh(); m_treePane->GetLibTree()->RefreshLibTree(); // update any previously-highlighted items return true; } FOOTPRINT* PCB_BASE_FRAME::SelectFootprintFromLibrary( LIB_ID aPreselect ) { wxString footprintName; LIB_ID fpid; FOOTPRINT* footprint = nullptr; static wxString lastComponentName; DIALOG_FOOTPRINT_CHOOSER dialog( this, aPreselect, s_FootprintHistoryList ); if( dialog.ShowModal() == wxID_CANCEL ) return nullptr; fpid = dialog.GetSelectedLibId(); if( !fpid.IsValid() ) return nullptr; else footprintName = fpid.Format().wx_str(); try { footprint = loadFootprint( fpid ); } catch( const IO_ERROR& ) { } if( footprint ) { lastComponentName = footprintName; AddFootprintToHistory( footprintName ); } return footprint; } FOOTPRINT* PCB_BASE_FRAME::LoadFootprint( const LIB_ID& aFootprintId ) { FOOTPRINT* footprint = nullptr; try { footprint = loadFootprint( aFootprintId ); } catch( const IO_ERROR& ) { } return footprint; } FOOTPRINT* PCB_BASE_FRAME::loadFootprint( const LIB_ID& aFootprintId ) { FP_LIB_TABLE* fptbl = PROJECT_PCB::PcbFootprintLibs( &Prj() ); wxCHECK_MSG( fptbl, nullptr, wxT( "Cannot look up LIB_ID in NULL FP_LIB_TABLE." ) ); FOOTPRINT *footprint = nullptr; // When loading a footprint from a library in the footprint editor // the items UUIDs must be keep and not reinitialized bool keepUUID = IsType( FRAME_FOOTPRINT_EDITOR ); try { footprint = fptbl->FootprintLoadWithOptionalNickname( aFootprintId, keepUUID ); } catch( const IO_ERROR& ) { } if( footprint ) { // If the footprint is found, clear all net info to be sure there are no broken links to // any netinfo list (should be not needed, but it can be edited from the footprint editor ) footprint->ClearAllNets(); if( m_pcb && !m_pcb->IsFootprintHolder() ) { BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings(); footprint->ApplyDefaultSettings( *m_pcb, bds.m_StyleFPFields, bds.m_StyleFPText, bds.m_StyleFPShapes ); } } return footprint; } FOOTPRINT* FOOTPRINT_EDIT_FRAME::SelectFootprintFromBoard( BOARD* aPcb ) { static wxString oldName; // Save name of last footprint selected. wxString fpname; wxString msg; wxArrayString listnames; for( FOOTPRINT* footprint : aPcb->Footprints() ) listnames.Add( footprint->GetReference() ); msg.Printf( _( "Footprints [%u items]" ), (unsigned) listnames.GetCount() ); wxArrayString headers; headers.Add( _( "Footprint" ) ); std::vector itemsToDisplay; // Conversion from wxArrayString to vector of ArrayString for( unsigned i = 0; i < listnames.GetCount(); i++ ) { wxArrayString item; item.Add( listnames[i] ); itemsToDisplay.push_back( item ); } EDA_LIST_DIALOG dlg( this, msg, headers, itemsToDisplay, wxEmptyString ); if( dlg.ShowModal() == wxID_OK ) fpname = dlg.GetTextSelection(); else return nullptr; oldName = fpname; for( FOOTPRINT* fp : aPcb->Footprints() ) { if( fpname == fp->GetReference() ) return fp; } return nullptr; } bool FOOTPRINT_EDIT_FRAME::SaveLibraryAs( const wxString& aLibraryPath ) { const wxString& curLibPath = aLibraryPath; wxString dstLibPath = CreateNewLibrary( wxEmptyString, aLibraryPath ); if( !dstLibPath ) return false; // user aborted in CreateNewLibrary() wxBusyCursor dummy; wxString msg; IO_MGR::PCB_FILE_T dstType = IO_MGR::GuessPluginTypeFromLibPath( dstLibPath ); IO_MGR::PCB_FILE_T curType = IO_MGR::GuessPluginTypeFromLibPath( curLibPath ); if( dstType == IO_MGR::FILE_TYPE_NONE ) dstType = IO_MGR::KICAD_SEXP; try { PLUGIN::RELEASER cur( IO_MGR::PluginFind( curType ) ); PLUGIN::RELEASER dst( IO_MGR::PluginFind( dstType ) ); if( !cur ) { msg = wxString::Format( _( "Unable to find a reader for '%s'." ), curLibPath ); DisplayError( this, msg ); return false; } if( !dst ) { msg = wxString::Format( _( "Unable to find a writer for '%s'." ), dstLibPath ); DisplayError( this, msg ); return false; } wxArrayString footprints; cur->FootprintEnumerate( footprints, curLibPath, false ); for( unsigned i = 0; i < footprints.size(); ++i ) { const FOOTPRINT* footprint = cur->GetEnumeratedFootprint( curLibPath, footprints[i] ); dst->FootprintSave( dstLibPath, footprint ); msg = wxString::Format( _( "Footprint '%s' saved." ), footprints[i] ); SetStatusText( msg ); } } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); return false; } msg = wxString::Format( _( "Footprint library '%s' saved as '%s'." ), curLibPath, dstLibPath ); DisplayInfoMessage( this, msg ); SetStatusText( wxEmptyString ); return true; } static FOOTPRINT* s_FootprintInitialCopy = nullptr; // Copy of footprint for abort/undo command static PICKED_ITEMS_LIST s_PickedList; // A pick-list to save initial footprint // and dragged tracks FOOTPRINT* PCB_BASE_FRAME::GetFootprintFromBoardByReference() { wxString footprintName; wxArrayString fplist; // Build list of available fp references, to display them in dialog for( FOOTPRINT* fp : GetBoard()->Footprints() ) fplist.Add( fp->GetReference() + wxT( " ( " ) + fp->GetValue() + wxT( " )" ) ); fplist.Sort(); DIALOG_GET_FOOTPRINT_BY_NAME dlg( this, fplist ); if( dlg.ShowModal() != wxID_OK ) //Aborted by user return nullptr; footprintName = dlg.GetValue(); footprintName.Trim( true ); footprintName.Trim( false ); if( !footprintName.IsEmpty() ) { for( FOOTPRINT* fp : GetBoard()->Footprints() ) { if( fp->GetReference().CmpNoCase( footprintName ) == 0 ) return fp; } } return nullptr; } void PCB_BASE_FRAME::PlaceFootprint( FOOTPRINT* aFootprint, bool aRecreateRatsnest ) { if( aFootprint == nullptr ) return; OnModify(); if( aFootprint->IsNew() ) { SaveCopyInUndoList( aFootprint, UNDO_REDO::NEWITEM ); } else if( aFootprint->IsMoving() ) { ITEM_PICKER picker( nullptr, aFootprint, UNDO_REDO::CHANGED ); picker.SetLink( s_FootprintInitialCopy ); s_PickedList.PushItem( picker ); s_FootprintInitialCopy = nullptr; // the picker is now owner of s_ModuleInitialCopy. } if( s_PickedList.GetCount() ) { SaveCopyInUndoList( s_PickedList, UNDO_REDO::UNSPECIFIED ); // Clear list, but DO NOT delete items, because they are owned by the saved undo // list and they therefore in use s_PickedList.ClearItemsList(); } aFootprint->SetPosition( GetCanvas()->GetViewControls()->GetCursorPosition() ); aFootprint->ClearFlags(); delete s_FootprintInitialCopy; s_FootprintInitialCopy = nullptr; if( aRecreateRatsnest ) m_pcb->GetConnectivity()->Update( aFootprint ); if( aRecreateRatsnest ) Compile_Ratsnest( true ); SetMsgPanel( aFootprint ); }