kicad/pcbnew/xchgmod.cpp

569 lines
16 KiB
C++
Raw Normal View History

/**
* @file xchgmod.cpp
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 1992-2013 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 <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <kicad_string.h>
#include <wxPcbStruct.h>
#include <macros.h>
#include <pcbcommon.h>
#include <class_board.h>
#include <class_module.h>
#include <pcbnew.h>
#include <dialog_exchange_modules_base.h>
#include <wildcards_and_files_ext.h>
static bool RecreateCmpFile( BOARD * aBrd, const wxString& aFullCmpFileName );
2009-06-20 19:09:43 +00:00
class DIALOG_EXCHANGE_MODULE : public DIALOG_EXCHANGE_MODULE_BASE
{
private:
PCB_EDIT_FRAME* m_parent;
MODULE* m_currentModule;
static int m_selectionMode; // Remember the last exchange option
2007-08-23 04:28:46 +00:00
public:
DIALOG_EXCHANGE_MODULE( PCB_EDIT_FRAME* aParent, MODULE* aModule );
2009-06-20 19:09:43 +00:00
~DIALOG_EXCHANGE_MODULE() { };
private:
void OnSelectionClicked( wxCommandEvent& event );
void OnOkClick( wxCommandEvent& event );
void OnQuit( wxCommandEvent& event );
void BrowseAndSelectFootprint( wxCommandEvent& event );
void RebuildCmpList( wxCommandEvent& event );
void init();
void ChangeCurrentFootprint();
void ChangeSameFootprints( bool aUseValue);
void ChangeAllFootprints();
bool Change_1_Module( MODULE* aModule,
const FPID& aNewFootprintFPID,
PICKED_ITEMS_LIST* aUndoPickList,
bool eShowError );
};
int DIALOG_EXCHANGE_MODULE::m_selectionMode = 0;
2007-08-23 04:28:46 +00:00
DIALOG_EXCHANGE_MODULE::DIALOG_EXCHANGE_MODULE( PCB_EDIT_FRAME* parent, MODULE* Module ) :
2009-06-20 19:09:43 +00:00
DIALOG_EXCHANGE_MODULE_BASE( parent )
{
m_parent = parent;
m_currentModule = Module;
init();
2009-06-20 19:09:43 +00:00
GetSizer()->Fit( this );
GetSizer()->SetSizeHints( this );
}
2007-08-23 04:28:46 +00:00
void PCB_EDIT_FRAME::InstallExchangeModuleFrame( MODULE* Module )
2009-06-20 19:09:43 +00:00
{
DIALOG_EXCHANGE_MODULE dialog( this, Module );
2007-08-23 04:28:46 +00:00
2009-06-20 19:09:43 +00:00
dialog.ShowModal();
}
2007-08-23 04:28:46 +00:00
void DIALOG_EXCHANGE_MODULE::OnQuit( wxCommandEvent& event )
2009-06-20 19:09:43 +00:00
{
m_selectionMode = m_Selection->GetSelection();
EndModal( 0 );
2009-06-20 19:09:43 +00:00
}
2007-08-23 04:28:46 +00:00
void DIALOG_EXCHANGE_MODULE::init()
2009-06-20 19:09:43 +00:00
{
SetFocus();
2007-08-23 04:28:46 +00:00
m_OldModule->AppendText( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
m_NewModule->AppendText( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
m_OldValue->AppendText( m_currentModule->GetValue() );
m_Selection->SetSelection( m_selectionMode );
2007-08-23 04:28:46 +00:00
2009-06-20 19:09:43 +00:00
// Enable/disable widgets:
wxCommandEvent event;
OnSelectionClicked( event );
}
2009-06-20 19:09:43 +00:00
void DIALOG_EXCHANGE_MODULE::OnOkClick( wxCommandEvent& event )
{
m_selectionMode = m_Selection->GetSelection();
2009-06-20 19:09:43 +00:00
switch( m_Selection->GetSelection() )
{
case 0:
ChangeCurrentFootprint();
2009-06-20 19:09:43 +00:00
break;
2007-08-23 04:28:46 +00:00
2009-06-20 19:09:43 +00:00
case 1:
ChangeSameFootprints( false );
2009-06-20 19:09:43 +00:00
break;
case 2:
ChangeSameFootprints( true );
2009-06-20 19:09:43 +00:00
break;
case 3:
ChangeAllFootprints();
2009-06-20 19:09:43 +00:00
break;
}
}
2009-06-20 19:09:43 +00:00
void DIALOG_EXCHANGE_MODULE::OnSelectionClicked( wxCommandEvent& event )
{
bool enable = true;
2009-06-20 19:09:43 +00:00
switch( m_Selection->GetSelection() )
{
case 0:
case 1:
case 2:
break;
case 3:
enable = false;
2009-06-20 19:09:43 +00:00
break;
}
m_NewModule->Enable( enable );
m_Browsebutton->Enable( enable );
}
2007-08-23 04:28:46 +00:00
/*
* Rebuild the file name.CMP (if any) after exchanging footprints
* if the footprint are managed by this file
* Return false if error
2007-08-23 04:28:46 +00:00
*/
void DIALOG_EXCHANGE_MODULE::RebuildCmpList( wxCommandEvent& event )
{
wxFileName fn;
wxString msg;
2007-08-23 04:28:46 +00:00
// Build CMP file name by changing the extension of NetList filename
fn = m_parent->GetBoard()->GetFileName();
fn.SetExt( ComponentFileExtension );
2007-08-23 04:28:46 +00:00
if( RecreateCmpFile( m_parent->GetBoard(), fn.GetFullPath() ) )
2007-08-23 04:28:46 +00:00
{
msg.Printf( _( "File '%s' created\n" ),
GetChars( fn.GetFullPath() ) );
2007-08-23 04:28:46 +00:00
}
else
2007-08-23 04:28:46 +00:00
{
msg.Printf( _( "** Could not create file '%s' ***\n" ),
GetChars( fn.GetFullPath() ) );
2007-08-23 04:28:46 +00:00
}
m_WinMessages->AppendText( msg );
}
/* Change the current footprint at the current cursor position.
* Retains the following:
* - position, orientation and side
* - value and ref
* - pads net names
2007-08-23 04:28:46 +00:00
*/
void DIALOG_EXCHANGE_MODULE::ChangeCurrentFootprint()
{
2007-08-23 04:28:46 +00:00
wxString newmodulename = m_NewModule->GetValue();
2007-08-23 04:28:46 +00:00
if( newmodulename == wxEmptyString )
return;
2009-08-06 07:11:04 +00:00
PICKED_ITEMS_LIST pickList;
if( Change_1_Module( m_currentModule, newmodulename, &pickList, true ) )
2007-08-23 04:28:46 +00:00
{
if( m_parent->GetBoard()->IsElementVisible( RATSNEST_VISIBLE ) )
m_parent->Compile_Ratsnest( NULL, true );
m_parent->GetCanvas()->Refresh();
2007-08-23 04:28:46 +00:00
}
2009-08-06 07:11:04 +00:00
if( pickList.GetCount() )
m_parent->SaveCopyInUndoList( pickList, UR_UNSPECIFIED );
}
2007-08-23 04:28:46 +00:00
/*
* Change all footprints having the same fpid by a new one from lib
* Retains:
* - direction, position, side
* - value and ref
* - pads net names
* Note: m_currentModule is no longer the current footprint
* since it has been changed!
* if aUseValue is true, footprints having the same fpid should
* also have the same value
2007-08-23 04:28:46 +00:00
*/
void DIALOG_EXCHANGE_MODULE::ChangeSameFootprints( bool aUseValue )
{
2007-08-23 04:28:46 +00:00
wxString msg;
MODULE* Module, * PtBack;
2009-08-06 07:11:04 +00:00
bool change = false;
2007-08-23 04:28:46 +00:00
wxString newmodulename = m_NewModule->GetValue();
wxString value;
FPID lib_reference;
2009-08-06 07:11:04 +00:00
bool check_module_value = false;
int ShowErr = 3; // Post 3 error messages max.
2007-08-23 04:28:46 +00:00
if( m_parent->GetBoard()->m_Modules == NULL )
2007-08-23 04:28:46 +00:00
return;
2007-08-23 04:28:46 +00:00
if( newmodulename == wxEmptyString )
return;
lib_reference = m_currentModule->GetFPID();
2009-06-20 19:09:43 +00:00
if( aUseValue )
2007-08-23 04:28:46 +00:00
{
check_module_value = true;
value = m_currentModule->GetValue();
msg.Printf( _( "Change modules %s -> %s (for value = %s)?" ),
GetChars( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) ),
GetChars( newmodulename ),
GetChars( m_currentModule->GetValue() ) );
2007-08-23 04:28:46 +00:00
}
else
{
msg.Printf( _( "Change modules %s -> %s ?" ),
GetChars( FROM_UTF8( lib_reference.Format().c_str() ) ),
GetChars( newmodulename ) );
2007-08-23 04:28:46 +00:00
}
if( !IsOK( this, msg ) )
return;
/* The change is done from the last module because
* Change_1_Module () modifies the last item in the list.
2007-08-23 04:28:46 +00:00
*/
2009-08-06 07:11:04 +00:00
PICKED_ITEMS_LIST pickList;
/* note: for the first module in chain (the last here), Module->Back()
* points the board or is NULL
2009-06-20 19:09:43 +00:00
*/
Module = m_parent->GetBoard()->m_Modules.GetLast();
for( ; Module && ( Module->Type() == PCB_MODULE_T ); Module = PtBack )
2007-08-23 04:28:46 +00:00
{
PtBack = Module->Back();
if( lib_reference != Module->GetFPID() )
2007-08-23 04:28:46 +00:00
continue;
2007-08-23 04:28:46 +00:00
if( check_module_value )
{
if( value.CmpNoCase( Module->GetValue() ) != 0 )
2007-08-23 04:28:46 +00:00
continue;
}
if( Change_1_Module( Module, newmodulename, &pickList, ShowErr ) )
change = true;
2007-08-23 04:28:46 +00:00
else if( ShowErr )
ShowErr--;
}
if( change )
{
if( m_parent->GetBoard()->IsElementVisible( RATSNEST_VISIBLE ) )
m_parent->Compile_Ratsnest( NULL, true );
m_parent->GetCanvas()->Refresh();
2007-08-23 04:28:46 +00:00
}
2009-08-06 07:11:04 +00:00
if( pickList.GetCount() )
m_parent->SaveCopyInUndoList( pickList, UR_UNSPECIFIED );
}
2007-08-23 04:28:46 +00:00
/*
* Change all modules with module of the same name in library.
* Maintains:
* - direction, position, side
* - value and ref
* - pads net names
2007-08-23 04:28:46 +00:00
*/
void DIALOG_EXCHANGE_MODULE::ChangeAllFootprints()
{
MODULE* Module, * PtBack;
2009-08-06 07:11:04 +00:00
bool change = false;
int ShowErr = 3; // Post 3 error max.
2007-08-23 04:28:46 +00:00
if( m_parent->GetBoard()->m_Modules == NULL )
2007-08-23 04:28:46 +00:00
return;
if( !IsOK( this, _( "Change ALL modules ?" ) ) )
return;
/* The change is done from the last module because the function
* Change_1_Module () modifies the last module in the list
2007-08-23 04:28:46 +00:00
*/
PICKED_ITEMS_LIST pickList;
2007-08-23 04:28:46 +00:00
/* note: for the first module in chain (the last here), Module->Back()
* points the board or is NULL
2009-06-20 19:09:43 +00:00
*/
Module = m_parent->GetBoard()->m_Modules.GetLast();
for( ; Module && ( Module->Type() == PCB_MODULE_T ); Module = PtBack )
2007-08-23 04:28:46 +00:00
{
PtBack = Module->Back();
if( Change_1_Module( Module, Module->GetFPID(), &pickList, ShowErr ) )
change = true;
2007-08-23 04:28:46 +00:00
else if( ShowErr )
ShowErr--;
}
if( change )
{
if( m_parent->GetBoard()->IsElementVisible( RATSNEST_VISIBLE ) )
m_parent->Compile_Ratsnest( NULL, true );
m_parent->GetCanvas()->Refresh();
2007-08-23 04:28:46 +00:00
}
2009-08-06 07:11:04 +00:00
if( pickList.GetCount() )
m_parent->SaveCopyInUndoList( pickList, UR_UNSPECIFIED );
}
2007-08-23 04:28:46 +00:00
/*
* Change aModule to a new, fresh one from lib
* Retains
* - direction, position, side
* - value and ref
* - pads net names
* Returns: false if no change (if the new module is not found)
* true if OK
2007-08-23 04:28:46 +00:00
*/
bool DIALOG_EXCHANGE_MODULE::Change_1_Module( MODULE* aModule,
const FPID& aNewFootprintFPID,
PICKED_ITEMS_LIST* aUndoPickList,
bool aShowError )
{
MODULE* newModule;
wxString line;
if( aModule == NULL )
2009-08-06 07:11:04 +00:00
return false;
2007-08-23 04:28:46 +00:00
wxBusyCursor dummy;
// Copy parameters from the old module.
FPID oldFootprintFPID = aModule->GetFPID();
// Load module.
line.Printf( _( "Change module '%s' (from '%s') to '%s'" ),
GetChars( aModule->GetReference() ),
oldFootprintFPID.Format().c_str(),
aNewFootprintFPID.Format().c_str() );
m_WinMessages->AppendText( line );
2014-01-02 02:17:07 +00:00
wxString moduleName = aNewFootprintFPID.GetFootprintName();
wxString libName = aNewFootprintFPID.GetLibNickname();
newModule = m_parent->LoadFootprint( aNewFootprintFPID );
if( newModule == NULL ) // New module not found, redraw the old one.
2007-08-23 04:28:46 +00:00
{
m_WinMessages->AppendText( wxT( " No\n" ) );
2009-08-06 07:11:04 +00:00
return false;
2007-08-23 04:28:46 +00:00
}
m_parent->GetBoard()->Add( newModule, ADD_APPEND );
if( aModule == m_currentModule )
m_currentModule = newModule;
m_WinMessages->AppendText( wxT( " OK\n" ) );
m_parent->Exchange_Module( aModule, newModule, aUndoPickList );
2009-08-06 07:11:04 +00:00
return true;
}
2007-08-23 04:28:46 +00:00
void PCB_EDIT_FRAME::Exchange_Module( MODULE* aOldModule,
MODULE* aNewModule,
PICKED_ITEMS_LIST* aUndoPickList )
{
2009-08-06 07:11:04 +00:00
aNewModule->SetParent( GetBoard() );
2007-08-23 04:28:46 +00:00
/* place module without ratsnest refresh: this will be made later
2009-06-20 19:09:43 +00:00
* when all modules are on board
*/
wxPoint oldpos = GetCrossHairPosition();
SetCrossHairPosition( aOldModule->GetPosition(), false );
PlaceModule( aNewModule, NULL, true );
SetCrossHairPosition( oldpos, false );
2007-08-23 04:28:46 +00:00
// Flip footprint if needed
2009-08-06 07:11:04 +00:00
if( aOldModule->GetLayer() != aNewModule->GetLayer() )
2007-08-23 04:28:46 +00:00
{
aNewModule->Flip( aNewModule->GetPosition() );
2007-08-23 04:28:46 +00:00
}
// Rotate footprint if needed
if( aOldModule->GetOrientation() != aNewModule->GetOrientation() )
2007-08-23 04:28:46 +00:00
{
Rotate_Module( NULL, aNewModule, aOldModule->GetOrientation(), false );
2007-08-23 04:28:46 +00:00
}
// Update reference and value
aNewModule->SetReference( aOldModule->GetReference() );
aNewModule->SetValue( aOldModule->GetValue() );
2007-08-23 04:28:46 +00:00
// Updating other parameters
2011-12-12 08:37:05 +00:00
aNewModule->SetTimeStamp( aOldModule->GetTimeStamp() );
aNewModule->SetPath( aOldModule->GetPath() );
2007-08-23 04:28:46 +00:00
// Update pad netnames ( when possible)
for( D_PAD* pad = aNewModule->Pads(); pad != NULL; pad = pad->Next() )
2007-08-23 04:28:46 +00:00
{
pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
D_PAD* old_pad = aOldModule->Pads();
for( ; old_pad != NULL; old_pad = old_pad->Next() )
2007-08-23 04:28:46 +00:00
{
2011-12-16 17:03:25 +00:00
if( pad->PadNameEqual( old_pad ) )
pad->SetNetCode( old_pad->GetNetCode() );
2007-08-23 04:28:46 +00:00
}
}
2009-08-06 07:11:04 +00:00
if( aUndoPickList )
{
GetBoard()->Remove( aOldModule );
ITEM_PICKER picker_old( aOldModule, UR_DELETED );
ITEM_PICKER picker_new( aNewModule, UR_NEW );
aUndoPickList->PushItem( picker_old );
aUndoPickList->PushItem( picker_new );
2009-08-06 07:11:04 +00:00
}
else
{
2009-08-06 07:11:04 +00:00
aOldModule->DeleteStructure();
}
2007-08-23 04:28:46 +00:00
GetBoard()->m_Status_Pcb = 0;
aNewModule->ClearFlags();
OnModify();
}
/*
* Displays the list of modules in library name and select 1 name.
*/
2009-06-20 19:09:43 +00:00
void DIALOG_EXCHANGE_MODULE::BrowseAndSelectFootprint( wxCommandEvent& event )
{
2007-08-23 04:28:46 +00:00
wxString newname;
newname = m_parent->SelectFootprint( m_parent, wxEmptyString, wxEmptyString, wxEmptyString,
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
m_parent->FootprintLibs() );
2007-08-23 04:28:46 +00:00
if( newname != wxEmptyString )
m_NewModule->SetValue( newname );
}
void PCB_EDIT_FRAME::RecreateCmpFileFromBoard( wxCommandEvent& aEvent )
{
wxFileName fn;
MODULE* Module = GetBoard()->m_Modules;
wxString msg;
wxString wildcard;
2007-08-23 04:28:46 +00:00
if( Module == NULL )
{
DisplayError( this, _( "No Modules!" ) );
return;
2007-08-23 04:28:46 +00:00
}
// Calculation file name by changing the extension name to NetList
2012-08-29 16:59:50 +00:00
fn = GetBoard()->GetFileName();
fn.SetExt( ComponentFileExtension );
wildcard = wxGetTranslation( ComponentFileWildcard );
2010-02-28 18:26:48 +00:00
wxFileDialog dlg( this, _( "Save Component Files" ), wxGetCwd(),
fn.GetFullName(), wildcard,
wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if( dlg.ShowModal() == wxID_CANCEL )
return;
2007-08-23 04:28:46 +00:00
fn = dlg.GetPath();
2007-08-23 04:28:46 +00:00
if( ! RecreateCmpFile( GetBoard(), fn.GetFullPath() ) )
2007-08-23 04:28:46 +00:00
{
msg.Printf( _( "Could not create file '%s'" ), GetChars(fn.GetFullPath() ) );
2007-08-23 04:28:46 +00:00
DisplayError( this, msg );
return;
2007-08-23 04:28:46 +00:00
}
}
2007-08-23 04:28:46 +00:00
bool RecreateCmpFile( BOARD * aBrd, const wxString& aFullCmpFileName )
{
FILE* cmpFile;
cmpFile = wxFopen( aFullCmpFileName, wxT( "wt" ) );
2007-08-23 04:28:46 +00:00
if( cmpFile == NULL )
return false;
fprintf( cmpFile, "Cmp-Mod V01 Genere par PcbNew le %s\n", TO_UTF8( DateAndTime() ) );
MODULE* module = aBrd->m_Modules;
for( ; module != NULL; module = module->Next() )
2007-08-23 04:28:46 +00:00
{
fprintf( cmpFile, "\nBeginCmp\n" );
fprintf( cmpFile, "TimeStamp = %8.8lX\n", module->GetTimeStamp() );
fprintf( cmpFile, "Path = %s\n", TO_UTF8( module->GetPath() ) );
fprintf( cmpFile, "Reference = %s;\n",
!module->GetReference().IsEmpty() ?
TO_UTF8( module->GetReference() ) : "[NoRef]" );
fprintf( cmpFile, "ValeurCmp = %s;\n",
!module->GetValue().IsEmpty() ?
TO_UTF8( module->GetValue() ) : "[NoVal]" );
fprintf( cmpFile, "IdModule = %s;\n", module->GetFPID().Format().c_str() );
fprintf( cmpFile, "EndCmp\n" );
2007-08-23 04:28:46 +00:00
}
fprintf( cmpFile, "\nEndListe\n" );
fclose( cmpFile );
return true;
}