57#include <wx/richmsgdlg.h>
58#include <wx/choicdlg.h>
59#include <unordered_set>
60#include <unordered_map>
64 const wxString& aDialogTitle,
bool& aIncludeConnectedPads )
68 aIncludeConnectedPads =
true;
72 std::unordered_set<PAD*> uniquePads( aPads.begin(), aPads.end() );
75 msg.Printf(
_(
"%zu unselected pad(s) are connected to these nets. How do you want to proceed?" ),
79 details <<
_(
"Connected tracks, vias, and other non-zone copper items will still swap nets"
80 " even if you ignore the unselected pads." )
82 <<
_(
"Unselected pads:" ) <<
'\n';
84 for(
PAD*
pad : uniquePads )
87 details << wxS(
" • " ) << ( fp ? fp->
GetReference() :
_(
"<no reference designator>" ) ) << wxS(
":" )
88 <<
pad->GetNumber() <<
'\n';
92 wxRichMessageDialog dlg( aFrame, msg, aDialogTitle, wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING );
93 dlg.SetYesNoLabels(
_(
"Ignore Unselected Pads" ),
_(
"Swap All Connected Pads" ) );
94 dlg.SetExtendedMessage( details );
96 int ret = dlg.ShowModal();
98 if( ret == wxID_CANCEL )
101 aIncludeConnectedPads = ( ret == wxID_NO );
122 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
127 aCollector.
Remove( item );
140 commit = &localCommit;
142 std::vector<EDA_ITEM*> sorted =
selection.GetItemsSortedBySelectionOrder();
148 for(
size_t i = 0; i < sorted.size() - 1; i++ )
151 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
161 std::swap( aPos, bPos );
184 std::swap( aAngle, bAngle );
194 std::swap( aLayer, bLayer );
200 if( !localCommit.
Empty() )
201 localCommit.
Push(
_(
"Swap" ) );
224 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
225 std::vector<PAD*> pads;
226 const size_t padsCount = orderedPads.size();
229 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
232 std::vector<int> originalNets( padsCount );
233 std::unordered_set<PAD*> selectedPads;
235 for(
size_t i = 0; i < padsCount; ++i )
237 originalNets[i] = pads[i]->GetNetCode();
238 selectedPads.insert( pads[i] );
244 for(
size_t i = 1; i < padsCount; ++i )
246 if( originalNets[i] != originalNets[0] )
257 auto newNetForIndex =
260 return originalNets[( i + 1 ) % padsCount];
268 commit = &localCommit;
271 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
274 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
275 std::vector<PAD*> nonSelectedPadsToChange;
277 for(
size_t i = 0; i < padsCount; ++i )
280 int fromNet = originalNets[i];
281 int toNet = newNetForIndex( i );
298 if( ci->GetNetCode() != fromNet )
302 itemNewNets[ci] = toNet;
306 PAD* otherPad =
static_cast<PAD*
>( ci );
308 if( !selectedPads.count( otherPad ) )
309 nonSelectedPadsToChange.push_back( otherPad );
314 bool includeConnectedPads =
true;
321 for(
size_t i = 0; i < padsCount; ++i )
323 commit->
Modify( pads[i] );
324 pads[i]->SetNetCode( newNetForIndex( i ) );
328 for(
const auto& itemNewNet : itemNewNets )
331 int newNet = itemNewNet.second;
335 PAD* p =
static_cast<PAD*
>( item );
337 if( selectedPads.count( p ) )
340 if( !includeConnectedPads )
348 if( !localCommit.
Empty() )
349 localCommit.
Push(
_(
"Swap Pad Nets" ) );
370 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
395 else if( fp && targetFp != fp )
402 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
412 std::vector<bool> unitHit( units.size(),
false );
413 std::vector<int> unitOrder;
415 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
421 const wxString& padNum =
pad->GetNumber();
424 for(
size_t i = 0; i < units.size(); ++i )
426 for(
const auto& p : units[i].m_pins )
430 unitIdx =
static_cast<int>( i );
433 unitOrder.push_back( unitIdx );
446 std::vector<int> activeUnitIdx;
449 if( unitOrder.size() >= 2 )
451 activeUnitIdx = unitOrder;
452 sourceIdx = unitOrder.front();
455 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
457 sourceIdx = unitOrder.front();
458 wxString targetUnitByName = aEvent.
Parameter<wxString>();
462 for(
size_t i = 0; i < units.size(); ++i )
464 if(
static_cast<int>( i ) == sourceIdx )
467 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
468 targetIdx =
static_cast<int>( i );
477 activeUnitIdx.push_back( sourceIdx );
478 activeUnitIdx.push_back( targetIdx );
487 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
489 for(
int idx : activeUnitIdx )
491 if( units[idx].m_pins.size() != pinCount )
493 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
499 const size_t unitCount = activeUnitIdx.size();
500 std::vector<std::vector<PAD*>> unitPads( unitCount );
501 std::vector<std::vector<int>> unitNets( unitCount );
503 for(
size_t ui = 0; ui < unitCount; ++ui )
505 int uidx = activeUnitIdx[ui];
506 const auto& pins = units[uidx].m_pins;
508 for(
size_t pi = 0; pi < pinCount; ++pi )
514 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
518 unitPads[ui].push_back( p );
526 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
528 int refNet = unitNets[0][pi];
530 for(
size_t ui = 1; ui < unitCount; ++ui )
532 if( unitNets[ui][pi] != refNet )
542 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
551 commit = &localCommit;
553 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
556 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
557 std::vector<PAD*> nonSelectedPadsToChange;
560 std::unordered_set<PAD*> swapPads;
562 for(
const auto& v : unitPads )
563 swapPads.insert( v.begin(), v.end() );
566 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
582 if( ci->GetNetCode() != fromNet )
585 itemNewNets[ ci ] = toNet;
589 PAD* other =
static_cast<PAD*
>( ci );
591 if( !swapPads.count( other ) )
592 nonSelectedPadsToChange.push_back( other );
598 for(
size_t pi = 0; pi < pinCount; ++pi )
600 for(
size_t ui = 0; ui < unitCount; ++ui )
603 size_t toIdx = ( ui + 1 ) % unitCount;
605 PAD* padFrom = unitPads[fromIdx][pi];
606 int fromNet = unitNets[fromIdx][pi];
607 int toNet = unitNets[toIdx][pi];
609 scheduleForPad( padFrom, fromNet, toNet );
613 bool includeConnectedPads =
true;
621 for(
size_t pi = 0; pi < pinCount; ++pi )
624 for(
size_t ui = 0; ui < unitCount; ++ui )
626 size_t toIdx = ( ui + 1 ) % unitCount;
627 PAD*
pad = unitPads[ui][pi];
628 int newNet = unitNets[toIdx][pi];
631 pad->SetNetCode( newNet );
636 for(
const auto&
kv : itemNewNets )
639 int newNet =
kv.second;
643 PAD* p =
static_cast<PAD*
>( item );
645 if( swapPads.count( p ) )
648 if( !includeConnectedPads )
656 if( !localCommit.
Empty() )
657 localCommit.
Push(
_(
"Swap Gate Nets" ) );
683 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
688 aCollector.
Remove( item );
694 std::vector<FOOTPRINT*> footprintsToPack;
697 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
699 if( footprintsToPack.empty() )
702 BOX2I footprintsBbox;
708 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
714 commit.
Push(
_(
"Pack Footprints" ) );
751 localCommit.
Push(
_(
"Move" ) );
768 typedef std::numeric_limits<int> coord_limits;
770 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
771 static const double min = -max;
774 testBox.
Offset( aBBoxOffset );
783 testBox.
Offset( aMovement );
791 if( testBox.
GetTop() < min )
812 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
837 controls->ForceCursorPosition(
false );
839 auto displayConstraintsMessage =
847 msg =
_(
"Angle snap lines: 45°" );
851 msg =
_(
"Angle snap lines: 90°" );
862 auto updateStatusPopup =
863 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
865 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
866 "Press <esc> to cancel all; double-click to finish" );
878 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
886 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
888 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
891 std::vector<BOARD_ITEM*> sel_items;
892 std::vector<BOARD_ITEM*> orig_items;
901 orig_items.push_back( boardItem );
903 sel_items.push_back( boardItem );
911 sel_items.push_back(
pad );
921 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
922 pickedReferencePoint ) )
927 editFrame->
PopTool( pushedEvent );
931 if( moveIndividually )
938 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
941 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
942 statusPopup->Popup();
944 canvas()->SetStatusPopup( statusPopup->GetPanel() );
950 sel_items.push_back( orig_items[ itemIdx ] );
953 bool restore_state =
false;
954 VECTOR2I originalPos = originalCursorPos;
957 bool updateBBox =
true;
963 bool enableLocalRatsnest =
true;
966 bool eatFirstMouseUp =
true;
972 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
973 long lastArrowKeyAction = 0;
976 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
978 if( showCourtyardConflicts )
980 std::shared_ptr<DRC_ENGINE> drcEngine =
m_toolMgr->GetTool<
DRC_TOOL>()->GetDRCEngine();
982 drc_on_move->Init(
board );
985 auto configureAngleSnap =
988 std::vector<VECTOR2I> directions;
1004 grid.SetSnapLineDirections( directions );
1006 if( directions.empty() )
1008 grid.ClearSnapLine();
1012 grid.SetSnapLineOrigin( originalPos );
1016 configureAngleSnap( angleSnapMode );
1017 displayConstraintsMessage( angleSnapMode );
1033 eatFirstMouseUp =
false;
1046 bool redraw3D =
false;
1050 if(
controls->GetSettings().m_lastKeyboardCursorPositionValid )
1052 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1054 grid.SetSnap(
false );
1068 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1072 if( axisLock == AXIS_LOCK::HORIZONTAL )
1078 axisLock = AXIS_LOCK::NONE;
1084 axisLock = AXIS_LOCK::HORIZONTAL;
1089 if( axisLock == AXIS_LOCK::VERTICAL )
1095 axisLock = AXIS_LOCK::NONE;
1101 axisLock = AXIS_LOCK::VERTICAL;
1105 lastArrowKeyAction = action;
1112 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1115 if( axisLock == AXIS_LOCK::HORIZONTAL )
1117 else if( axisLock == AXIS_LOCK::VERTICAL )
1125 originalBBox =
BOX2I();
1144 bboxMovement += movement;
1152 item->Move( movement );
1170 if( redraw3D && allowRedraw3D )
1173 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1176 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1197 enableLocalRatsnest =
false;
1210 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1212 item->RunOnChildren(
1218 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1229 grid.SetAuxAxes(
false );
1244 boardItem->
Move( movement );
1256 if( showCourtyardConflicts )
1258 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1263 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1265 item->RunOnChildren(
1269 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1282 if( moveWithReference )
1284 selection.SetReferencePoint( pickedReferencePoint );
1289 controls->ForceCursorPosition(
true, pickedReferencePoint );
1296 selection.SetReferencePoint( dragOrigin );
1299 grid.SetSnapLineOrigin( dragOrigin );
1301 grid.SetAuxAxes(
true, dragOrigin );
1309 originalPos =
selection.GetReferencePoint();
1325 if( enableLocalRatsnest )
1333 restore_state =
true;
1342 restore_state =
true;
1366 eatFirstMouseUp =
false;
1374 eatFirstMouseUp =
false;
1381 orig_items[itemIdx]->SetPosition( originalPos );
1383 view()->Update( orig_items[itemIdx] );
1386 if( ++itemIdx < orig_items.size() )
1394 selection.SetReferencePoint( originalPos );
1399 sel_items.push_back( nextItem );
1400 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1420 if( moveIndividually )
1421 orig_items[itemIdx]->SetPosition( originalPos );
1428 configureAngleSnap( angleSnapMode );
1429 displayConstraintsMessage( angleSnapMode );
1454 }
while( ( evt =
Wait() ) );
1457 if( showCourtyardConflicts )
1458 drc_on_move->ClearConflicts(
m_toolMgr->GetView() );
1460 controls->ForceCursorPosition(
false );
1475 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1483 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1489 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1496 editFrame->
PopTool( pushedEvent );
1499 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 MAGNETIC_SETTINGS * GetMagneticItemsSettings()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual PCB_LAYER_ID GetActiveLayer() const
virtual void Update3DView(bool aMarkDirty, bool aRefresh, const wxString *aTitle=nullptr)
Update the 3D view, if the viewer is opened by this frame.
#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)
@ GEOMETRY
Position or shape has changed.
Class to handle a set of BOARD_ITEMs.
std::vector< EDA_ITEM * > EDA_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_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
@ 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