55#include <wx/richmsgdlg.h>
56#include <wx/choicdlg.h>
57#include <unordered_set>
58#include <unordered_map>
62 const wxString& aDialogTitle,
bool& aIncludeConnectedPads )
66 aIncludeConnectedPads =
true;
70 std::unordered_set<PAD*> uniquePads( aPads.begin(), aPads.end() );
73 msg.Printf(
_(
"%zu unselected pad(s) are connected to these nets. How do you want to proceed?" ),
77 details <<
_(
"Connected tracks, vias, and other non-zone copper items will still swap nets"
78 " even if you ignore the unselected pads." )
80 <<
_(
"Unselected pads:" ) <<
'\n';
82 for(
PAD*
pad : uniquePads )
85 details << wxS(
" • " ) << ( fp ? fp->
GetReference() :
_(
"<no reference designator>" ) ) << wxS(
":" )
86 <<
pad->GetNumber() <<
'\n';
90 wxRichMessageDialog dlg( aFrame, msg, aDialogTitle, wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING );
91 dlg.SetYesNoLabels(
_(
"Ignore Unselected Pads" ),
_(
"Swap All Connected Pads" ) );
92 dlg.SetExtendedMessage( details );
94 int ret = dlg.ShowModal();
96 if( ret == wxID_CANCEL )
99 aIncludeConnectedPads = ( ret == wxID_NO );
120 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
125 aCollector.
Remove( item );
138 commit = &localCommit;
140 std::vector<EDA_ITEM*> sorted =
selection.GetItemsSortedBySelectionOrder();
146 for(
size_t i = 0; i < sorted.size() - 1; i++ )
149 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
159 std::swap( aPos, bPos );
182 std::swap( aAngle, bAngle );
192 std::swap( aLayer, bLayer );
198 if( !localCommit.
Empty() )
199 localCommit.
Push(
_(
"Swap" ) );
222 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
223 std::vector<PAD*> pads;
224 const size_t padsCount = orderedPads.size();
227 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
230 std::vector<int> originalNets( padsCount );
231 std::unordered_set<PAD*> selectedPads;
233 for(
size_t i = 0; i < padsCount; ++i )
235 originalNets[i] = pads[i]->GetNetCode();
236 selectedPads.insert( pads[i] );
242 for(
size_t i = 1; i < padsCount; ++i )
244 if( originalNets[i] != originalNets[0] )
255 auto newNetForIndex =
258 return originalNets[( i + 1 ) % padsCount];
266 commit = &localCommit;
269 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
272 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
273 std::vector<PAD*> nonSelectedPadsToChange;
275 for(
size_t i = 0; i < padsCount; ++i )
278 int fromNet = originalNets[i];
279 int toNet = newNetForIndex( i );
296 if( ci->GetNetCode() != fromNet )
300 itemNewNets[ci] = toNet;
304 PAD* otherPad =
static_cast<PAD*
>( ci );
306 if( !selectedPads.count( otherPad ) )
307 nonSelectedPadsToChange.push_back( otherPad );
312 bool includeConnectedPads =
true;
319 for(
size_t i = 0; i < padsCount; ++i )
321 commit->
Modify( pads[i] );
322 pads[i]->SetNetCode( newNetForIndex( i ) );
326 for(
const auto& itemNewNet : itemNewNets )
329 int newNet = itemNewNet.second;
333 PAD* p =
static_cast<PAD*
>( item );
335 if( selectedPads.count( p ) )
338 if( !includeConnectedPads )
346 if( !localCommit.
Empty() )
347 localCommit.
Push(
_(
"Swap Pad Nets" ) );
368 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
393 else if( fp && targetFp != fp )
400 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
410 std::vector<bool> unitHit( units.size(),
false );
411 std::vector<int> unitOrder;
413 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
419 const wxString& padNum =
pad->GetNumber();
422 for(
size_t i = 0; i < units.size(); ++i )
424 for(
const auto& p : units[i].m_pins )
428 unitIdx =
static_cast<int>( i );
431 unitOrder.push_back( unitIdx );
444 std::vector<int> activeUnitIdx;
447 if( unitOrder.size() >= 2 )
449 activeUnitIdx = unitOrder;
450 sourceIdx = unitOrder.front();
453 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
455 sourceIdx = unitOrder.front();
456 wxString targetUnitByName = aEvent.
Parameter<wxString>();
460 for(
size_t i = 0; i < units.size(); ++i )
462 if(
static_cast<int>( i ) == sourceIdx )
465 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
466 targetIdx =
static_cast<int>( i );
475 activeUnitIdx.push_back( sourceIdx );
476 activeUnitIdx.push_back( targetIdx );
485 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
487 for(
int idx : activeUnitIdx )
489 if( units[idx].m_pins.size() != pinCount )
491 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
497 const size_t unitCount = activeUnitIdx.size();
498 std::vector<std::vector<PAD*>> unitPads( unitCount );
499 std::vector<std::vector<int>> unitNets( unitCount );
501 for(
size_t ui = 0; ui < unitCount; ++ui )
503 int uidx = activeUnitIdx[ui];
504 const auto& pins = units[uidx].m_pins;
506 for(
size_t pi = 0; pi < pinCount; ++pi )
512 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
516 unitPads[ui].push_back( p );
524 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
526 int refNet = unitNets[0][pi];
528 for(
size_t ui = 1; ui < unitCount; ++ui )
530 if( unitNets[ui][pi] != refNet )
540 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
549 commit = &localCommit;
551 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
554 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
555 std::vector<PAD*> nonSelectedPadsToChange;
558 std::unordered_set<PAD*> swapPads;
560 for(
const auto& v : unitPads )
561 swapPads.insert( v.begin(), v.end() );
564 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
580 if( ci->GetNetCode() != fromNet )
583 itemNewNets[ ci ] = toNet;
587 PAD* other =
static_cast<PAD*
>( ci );
589 if( !swapPads.count( other ) )
590 nonSelectedPadsToChange.push_back( other );
596 for(
size_t pi = 0; pi < pinCount; ++pi )
598 for(
size_t ui = 0; ui < unitCount; ++ui )
601 size_t toIdx = ( ui + 1 ) % unitCount;
603 PAD* padFrom = unitPads[fromIdx][pi];
604 int fromNet = unitNets[fromIdx][pi];
605 int toNet = unitNets[toIdx][pi];
607 scheduleForPad( padFrom, fromNet, toNet );
611 bool includeConnectedPads =
true;
619 for(
size_t pi = 0; pi < pinCount; ++pi )
622 for(
size_t ui = 0; ui < unitCount; ++ui )
624 size_t toIdx = ( ui + 1 ) % unitCount;
625 PAD*
pad = unitPads[ui][pi];
626 int newNet = unitNets[toIdx][pi];
629 pad->SetNetCode( newNet );
634 for(
const auto&
kv : itemNewNets )
637 int newNet =
kv.second;
641 PAD* p =
static_cast<PAD*
>( item );
643 if( swapPads.count( p ) )
646 if( !includeConnectedPads )
654 if( !localCommit.
Empty() )
655 localCommit.
Push(
_(
"Swap Gate Nets" ) );
681 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
686 aCollector.
Remove( item );
692 std::vector<FOOTPRINT*> footprintsToPack;
695 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
697 if( footprintsToPack.empty() )
700 BOX2I footprintsBbox;
706 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
712 commit.
Push(
_(
"Pack Footprints" ) );
749 localCommit.
Push(
_(
"Move" ) );
766 typedef std::numeric_limits<int> coord_limits;
768 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
769 static const double min = -max;
772 testBox.
Offset( aBBoxOffset );
781 testBox.
Offset( aMovement );
789 if( testBox.
GetTop() < min )
810 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
835 controls->ForceCursorPosition(
false );
837 auto displayConstraintsMessage =
845 msg =
_(
"Angle snap lines: 45°" );
849 msg =
_(
"Angle snap lines: 90°" );
860 auto updateStatusPopup =
861 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
863 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
864 "Press <esc> to cancel all; double-click to finish" );
876 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
884 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
886 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
889 std::vector<BOARD_ITEM*> sel_items;
890 std::vector<BOARD_ITEM*> orig_items;
899 orig_items.push_back( boardItem );
901 sel_items.push_back( boardItem );
909 sel_items.push_back(
pad );
919 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
920 pickedReferencePoint ) )
925 editFrame->
PopTool( pushedEvent );
929 if( moveIndividually )
936 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
939 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
940 statusPopup->Popup();
942 canvas()->SetStatusPopup( statusPopup->GetPanel() );
948 sel_items.push_back( orig_items[ itemIdx ] );
951 bool restore_state =
false;
952 VECTOR2I originalPos = originalCursorPos;
955 bool updateBBox =
true;
961 bool enableLocalRatsnest =
true;
964 bool eatFirstMouseUp =
true;
970 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
971 long lastArrowKeyAction = 0;
974 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
976 if( showCourtyardConflicts )
978 std::shared_ptr<DRC_ENGINE> drcEngine =
m_toolMgr->GetTool<
DRC_TOOL>()->GetDRCEngine();
980 drc_on_move->Init(
board );
983 auto configureAngleSnap =
986 std::vector<VECTOR2I> directions;
1002 grid.SetSnapLineDirections( directions );
1004 if( directions.empty() )
1006 grid.ClearSnapLine();
1010 grid.SetSnapLineOrigin( originalPos );
1014 configureAngleSnap( angleSnapMode );
1015 displayConstraintsMessage( angleSnapMode );
1031 eatFirstMouseUp =
false;
1044 bool redraw3D =
false;
1053 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1054 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1056 grid.SetSnap(
false );
1062 if( axisLock == AXIS_LOCK::HORIZONTAL )
1068 axisLock = AXIS_LOCK::NONE;
1074 axisLock = AXIS_LOCK::HORIZONTAL;
1079 if( axisLock == AXIS_LOCK::VERTICAL )
1085 axisLock = AXIS_LOCK::NONE;
1091 axisLock = AXIS_LOCK::VERTICAL;
1095 lastArrowKeyAction = action;
1101 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1104 if( axisLock == AXIS_LOCK::HORIZONTAL )
1106 else if( axisLock == AXIS_LOCK::VERTICAL )
1114 originalBBox =
BOX2I();
1133 bboxMovement += movement;
1140 item->Move( movement );
1152 if( redraw3D && allowRedraw3D )
1155 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1158 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1179 enableLocalRatsnest =
false;
1192 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1194 item->RunOnChildren(
1200 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1211 grid.SetAuxAxes(
false );
1232 if( showCourtyardConflicts )
1234 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1239 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1241 item->RunOnChildren(
1245 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1258 if( moveWithReference )
1260 selection.SetReferencePoint( pickedReferencePoint );
1265 controls->ForceCursorPosition(
true, pickedReferencePoint );
1278 selection.SetReferencePoint( dragOrigin );
1282 grid.SetSnapLineOrigin( dragOrigin );
1284 grid.SetAuxAxes(
true, dragOrigin );
1297 originalPos =
selection.GetReferencePoint();
1313 if( enableLocalRatsnest )
1321 restore_state =
true;
1330 restore_state =
true;
1354 eatFirstMouseUp =
false;
1362 eatFirstMouseUp =
false;
1369 orig_items[itemIdx]->SetPosition( originalPos );
1371 view()->Update( orig_items[itemIdx] );
1374 if( ++itemIdx < orig_items.size() )
1382 selection.SetReferencePoint( originalPos );
1387 sel_items.push_back( nextItem );
1388 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1403 if( moveIndividually )
1404 orig_items[itemIdx]->SetPosition( originalPos );
1411 configureAngleSnap( angleSnapMode );
1412 displayConstraintsMessage( angleSnapMode );
1437 }
while( ( evt =
Wait() ) );
1440 if( showCourtyardConflicts )
1441 drc_on_move->ClearConflicts(
m_toolMgr->GetView() );
1443 controls->ForceCursorPosition(
false );
1458 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1466 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1472 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1479 editFrame->
PopTool( pushedEvent );
1482 return !restore_state;
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
static TOOL_ACTION duplicate
static TOOL_ACTION doDelete
static TOOL_ACTION cursorClick
static TOOL_ACTION increment
static TOOL_ACTION selectionClear
Clear the current selection.
static TOOL_ACTION refreshPreview
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Information pertinent to a Pcbnew printed circuit board.
constexpr const Vec & GetPosition() const
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr coord_type GetLeft() const
constexpr const Vec & GetOrigin() const
constexpr coord_type GetRight() const
constexpr const SizeVec & GetSize() const
constexpr coord_type GetTop() const
constexpr void Offset(coord_type dx, coord_type dy)
constexpr coord_type GetBottom() const
int GetCount() const
Return the number of objects in the list.
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
void DisplayConstraintsMsg(const wxString &msg)
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A base class for most all the KiCad significant classes used in schematics and boards.
virtual VECTOR2I GetPosition() const
virtual void SetPosition(const VECTOR2I &aPos)
wxString GetTypeDesc() const
Return a translated description of the type for this EDA_ITEM for display in user facing messages.
void SetFlags(EDA_ITEM_FLAGS aMask)
KICAD_T Type() const
Returns the type of object.
EDA_ITEM * GetParent() const
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
static const TOOL_EVENT SelectedEvent
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Used when the right click button is pressed, or when the select tool is in effect.
An interface for classes handling user events controlling the view behavior such as zooming,...
bool IsBOARD_ITEM() const
LSET is a set of PCB_LAYER_IDs.
DISPLAY_OPTIONS m_Display
bool m_ShowCourtyardCollisions
static TOOL_ACTION mirrorH
Mirroring of selected items.
static TOOL_ACTION genFinishEdit
static TOOL_ACTION hideLocalRatsnest
static TOOL_ACTION genStartEdit
static TOOL_ACTION moveWithReference
move with a reference point
static TOOL_ACTION angleSnapModeChanged
Notification event when angle mode changes.
static TOOL_ACTION moveExact
Activation of the exact move tool.
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
static TOOL_ACTION genCancelEdit
static TOOL_ACTION genUpdateEdit
static TOOL_ACTION updateLocalRatsnest
static TOOL_ACTION moveIndividually
move items one-by-one
static TOOL_ACTION interactiveOffsetTool
static TOOL_ACTION positionRelative
static TOOL_ACTION move
move or drag an item
static TOOL_ACTION mirrorV
static TOOL_ACTION flip
Flipping of selected objects.
static TOOL_ACTION rotateCw
Rotation of selected objects.
static TOOL_ACTION rotateCcw
Common, abstract interface for edit frames.
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual PCB_LAYER_ID GetActiveLayer() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual void Update3DView(bool aMarkDirty, bool aRefresh, const wxString *aTitle=nullptr)
Update the 3D view, if the viewer is opened by this frame.
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
#define IS_MOVING
Item being moved.
a few functions useful in geometry calculations.
LEADER_MODE
The kind of the leader line.
@ DIRECT
Unconstrained point-to-point.
PCB_LAYER_ID
A quick note on layer IDs:
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Class to handle a set of BOARD_ITEMs.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D