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 );
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()];
166 std::swap( aLayer, bLayer );
175 std::swap( aPos, bPos );
198 std::swap( aAngle, bAngle );
208 std::swap( aLayer, bLayer );
214 if( !localCommit.
Empty() )
215 localCommit.
Push(
_(
"Swap" ) );
238 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
239 std::vector<PAD*> pads;
240 const size_t padsCount = orderedPads.size();
243 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
246 std::vector<int> originalNets( padsCount );
247 std::unordered_set<PAD*> selectedPads;
249 for(
size_t i = 0; i < padsCount; ++i )
251 originalNets[i] = pads[i]->GetNetCode();
252 selectedPads.insert( pads[i] );
258 for(
size_t i = 1; i < padsCount; ++i )
260 if( originalNets[i] != originalNets[0] )
271 auto newNetForIndex =
274 return originalNets[( i + 1 ) % padsCount];
282 commit = &localCommit;
285 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
288 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
289 std::vector<PAD*> nonSelectedPadsToChange;
291 for(
size_t i = 0; i < padsCount; ++i )
294 int fromNet = originalNets[i];
295 int toNet = newNetForIndex( i );
312 if( ci->GetNetCode() != fromNet )
316 itemNewNets[ci] = toNet;
320 PAD* otherPad =
static_cast<PAD*
>( ci );
322 if( !selectedPads.count( otherPad ) )
323 nonSelectedPadsToChange.push_back( otherPad );
328 bool includeConnectedPads =
true;
335 for(
size_t i = 0; i < padsCount; ++i )
337 commit->
Modify( pads[i] );
338 pads[i]->SetNetCode( newNetForIndex( i ) );
342 for(
const auto& itemNewNet : itemNewNets )
345 int newNet = itemNewNet.second;
349 PAD* p =
static_cast<PAD*
>( item );
351 if( selectedPads.count( p ) )
354 if( !includeConnectedPads )
362 if( !localCommit.
Empty() )
363 localCommit.
Push(
_(
"Swap Pad Nets" ) );
384 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
409 else if( fp && targetFp != fp )
416 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
426 std::vector<bool> unitHit( units.size(),
false );
427 std::vector<int> unitOrder;
429 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
435 const wxString& padNum =
pad->GetNumber();
438 for(
size_t i = 0; i < units.size(); ++i )
440 for(
const auto& p : units[i].m_pins )
444 unitIdx =
static_cast<int>( i );
447 unitOrder.push_back( unitIdx );
460 std::vector<int> activeUnitIdx;
463 if( unitOrder.size() >= 2 )
465 activeUnitIdx = unitOrder;
466 sourceIdx = unitOrder.front();
469 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
471 sourceIdx = unitOrder.front();
472 wxString targetUnitByName = aEvent.
Parameter<wxString>();
476 for(
size_t i = 0; i < units.size(); ++i )
478 if(
static_cast<int>( i ) == sourceIdx )
481 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
482 targetIdx =
static_cast<int>( i );
491 activeUnitIdx.push_back( sourceIdx );
492 activeUnitIdx.push_back( targetIdx );
501 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
503 for(
int idx : activeUnitIdx )
505 if( units[idx].m_pins.size() != pinCount )
507 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
513 const size_t unitCount = activeUnitIdx.size();
514 std::vector<std::vector<PAD*>> unitPads( unitCount );
515 std::vector<std::vector<int>> unitNets( unitCount );
517 for(
size_t ui = 0; ui < unitCount; ++ui )
519 int uidx = activeUnitIdx[ui];
520 const auto& pins = units[uidx].m_pins;
522 for(
size_t pi = 0; pi < pinCount; ++pi )
528 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
532 unitPads[ui].push_back( p );
540 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
542 int refNet = unitNets[0][pi];
544 for(
size_t ui = 1; ui < unitCount; ++ui )
546 if( unitNets[ui][pi] != refNet )
556 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
565 commit = &localCommit;
567 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
570 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
571 std::vector<PAD*> nonSelectedPadsToChange;
574 std::unordered_set<PAD*> swapPads;
576 for(
const auto& v : unitPads )
577 swapPads.insert( v.begin(), v.end() );
580 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
596 if( ci->GetNetCode() != fromNet )
599 itemNewNets[ ci ] = toNet;
603 PAD* other =
static_cast<PAD*
>( ci );
605 if( !swapPads.count( other ) )
606 nonSelectedPadsToChange.push_back( other );
612 for(
size_t pi = 0; pi < pinCount; ++pi )
614 for(
size_t ui = 0; ui < unitCount; ++ui )
617 size_t toIdx = ( ui + 1 ) % unitCount;
619 PAD* padFrom = unitPads[fromIdx][pi];
620 int fromNet = unitNets[fromIdx][pi];
621 int toNet = unitNets[toIdx][pi];
623 scheduleForPad( padFrom, fromNet, toNet );
627 bool includeConnectedPads =
true;
635 for(
size_t pi = 0; pi < pinCount; ++pi )
638 for(
size_t ui = 0; ui < unitCount; ++ui )
640 size_t toIdx = ( ui + 1 ) % unitCount;
641 PAD*
pad = unitPads[ui][pi];
642 int newNet = unitNets[toIdx][pi];
645 pad->SetNetCode( newNet );
650 for(
const auto&
kv : itemNewNets )
653 int newNet =
kv.second;
657 PAD* p =
static_cast<PAD*
>( item );
659 if( swapPads.count( p ) )
662 if( !includeConnectedPads )
670 if( !localCommit.
Empty() )
671 localCommit.
Push(
_(
"Swap Gate Nets" ) );
697 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
702 aCollector.
Remove( item );
710 std::vector<FOOTPRINT*> footprintsToPack;
713 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
715 if( footprintsToPack.empty() )
718 BOX2I footprintsBbox;
724 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
730 commit.
Push(
_(
"Pack Footprints" ) );
767 localCommit.
Push(
_(
"Move" ) );
784 typedef std::numeric_limits<int> coord_limits;
786 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
787 static const double min = -max;
790 testBox.
Offset( aBBoxOffset );
799 testBox.
Offset( aMovement );
807 if( testBox.
GetTop() < min )
828 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
858 controls->ForceCursorPosition(
false );
860 auto displayConstraintsMessage =
868 msg =
_(
"Angle snap lines: 45°" );
872 msg =
_(
"Angle snap lines: 90°" );
883 auto updateStatusPopup =
884 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
886 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
887 "Press <esc> to cancel all; double-click to finish" );
899 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
907 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
909 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
912 std::vector<BOARD_ITEM*> sel_items;
913 std::vector<BOARD_ITEM*> orig_items;
922 orig_items.push_back( boardItem );
924 sel_items.push_back( boardItem );
932 sel_items.push_back(
pad );
942 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
943 pickedReferencePoint ) )
948 editFrame->
PopTool( pushedEvent );
954 if( moveIndividually )
961 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
964 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
965 statusPopup->Popup();
967 canvas()->SetStatusPopup( statusPopup->GetPanel() );
973 sel_items.push_back( orig_items[ itemIdx ] );
976 bool restore_state =
false;
977 VECTOR2I originalPos = originalCursorPos;
980 bool updateBBox =
true;
986 bool enableLocalRatsnest =
true;
989 bool eatFirstMouseUp =
true;
995 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
996 long lastArrowKeyAction = 0;
1000 std::shared_ptr<DRC_ENGINE> drcEngine = drcTool ? drcTool->
GetDRCEngine() :
nullptr;
1003 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
1005 if( showCourtyardConflicts )
1008 drc_on_move->Init(
board );
1012 std::unique_ptr<CREEPAGE_OVERLAY> creepage_on_move =
1013 std::make_unique<CREEPAGE_OVERLAY>(
board, drcEngine,
m_toolMgr->GetView() );
1015 auto configureAngleSnap =
1018 std::vector<VECTOR2I> directions;
1034 grid.SetSnapLineDirections( directions );
1036 if( directions.empty() )
1038 grid.ClearSnapLine();
1042 grid.SetSnapLineOrigin( originalPos );
1046 configureAngleSnap( angleSnapMode );
1047 displayConstraintsMessage( angleSnapMode );
1063 eatFirstMouseUp =
false;
1076 bool redraw3D =
false;
1080 if(
controls->GetSettings().m_lastKeyboardCursorPositionValid )
1082 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1084 grid.SetSnap(
false );
1098 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1102 if( axisLock == AXIS_LOCK::HORIZONTAL )
1108 axisLock = AXIS_LOCK::NONE;
1114 axisLock = AXIS_LOCK::HORIZONTAL;
1119 if( axisLock == AXIS_LOCK::VERTICAL )
1125 axisLock = AXIS_LOCK::NONE;
1131 axisLock = AXIS_LOCK::VERTICAL;
1135 lastArrowKeyAction = action;
1142 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1145 if( axisLock == AXIS_LOCK::HORIZONTAL )
1147 else if( axisLock == AXIS_LOCK::VERTICAL )
1155 originalBBox =
BOX2I();
1174 bboxMovement += movement;
1182 item->Move( movement );
1200 if( redraw3D && allowRedraw3D )
1203 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1206 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1209 creepage_on_move->Update();
1229 enableLocalRatsnest =
false;
1242 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1244 item->RunOnChildren(
1250 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1261 grid.SetAuxAxes(
false );
1276 boardItem->
Move( movement );
1288 if( showCourtyardConflicts )
1290 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1295 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1297 item->RunOnChildren(
1301 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1307 creepage_on_move->Start( sel_items );
1316 if( moveWithReference )
1318 selection.SetReferencePoint( pickedReferencePoint );
1323 controls->ForceCursorPosition(
true, pickedReferencePoint );
1330 selection.SetReferencePoint( dragOrigin );
1333 grid.SetSnapLineOrigin( dragOrigin );
1335 grid.SetAuxAxes(
true, dragOrigin );
1343 originalPos =
selection.GetReferencePoint();
1359 if( enableLocalRatsnest )
1367 restore_state =
true;
1376 restore_state =
true;
1400 eatFirstMouseUp =
false;
1408 eatFirstMouseUp =
false;
1415 orig_items[itemIdx]->SetPosition( originalPos );
1417 view()->Update( orig_items[itemIdx] );
1420 if( ++itemIdx < orig_items.size() )
1428 selection.SetReferencePoint( originalPos );
1433 sel_items.push_back( nextItem );
1434 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1454 if( moveIndividually )
1455 orig_items[itemIdx]->SetPosition( originalPos );
1462 configureAngleSnap( angleSnapMode );
1463 displayConstraintsMessage( angleSnapMode );
1488 }
while( ( evt =
Wait() ) );
1491 if( showCourtyardConflicts )
1492 drc_on_move->ClearConflicts(
m_toolMgr->GetView() );
1494 creepage_on_move->Stop();
1496 controls->ForceCursorPosition(
false );
1511 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1519 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1525 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1532 editFrame->
PopTool( pushedEvent );
1536 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,...
virtual 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.
static void SwapShapePositions(PAD *aLhs, PAD *aRhs)
Swap the visible shape positions of two pads, preserving each pad's own shape offset.
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