eeschema and pcbnew paste: consistent paste behaviour

Duplicates are reannotated on paste in the same way in pcbnew and
eeschema such that when copying and pasting the same block in the
pcb and schematic, the reference designators will match.
This commit is contained in:
Roberto Fernandez Bautista 2021-05-01 23:39:16 +01:00
parent cf2dc06add
commit 98c8f43320
7 changed files with 163 additions and 14 deletions

View File

@ -309,15 +309,38 @@ wxString buildFullReference( const SCH_REFERENCE& aItem, int aUnitNumber = -1 )
return fullref;
}
void SCH_REFERENCE_LIST::ReannotateDuplicates( const SCH_REFERENCE_LIST& aAdditionalReferences )
{
SplitReferences();
// All multi-unit symbols always locked to ensure consistent re-annotation
SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
for( size_t i = 0; i < GetCount(); i++ )
{
SCH_REFERENCE& ref = flatList[i];
wxString refstr = ref.GetSymbol()->GetRef( &ref.GetSheetPath() );
// Never lock unassigned references
if( refstr[refstr.Len() - 1] == '?' )
continue;
lockedSymbols[refstr].AddItem( ref );
ref.m_isNew = true; // We want to reannotate all references
}
Annotate( false, 0, 0, lockedSymbols, aAdditionalReferences, true );
}
void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId, int aStartNumber,
SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap,
const SCH_REFERENCE_LIST& aAdditionalRefs )
const SCH_REFERENCE_LIST& aAdditionalRefs, bool aStartAtCurrent )
{
if ( flatList.size() == 0 )
return;
int originalSize = GetCount();
size_t originalSize = GetCount();
// For multi units components, store the list of already used full references.
// The algorithm tries to allocate the new reference to components having the same
@ -410,6 +433,13 @@ void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId, int
GetRefsInUse( first, idList, minRefId );
}
// Find references greater than current reference (unless not annotated)
if( aStartAtCurrent && ref_unit.m_numRef > 0 )
{
minRefId = ref_unit.m_numRef;
GetRefsInUse( first, idList, minRefId );
}
// Annotation of one part per package components (trivial case).
if( ref_unit.GetLibPart()->GetUnitCount() <= 1 )
{

View File

@ -158,7 +158,10 @@ public:
bool IsUnitsLocked()
{
return m_libPart->UnitsLocked();
if( m_libPart )
return m_libPart->UnitsLocked();
else
return true; // Assume units locked when we don't have a library
}
private:
@ -281,6 +284,14 @@ public:
flatList[ii].Annotate();
}
/**
* Replace any duplicate reference designators with the next available number after the
* present number. Multi-unit symbols are reannotated together.
*
* @param aAdditionalReferences Additional references to check for duplicates
*/
void ReannotateDuplicates( const SCH_REFERENCE_LIST& aAdditionalReferences );
/**
* Set the reference designators in the list that have not been annotated.
*
@ -299,10 +310,12 @@ public:
* @param aAdditionalRefs Additional references to use for checking that there a reference
* designator doesn't already exist. The caller must ensure that none of the references
* in aAdditionalRefs exist in this list.
* @param aStartAtCurrent Use m_numRef for each reference as the start number (overrides
aStartNumber)
*/
void Annotate( bool aUseSheetNum, int aSheetIntervalId, int aStartNumber,
SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap,
const SCH_REFERENCE_LIST& aAdditionalRefs );
const SCH_REFERENCE_LIST& aAdditionalRefs, bool aStartAtCurrent = false );
/**
* Check for annotations errors.

View File

@ -1463,6 +1463,9 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
return 0;
}
bool reannotateOnPaste = !forceKeepAnnotations;
forceKeepAnnotations = true; // Always keep annotations of pasted instances
// SCH_SEXP_PLUGIN added the items to the paste screen, but not to the view or anything
// else. Pull them back out to start with.
//
@ -1684,6 +1687,21 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
}
pasteInstances.SortByPageNumbers();
if( reannotateOnPaste )
{
for( SCH_SHEET_PATH& instance : pasteInstances )
{
pastedSymbols[instance].SortByReferenceOnly();
pastedSymbols[instance].ReannotateDuplicates( existingRefs );
pastedSymbols[instance].UpdateAnnotation();
// Update existing refs for next iteration
for( size_t i = 0; i < pastedSymbols[instance].GetCount(); i++ )
existingRefs.AddItem( pastedSymbols[instance][i] );
}
}
m_frame->GetCurrentSheet().UpdateAllScreenReferences();
if( sheetsPasted )

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Brian Piccioni brian@documenteddesigns.com
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* @author Brian Piccioni <brian@documenteddesigns.com>
*
* This program is free software; you can redistribute it and/or
@ -23,6 +23,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <refdes_utils.h>
#include <tool/tool_manager.h>
#include <wx/filedlg.h>
#include <tools/board_reannotate_tool.h>
@ -30,6 +31,7 @@
BOARD_REANNOTATE_TOOL::BOARD_REANNOTATE_TOOL() :
PCB_TOOL_BASE( "pcbnew.ReannotateTool" ),
m_selectionTool( NULL ),
m_frame( nullptr )
{
}
@ -37,6 +39,9 @@ BOARD_REANNOTATE_TOOL::BOARD_REANNOTATE_TOOL() :
bool BOARD_REANNOTATE_TOOL::Init()
{
// Find the selection tool, so they can cooperate
m_selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
return true;
}
@ -55,6 +60,79 @@ int BOARD_REANNOTATE_TOOL::ShowReannotateDialog( const TOOL_EVENT& aEvent )
}
int BOARD_REANNOTATE_TOOL::ReannotateDuplicatesInSelection()
{
PCB_SELECTION& selection = m_selectionTool->GetSelection();
if( selection.Empty() )
return 0;
// 1. Build list of designators on the board
FOOTPRINTS fpOnBoard = m_frame->GetBoard()->Footprints();
std::multimap<wxString, KIID> usedDesignatorsMap;
for( FOOTPRINT* fp : fpOnBoard )
usedDesignatorsMap.insert( { fp->GetReference(), fp->m_Uuid } );
// 2. Get a sorted list of footprints from the selection
FOOTPRINTS fpInSelection;
for( EDA_ITEM* item : selection )
{
if( item->Type() == PCB_FOOTPRINT_T )
fpInSelection.push_back( static_cast<FOOTPRINT*>( item ) );
}
std::sort( fpInSelection.begin(), fpInSelection.end(),
[]( const FOOTPRINT* aA, const FOOTPRINT* aB ) -> bool
{
int ii = UTIL::RefDesStringCompare( aA->GetReference(), aB->GetReference() );
if( ii == 0 )
ii = aA->m_Uuid < aB->m_Uuid; // ensure a deterministic sort
return ii < 0;
} );
// 3. Iterate through the sorted list of footprints
for( FOOTPRINT* fp : fpInSelection )
{
wxString stem = UTIL::GetRefDesPrefix( fp->GetReference() );
int value = UTIL::GetRefDesNumber( fp->GetReference() );
bool duplicate = false;
while( usedDesignatorsMap.find( fp->GetReference() ) != usedDesignatorsMap.end() )
{
auto result = usedDesignatorsMap.equal_range( fp->GetReference() );
for( auto& it = result.first; it != result.second; it++ )
{
if( it->second != fp->m_Uuid )
{
duplicate = true;
break;
}
}
if( !duplicate )
break; // The only designator in the board with this reference is the selected one
if( value < 0 )
value = 1;
else
++value;
fp->SetReference( stem + std::to_string( value ) );
}
if( duplicate )
usedDesignatorsMap.insert( { fp->GetReference(), fp->m_Uuid } );
}
return 0;
}
void BOARD_REANNOTATE_TOOL::setTransitions()
{
Go( &BOARD_REANNOTATE_TOOL::ShowReannotateDialog, PCB_ACTIONS::boardReannotate.MakeEvent() );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Brian Piccioni brian@documenteddesigns.com
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* @author Brian Piccioni <brian@documenteddesigns.com>
*
* This program is free software; you can redistribute it and/or
@ -40,12 +40,14 @@ public:
bool Init() override;
void Reset( RESET_REASON aReason ) override;
int ShowReannotateDialog( const TOOL_EVENT& aEvent );
//
int ReannotateDuplicatesInSelection();
private:
void setTransitions() override; //> Bind handlers to corresponding TOOL_ACTIONs
private:
PCB_EDIT_FRAME* m_frame; // Pointer to the currently used edit frame.
PCB_SELECTION_TOOL* m_selectionTool;
PCB_EDIT_FRAME* m_frame; // Pointer to the currently used edit frame.
};
#endif /* PCBREANNOTATETOOL_H_ */

