58#include <wx/richmsgdlg.h>
59#include <wx/choicdlg.h>
60#include <unordered_set>
61#include <unordered_map>
65 const wxString& aDialogTitle,
bool& aIncludeConnectedPads )
69 aIncludeConnectedPads =
true;
73 std::unordered_set<PAD*> uniquePads( aPads.begin(), aPads.end() );
76 msg.Printf(
_(
"%zu unselected pad(s) are connected to these nets. How do you want to proceed?" ),
80 details <<
_(
"Connected tracks, vias, and other non-zone copper items will still swap nets"
81 " even if you ignore the unselected pads." )
83 <<
_(
"Unselected pads:" ) <<
'\n';
85 for(
PAD*
pad : uniquePads )
88 details << wxS(
" • " ) << ( fp ? fp->
GetReference() :
_(
"<no reference designator>" ) ) << wxS(
":" )
89 <<
pad->GetNumber() <<
'\n';
93 wxRichMessageDialog dlg( aFrame, msg, aDialogTitle, wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING );
94 dlg.SetYesNoLabels(
_(
"Ignore Unselected Pads" ),
_(
"Swap All Connected Pads" ) );
95 dlg.SetExtendedMessage( details );
97 int ret = dlg.ShowModal();
99 if( ret == wxID_CANCEL )
102 aIncludeConnectedPads = ( ret == wxID_NO );
123 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
128 aCollector.
Remove( item );
143 commit = &localCommit;
145 std::vector<EDA_ITEM*> sorted =
selection.GetItemsSortedBySelectionOrder();
151 for(
size_t i = 0; i < sorted.size() - 1; i++ )
154 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
169 std::swap( aLayer, bLayer );
178 std::swap( aPos, bPos );
201 std::swap( aAngle, bAngle );
211 std::swap( aLayer, bLayer );
217 if( !localCommit.
Empty() )
218 localCommit.
Push(
_(
"Swap" ) );
241 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
242 std::vector<PAD*> pads;
243 const size_t padsCount = orderedPads.size();
246 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
249 std::vector<int> originalNets( padsCount );
250 std::unordered_set<PAD*> selectedPads;
252 for(
size_t i = 0; i < padsCount; ++i )
254 originalNets[i] = pads[i]->GetNetCode();
255 selectedPads.insert( pads[i] );
261 for(
size_t i = 1; i < padsCount; ++i )
263 if( originalNets[i] != originalNets[0] )
274 auto newNetForIndex =
277 return originalNets[( i + 1 ) % padsCount];
285 commit = &localCommit;
288 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
291 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
292 std::vector<PAD*> nonSelectedPadsToChange;
294 for(
size_t i = 0; i < padsCount; ++i )
297 int fromNet = originalNets[i];
298 int toNet = newNetForIndex( i );
315 if( ci->GetNetCode() != fromNet )
319 itemNewNets[ci] = toNet;
323 PAD* otherPad =
static_cast<PAD*
>( ci );
325 if( !selectedPads.count( otherPad ) )
326 nonSelectedPadsToChange.push_back( otherPad );
331 bool includeConnectedPads =
true;
338 for(
size_t i = 0; i < padsCount; ++i )
340 commit->
Modify( pads[i] );
341 pads[i]->SetNetCode( newNetForIndex( i ) );
345 for(
const auto& itemNewNet : itemNewNets )
348 int newNet = itemNewNet.second;
352 PAD* p =
static_cast<PAD*
>( item );
354 if( selectedPads.count( p ) )
357 if( !includeConnectedPads )
365 if( !localCommit.
Empty() )
366 localCommit.
Push(
_(
"Swap Pad Nets" ) );
387 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
412 else if( fp && targetFp != fp )
419 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
429 std::vector<bool> unitHit( units.size(),
false );
430 std::vector<int> unitOrder;
432 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
438 const wxString& padNum =
pad->GetNumber();
441 for(
size_t i = 0; i < units.size(); ++i )
443 for(
const auto& p : units[i].m_pins )
447 unitIdx =
static_cast<int>( i );
450 unitOrder.push_back( unitIdx );
463 std::vector<int> activeUnitIdx;
466 if( unitOrder.size() >= 2 )
468 activeUnitIdx = unitOrder;
469 sourceIdx = unitOrder.front();
472 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
474 sourceIdx = unitOrder.front();
475 wxString targetUnitByName = aEvent.
Parameter<wxString>();
479 for(
size_t i = 0; i < units.size(); ++i )
481 if(
static_cast<int>( i ) == sourceIdx )
484 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
485 targetIdx =
static_cast<int>( i );
494 activeUnitIdx.push_back( sourceIdx );
495 activeUnitIdx.push_back( targetIdx );
504 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
506 for(
int idx : activeUnitIdx )
508 if( units[idx].m_pins.size() != pinCount )
510 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
516 const size_t unitCount = activeUnitIdx.size();
517 std::vector<std::vector<PAD*>> unitPads( unitCount );
518 std::vector<std::vector<int>> unitNets( unitCount );
520 for(
size_t ui = 0; ui < unitCount; ++ui )
522 int uidx = activeUnitIdx[ui];
523 const auto& pins = units[uidx].m_pins;
525 for(
size_t pi = 0; pi < pinCount; ++pi )
531 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
535 unitPads[ui].push_back( p );
543 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
545 int refNet = unitNets[0][pi];
547 for(
size_t ui = 1; ui < unitCount; ++ui )
549 if( unitNets[ui][pi] != refNet )
559 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
568 commit = &localCommit;
570 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
573 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
574 std::vector<PAD*> nonSelectedPadsToChange;
577 std::unordered_set<PAD*> swapPads;
579 for(
const auto& v : unitPads )
580 swapPads.insert( v.begin(), v.end() );
583 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
599 if( ci->GetNetCode() != fromNet )
602 itemNewNets[ ci ] = toNet;
606 PAD* other =
static_cast<PAD*
>( ci );
608 if( !swapPads.count( other ) )
609 nonSelectedPadsToChange.push_back( other );
615 for(
size_t pi = 0; pi < pinCount; ++pi )
617 for(
size_t ui = 0; ui < unitCount; ++ui )
620 size_t toIdx = ( ui + 1 ) % unitCount;
622 PAD* padFrom = unitPads[fromIdx][pi];
623 int fromNet = unitNets[fromIdx][pi];
624 int toNet = unitNets[toIdx][pi];
626 scheduleForPad( padFrom, fromNet, toNet );
630 bool includeConnectedPads =
true;
638 for(
size_t pi = 0; pi < pinCount; ++pi )
641 for(
size_t ui = 0; ui < unitCount; ++ui )
643 size_t toIdx = ( ui + 1 ) % unitCount;
644 PAD*
pad = unitPads[ui][pi];
645 int newNet = unitNets[toIdx][pi];
648 pad->SetNetCode( newNet );
653 for(
const auto&
kv : itemNewNets )
656 int newNet =
kv.second;
660 PAD* p =
static_cast<PAD*
>( item );
662 if( swapPads.count( p ) )
665 if( !includeConnectedPads )
673 if( !localCommit.
Empty() )
674 localCommit.
Push(
_(
"Swap Gate Nets" ) );
700 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
705 aCollector.
Remove( item );
713 std::vector<FOOTPRINT*> footprintsToPack;
716 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
718 if( footprintsToPack.empty() )
721 BOX2I footprintsBbox;
727 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
733 commit.
Push(
_(
"Pack Footprints" ) );
770 localCommit.
Push(
_(
"Move" ) );
787 typedef std::numeric_limits<int> coord_limits;
789 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
790 static const double min = -max;
793 testBox.
Offset( aBBoxOffset );
802 testBox.
Offset( aMovement );
810 if( testBox.
GetTop() < min )
831 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
861 controls->ForceCursorPosition(
false );
863 auto displayConstraintsMessage =
871 msg =
_(
"Angle snap lines: 45°" );
875 msg =
_(
"Angle snap lines: 90°" );
886 auto updateStatusPopup =
887 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
889 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
890 "Press <esc> to cancel all; double-click to finish" );
902 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
910 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
912 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
915 std::vector<BOARD_ITEM*> sel_items;
916 std::vector<BOARD_ITEM*> orig_items;
925 orig_items.push_back( boardItem );
927 sel_items.push_back( boardItem );
935 sel_items.push_back(
pad );
945 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
946 pickedReferencePoint ) )
951 editFrame->
PopTool( pushedEvent );
955 if( moveIndividually )
962 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
965 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
966 statusPopup->Popup();
968 canvas()->SetStatusPopup( statusPopup->GetPanel() );
974 sel_items.push_back( orig_items[ itemIdx ] );
977 bool restore_state =
false;
978 VECTOR2I originalPos = originalCursorPos;
981 bool updateBBox =
true;
987 bool enableLocalRatsnest =
true;
990 bool eatFirstMouseUp =
true;
996 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
997 long lastArrowKeyAction = 0;
1000 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
1002 if( showCourtyardConflicts )
1004 std::shared_ptr<DRC_ENGINE> drcEngine =
m_toolMgr->GetTool<
DRC_TOOL>()->GetDRCEngine();
1006 drc_on_move->Init(
board );
1009 auto configureAngleSnap =
1012 std::vector<VECTOR2I> directions;
1028 grid.SetSnapLineDirections( directions );
1030 if( directions.empty() )
1032 grid.ClearSnapLine();
1036 grid.SetSnapLineOrigin( originalPos );
1040 configureAngleSnap( angleSnapMode );
1041 displayConstraintsMessage( angleSnapMode );
1057 eatFirstMouseUp =
false;
1070 bool redraw3D =
false;
1074 if(
controls->GetSettings().m_lastKeyboardCursorPositionValid )
1076 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1078 grid.SetSnap(
false );
1092 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1096 if( axisLock == AXIS_LOCK::HORIZONTAL )
1102 axisLock = AXIS_LOCK::NONE;
1108 axisLock = AXIS_LOCK::HORIZONTAL;
1113 if( axisLock == AXIS_LOCK::VERTICAL )
1119 axisLock = AXIS_LOCK::NONE;
1125 axisLock = AXIS_LOCK::VERTICAL;
1129 lastArrowKeyAction = action;
1136 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1139 if( axisLock == AXIS_LOCK::HORIZONTAL )
1141 else if( axisLock == AXIS_LOCK::VERTICAL )
1149 originalBBox =
BOX2I();
1168 bboxMovement += movement;
1176 item->Move( movement );
1194 if( redraw3D && allowRedraw3D )
1197 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1200 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1221 enableLocalRatsnest =
false;
1234 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1236 item->RunOnChildren(
1242 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1253 grid.SetAuxAxes(
false );
1268 boardItem->
Move( movement );
1280 if( showCourtyardConflicts )
1282 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1287 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1289 item->RunOnChildren(
1293 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1306 if( moveWithReference )
1308 selection.SetReferencePoint( pickedReferencePoint );
1313 controls->ForceCursorPosition(
true, pickedReferencePoint );
1320 selection.SetReferencePoint( dragOrigin );
1323 grid.SetSnapLineOrigin( dragOrigin );
1325 grid.SetAuxAxes(
true, dragOrigin );
1333 originalPos =
selection.GetReferencePoint();
1349 if( enableLocalRatsnest )
1357 restore_state =
true;
1366 restore_state =
true;
1390 eatFirstMouseUp =
false;
1398 eatFirstMouseUp =
false;
1405 orig_items[itemIdx]->SetPosition( originalPos );
1407 view()->Update( orig_items[itemIdx] );
1410 if( ++itemIdx < orig_items.size() )
1418 selection.SetReferencePoint( originalPos );
1423 sel_items.push_back( nextItem );
1424 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1444 if( moveIndividually )
1445 orig_items[itemIdx]->SetPosition( originalPos );
1452 configureAngleSnap( angleSnapMode );
1453 displayConstraintsMessage( angleSnapMode );
1478 }
while( ( evt =
Wait() ) );
1481 if( showCourtyardConflicts )
1482 drc_on_move->ClearConflicts(
m_toolMgr->GetView() );
1484 controls->ForceCursorPosition(
false );
1499 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1507 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1513 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1520 editFrame->
PopTool( pushedEvent );
1523 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