Recommendation is to avoid using the year nomenclature as this information is already encoded in the git repo. Avoids needing to repeatly update. Also updates AUTHORS.txt from current repo with contributor names
459 lines
15 KiB
C++
459 lines
15 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
|
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
|
|
* Copyright (C) 2013 Wayne Stambaugh <[email protected]>
|
|
* Copyright The 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 <bitmaps.h>
|
|
#include <board.h>
|
|
#include <footprint.h>
|
|
#include <pad.h>
|
|
#include <dialog_exchange_footprints.h>
|
|
#include <string_utils.h>
|
|
#include <kiway.h>
|
|
#include <kiway_express.h>
|
|
#include <macros.h>
|
|
#include <pcb_edit_frame.h>
|
|
#include <pcbnew_settings.h>
|
|
#include <widgets/wx_html_report_panel.h>
|
|
#include <widgets/std_bitmap_button.h>
|
|
#include <tool/tool_manager.h>
|
|
#include <tools/pcb_selection_tool.h>
|
|
|
|
|
|
#define ID_MATCH_FP_ALL 4200
|
|
#define ID_MATCH_FP_SELECTED 4201
|
|
#define ID_MATCH_FP_REF 4202
|
|
#define ID_MATCH_FP_VAL 4203
|
|
#define ID_MATCH_FP_ID 4204
|
|
|
|
|
|
int g_matchModeForUpdate = ID_MATCH_FP_ALL;
|
|
int g_matchModeForUpdateSelected = ID_MATCH_FP_SELECTED;
|
|
int g_matchModeForExchange = ID_MATCH_FP_REF;
|
|
int g_matchModeForExchangeSelected = ID_MATCH_FP_SELECTED;
|
|
|
|
// { update, change }
|
|
bool g_removeExtraTextItems[2] = { false, false };
|
|
bool g_resetTextItemLayers[2] = { false, true };
|
|
bool g_resetTextItemEffects[2] = { false, true };
|
|
bool g_resetTextItemContent[2] = { false, true };
|
|
bool g_resetFabricationAttrs[2] = { false, true };
|
|
bool g_reset3DModels[2] = { true, true };
|
|
|
|
|
|
DIALOG_EXCHANGE_FOOTPRINTS::DIALOG_EXCHANGE_FOOTPRINTS( PCB_EDIT_FRAME* aParent,
|
|
FOOTPRINT* aFootprint,
|
|
bool updateMode, bool selectedMode ) :
|
|
DIALOG_EXCHANGE_FOOTPRINTS_BASE( aParent ),
|
|
m_commit( aParent ),
|
|
m_parent( aParent ),
|
|
m_currentFootprint( aFootprint ),
|
|
m_updateMode( updateMode )
|
|
{
|
|
if( !updateMode )
|
|
{
|
|
SetTitle( _( "Change Footprints" ) );
|
|
m_matchAll->SetLabel( _( "Change all footprints on board" ) );
|
|
m_matchSelected->SetLabel( _( "Change selected footprint(s)" ) );
|
|
m_matchSpecifiedRef->SetLabel( _( "Change footprints matching reference designator:" ) );
|
|
m_matchSpecifiedValue->SetLabel( _( "Change footprints matching value:" ) );
|
|
m_matchSpecifiedID->SetLabel( _( "Change footprints with library id:" ) );
|
|
m_resetTextItemLayers->SetLabel( _( "Update text layers and visibilities" ) );
|
|
m_resetTextItemEffects->SetLabel( _( "Update text sizes, styles and positions" ) );
|
|
m_resetTextItemContent->SetLabel( _( "Update text content" ) );
|
|
m_resetFabricationAttrs->SetLabel( _( "Update fabrication attributes" ) );
|
|
m_reset3DModels->SetLabel( _( "Update 3D models" ) );
|
|
}
|
|
|
|
#if 0 // translator hint
|
|
wxString x = _( "Update/reset strings: there are two cases these descriptions need to cover: "
|
|
"the user made overrides to a footprint on the PCB and wants to remove them, "
|
|
"or the user made changes to the library footprint and wants to propagate "
|
|
"them back to the PCB." );
|
|
#endif
|
|
|
|
if( m_updateMode )
|
|
{
|
|
m_changeSizer->Show( false );
|
|
}
|
|
else
|
|
{
|
|
m_upperSizer->FindItem( m_matchAll )->Show( false );
|
|
m_newIDBrowseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_library ) );
|
|
}
|
|
|
|
if( m_currentFootprint )
|
|
m_newID->AppendText( From_UTF8( m_currentFootprint->GetFPID().Format().c_str() ) );
|
|
else
|
|
m_upperSizer->FindItem( m_matchSelected )->Show( false );
|
|
|
|
// Use ChangeValue() instead of SetValue() so we don't generate events.
|
|
if( m_currentFootprint )
|
|
m_specifiedRef->ChangeValue( m_currentFootprint->GetReference() );
|
|
|
|
if( m_currentFootprint )
|
|
m_specifiedValue->ChangeValue( m_currentFootprint->GetValue() );
|
|
|
|
if( m_currentFootprint )
|
|
m_specifiedID->ChangeValue( From_UTF8( m_currentFootprint->GetFPID().Format().c_str() ) );
|
|
|
|
m_specifiedIDBrowseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_library ) );
|
|
|
|
m_upperSizer->SetEmptyCellSize( wxSize( 0, 0 ) );
|
|
// The upper sizer has its content modified: re-layout it:
|
|
m_upperSizer->Layout();
|
|
|
|
// initialize match-mode
|
|
if( m_updateMode )
|
|
m_matchMode = selectedMode ? &g_matchModeForUpdateSelected : &g_matchModeForUpdate;
|
|
else
|
|
m_matchMode = selectedMode ? &g_matchModeForExchangeSelected : &g_matchModeForExchange;
|
|
|
|
wxCommandEvent event;
|
|
event.SetEventObject( this );
|
|
|
|
switch( *m_matchMode )
|
|
{
|
|
case ID_MATCH_FP_ALL: OnMatchAllClicked( event ); break;
|
|
case ID_MATCH_FP_SELECTED: OnMatchSelectedClicked( event ); break;
|
|
case ID_MATCH_FP_REF: OnMatchRefClicked( event ); break;
|
|
case ID_MATCH_FP_VAL: OnMatchValueClicked( event ); break;
|
|
case ID_MATCH_FP_ID: OnMatchIDClicked( event ); break;
|
|
default: break;
|
|
}
|
|
|
|
m_removeExtraBox->SetValue( g_removeExtraTextItems[ m_updateMode ? 0 : 1 ] );
|
|
m_resetTextItemLayers->SetValue( g_resetTextItemLayers[ m_updateMode ? 0 : 1 ] );
|
|
m_resetTextItemEffects->SetValue( g_resetTextItemEffects[ m_updateMode ? 0 : 1 ] );
|
|
m_resetTextItemContent->SetValue( g_resetTextItemContent[ m_updateMode ? 0 : 1 ] );
|
|
m_resetFabricationAttrs->SetValue( g_resetFabricationAttrs[ m_updateMode ? 0 : 1 ] );
|
|
m_reset3DModels->SetValue( g_reset3DModels[ m_updateMode ? 0 : 1 ] );
|
|
|
|
m_MessageWindow->SetLazyUpdate( true );
|
|
m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
|
|
|
|
// DIALOG_SHIM needs a unique hash_key because classname is not sufficient
|
|
// because the update and change versions of this dialog have different controls.
|
|
m_hash_key = TO_UTF8( GetTitle() );
|
|
|
|
wxString okLabel = m_updateMode ? _( "Update" ) : _( "Change" );
|
|
|
|
SetupStandardButtons( { { wxID_OK, okLabel },
|
|
{ wxID_CANCEL, _( "Close" ) } } );
|
|
|
|
// Now all widgets have the size fixed, call FinishDialogSettings
|
|
finishDialogSettings();
|
|
}
|
|
|
|
|
|
DIALOG_EXCHANGE_FOOTPRINTS::~DIALOG_EXCHANGE_FOOTPRINTS()
|
|
{
|
|
g_removeExtraTextItems[ m_updateMode ? 0 : 1 ] = m_removeExtraBox->GetValue();
|
|
g_resetTextItemLayers[ m_updateMode ? 0 : 1 ] = m_resetTextItemLayers->GetValue();
|
|
g_resetTextItemEffects[ m_updateMode ? 0 : 1 ] = m_resetTextItemEffects->GetValue();
|
|
g_resetTextItemContent[ m_updateMode ? 0 : 1 ] = m_resetTextItemContent->GetValue();
|
|
g_resetFabricationAttrs[ m_updateMode ? 0 : 1 ] = m_resetFabricationAttrs->GetValue();
|
|
g_reset3DModels[ m_updateMode ? 0 : 1 ] = m_reset3DModels->GetValue();
|
|
}
|
|
|
|
|
|
bool DIALOG_EXCHANGE_FOOTPRINTS::isMatch( FOOTPRINT* aFootprint )
|
|
{
|
|
LIB_ID specifiedID;
|
|
|
|
switch( *m_matchMode )
|
|
{
|
|
case ID_MATCH_FP_ALL:
|
|
return true;
|
|
case ID_MATCH_FP_SELECTED:
|
|
return aFootprint == m_currentFootprint || aFootprint->IsSelected();
|
|
case ID_MATCH_FP_REF:
|
|
return WildCompareString( m_specifiedRef->GetValue(), aFootprint->GetReference(), false );
|
|
case ID_MATCH_FP_VAL:
|
|
return WildCompareString( m_specifiedValue->GetValue(), aFootprint->GetValue(), false );
|
|
case ID_MATCH_FP_ID:
|
|
specifiedID.Parse( m_specifiedID->GetValue() );
|
|
return aFootprint->GetFPID() == specifiedID;
|
|
default:
|
|
return false; // just to quiet compiler warnings....
|
|
}
|
|
}
|
|
|
|
|
|
wxRadioButton* DIALOG_EXCHANGE_FOOTPRINTS::getRadioButtonForMode()
|
|
{
|
|
switch( *m_matchMode )
|
|
{
|
|
case ID_MATCH_FP_ALL: return m_matchAll;
|
|
case ID_MATCH_FP_SELECTED: return m_matchSelected;
|
|
case ID_MATCH_FP_REF: return m_matchSpecifiedRef;
|
|
case ID_MATCH_FP_VAL: return m_matchSpecifiedValue;
|
|
case ID_MATCH_FP_ID: return m_matchSpecifiedID;
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::updateMatchModeRadioButtons( wxUpdateUIEvent& )
|
|
{
|
|
wxRadioButton* rb_button = getRadioButtonForMode();
|
|
|
|
wxRadioButton* rb_butt_list[] =
|
|
{
|
|
m_matchAll,
|
|
m_matchSelected,
|
|
m_matchSpecifiedRef,
|
|
m_matchSpecifiedValue,
|
|
m_matchSpecifiedID,
|
|
nullptr // end of list
|
|
};
|
|
|
|
// Ensure the button state is ok. Only one button can be checked
|
|
// Change button state only if its state is incorrect, otherwise
|
|
// we have issues depending on the platform.
|
|
for( int ii = 0; rb_butt_list[ii]; ++ii )
|
|
{
|
|
bool state = rb_butt_list[ii] == rb_button;
|
|
|
|
if( rb_butt_list[ii]->GetValue() != state )
|
|
rb_butt_list[ii]->SetValue( state );
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchAllClicked( wxCommandEvent& event )
|
|
{
|
|
*m_matchMode = ID_MATCH_FP_ALL;
|
|
|
|
if( event.GetEventObject() == this )
|
|
SetInitialFocus( m_matchAll );
|
|
else
|
|
m_matchAll->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchSelectedClicked( wxCommandEvent& event )
|
|
{
|
|
*m_matchMode = ID_MATCH_FP_SELECTED;
|
|
|
|
if( event.GetEventObject() == this )
|
|
SetInitialFocus( m_matchSelected );
|
|
else
|
|
m_matchSelected->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchRefClicked( wxCommandEvent& event )
|
|
{
|
|
*m_matchMode = ID_MATCH_FP_REF;
|
|
|
|
if( event.GetEventObject() == this )
|
|
SetInitialFocus( m_specifiedRef );
|
|
else if( event.GetEventObject() != m_specifiedRef )
|
|
m_specifiedRef->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchValueClicked( wxCommandEvent& event )
|
|
{
|
|
*m_matchMode = ID_MATCH_FP_VAL;
|
|
|
|
if( event.GetEventObject() == this )
|
|
SetInitialFocus( m_specifiedValue );
|
|
else if( event.GetEventObject() != m_specifiedValue )
|
|
m_specifiedValue->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchIDClicked( wxCommandEvent& event )
|
|
{
|
|
*m_matchMode = ID_MATCH_FP_ID;
|
|
|
|
if( event.GetEventObject() == this )
|
|
SetInitialFocus( m_specifiedID );
|
|
else if( event.GetEventObject() != m_specifiedID )
|
|
m_specifiedID->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::OnOKClicked( wxCommandEvent& event )
|
|
{
|
|
PCB_SELECTION_TOOL* selTool = m_parent->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
|
|
wxBusyCursor dummy;
|
|
|
|
m_MessageWindow->Clear();
|
|
m_MessageWindow->Flush( false );
|
|
|
|
m_newFootprints.clear();
|
|
processMatchingFootprints();
|
|
m_commit.Push( m_updateMode ? _( "Update Footprint" ) : _( "Change Footprint" ) );
|
|
selTool->AddItemsToSel( &m_newFootprints );
|
|
|
|
m_MessageWindow->Flush( false );
|
|
|
|
WINDOW_THAWER thawer( m_parent );
|
|
m_parent->GetCanvas()->Refresh();
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::processMatchingFootprints()
|
|
{
|
|
LIB_ID newFPID;
|
|
|
|
if( m_parent->GetBoard()->Footprints().empty() )
|
|
return;
|
|
|
|
if( !m_updateMode )
|
|
{
|
|
newFPID.Parse( m_newID->GetValue() );
|
|
|
|
if( !newFPID.IsValid() )
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* NB: the change is done from the last footprint because processFootprint() modifies the
|
|
* last item in the list.
|
|
*/
|
|
for( auto it = m_parent->GetBoard()->Footprints().rbegin();
|
|
it != m_parent->GetBoard()->Footprints().rend(); it++ )
|
|
{
|
|
FOOTPRINT* footprint = *it;
|
|
|
|
if( !isMatch( footprint ) )
|
|
continue;
|
|
|
|
if( m_updateMode )
|
|
processFootprint( footprint, footprint->GetFPID() );
|
|
else
|
|
processFootprint( footprint, newFPID );
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::processFootprint( FOOTPRINT* aFootprint, const LIB_ID& aNewFPID )
|
|
{
|
|
LIB_ID oldFPID = aFootprint->GetFPID();
|
|
wxString msg;
|
|
|
|
// Load new footprint.
|
|
if( m_updateMode )
|
|
{
|
|
msg.Printf( _( "Updated footprint %s (%s)" ) + wxS( ": " ),
|
|
aFootprint->GetReference(),
|
|
oldFPID.Format().c_str() );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "Changed footprint %s from '%s' to '%s'" ) + wxS( ": " ),
|
|
aFootprint->GetReference(),
|
|
oldFPID.Format().c_str(),
|
|
aNewFPID.Format().c_str() );
|
|
}
|
|
|
|
FOOTPRINT* newFootprint = m_parent->LoadFootprint( aNewFPID );
|
|
|
|
if( !newFootprint )
|
|
{
|
|
msg += _( "*** library footprint not found ***" );
|
|
m_MessageWindow->Report( msg, RPT_SEVERITY_ERROR );
|
|
return;
|
|
}
|
|
|
|
bool updated = !m_updateMode || aFootprint->FootprintNeedsUpdate( newFootprint );
|
|
|
|
m_parent->ExchangeFootprint( aFootprint, newFootprint, m_commit,
|
|
m_removeExtraBox->GetValue(),
|
|
m_resetTextItemLayers->GetValue(),
|
|
m_resetTextItemEffects->GetValue(),
|
|
m_resetTextItemContent->GetValue(),
|
|
m_resetFabricationAttrs->GetValue(),
|
|
m_reset3DModels->GetValue(),
|
|
&updated );
|
|
|
|
if( aFootprint == m_currentFootprint )
|
|
m_currentFootprint = newFootprint;
|
|
|
|
if( newFootprint )
|
|
m_newFootprints.push_back( newFootprint );
|
|
|
|
if( m_updateMode && !updated )
|
|
{
|
|
msg += _( ": (no changes)" );
|
|
m_MessageWindow->Report( msg, RPT_SEVERITY_INFO );
|
|
}
|
|
else
|
|
{
|
|
msg += _( ": OK" );
|
|
m_MessageWindow->Report( msg, RPT_SEVERITY_ACTION );
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_EXCHANGE_FOOTPRINTS::ViewAndSelectFootprint( wxCommandEvent& event )
|
|
{
|
|
wxString newname = m_newID->GetValue();
|
|
|
|
if( KIWAY_PLAYER* frame = Kiway().Player( FRAME_FOOTPRINT_CHOOSER, true, this ) )
|
|
{
|
|
if( m_currentFootprint )
|
|
{
|
|
/*
|
|
* Symbol netlist format:
|
|
* pinNumber pinName <tab> pinNumber pinName...
|
|
* fpFilter fpFilter...
|
|
*/
|
|
wxString netlist;
|
|
|
|
wxArrayString pins;
|
|
|
|
for( const wxString& pad : m_currentFootprint->GetUniquePadNumbers() )
|
|
pins.push_back( pad + ' ' + wxEmptyString /* leave pinName empty */ );
|
|
|
|
if( !pins.IsEmpty() )
|
|
netlist << EscapeString( wxJoin( pins, '\t' ), CTX_LINE );
|
|
|
|
netlist << wxS( "\r" );
|
|
|
|
netlist << EscapeString( m_currentFootprint->GetFilters(), CTX_LINE ) << wxS( "\r" );
|
|
|
|
std::string payload( netlist.ToStdString() );
|
|
KIWAY_EXPRESS mail( FRAME_FOOTPRINT_CHOOSER, MAIL_SYMBOL_NETLIST, payload );
|
|
frame->KiwayMailIn( mail );
|
|
}
|
|
|
|
if( frame->ShowModal( &newname, this ) )
|
|
{
|
|
if( event.GetEventObject() == m_newIDBrowseButton )
|
|
m_newID->SetValue( newname );
|
|
else
|
|
m_specifiedID->SetValue( newname );
|
|
}
|
|
|
|
frame->Destroy();
|
|
}
|
|
}
|
|
|
|
|