View File

@ -198,6 +198,7 @@ bool EDIT_TOOL::Init()
// Selection tool handles the context menu for some other tools, such as the Picker.
// Don't add things like Paste when another tool is active.
menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition, 150 );
menu.AddItem( ACTIONS::duplicate, SELECTION_CONDITIONS::NotEmpty, 150 );
menu.AddItem( ACTIONS::doDelete, SELECTION_CONDITIONS::NotEmpty, 150 );

View File

@ -28,6 +28,7 @@
#include "pcb_control.h"
#include "pcb_picker_tool.h"
#include "pcb_selection_tool.h"
#include "board_reannotate_tool.h"
#include <3d_viewer/eda_3d_viewer.h>
#include <bitmaps.h>
#include <board_commit.h>
@ -891,12 +892,6 @@ int PCB_CONTROL::placeBoardItems( std::vector<BOARD_ITEM*>& aItems, bool aIsNew,
break;
}
// Add or just select items for the move/place command
if( aIsNew )
editTool->GetCurrentCommit()->Add( item );
else
editTool->GetCurrentCommit()->Added( item );
// We only need to add the items that aren't inside a group currently selected
// to the selection. If an item is inside a group and that group is selected,
// then the selection tool will select it for us.
@ -907,6 +902,18 @@ int PCB_CONTROL::placeBoardItems( std::vector<BOARD_ITEM*>& aItems, bool aIsNew,
// Select the items that should be selected
m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &itemsToSel );
// Reannotate duplicate footprints
m_toolMgr->GetTool<BOARD_REANNOTATE_TOOL>()->ReannotateDuplicatesInSelection();
for( BOARD_ITEM* item : aItems )
{
// Commit after reannotation
if( aIsNew )
editTool->GetCurrentCommit()->Add( item );
else
editTool->GetCurrentCommit()->Added( item );
}
PCB_SELECTION& selection = selectionTool->GetSelection();
if( selection.Size() > 0 )