56#include <wx/richmsgdlg.h>
57#include <wx/choicdlg.h>
58#include <unordered_set>
59#include <unordered_map>
63 const wxString& aDialogTitle,
bool& aIncludeConnectedPads )
67 aIncludeConnectedPads =
true;
71 std::unordered_set<PAD*> uniquePads( aPads.begin(), aPads.end() );
74 msg.Printf(
_(
"%zu unselected pad(s) are connected to these nets. How do you want to proceed?" ),
78 details <<
_(
"Connected tracks, vias, and other non-zone copper items will still swap nets"
79 " even if you ignore the unselected pads." )
81 <<
_(
"Unselected pads:" ) <<
'\n';
83 for(
PAD*
pad : uniquePads )
86 details << wxS(
" • " ) << ( fp ? fp->
GetReference() :
_(
"<no reference designator>" ) ) << wxS(
":" )
87 <<
pad->GetNumber() <<
'\n';
91 wxRichMessageDialog dlg( aFrame, msg, aDialogTitle, wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING );
92 dlg.SetYesNoLabels(
_(
"Ignore Unselected Pads" ),
_(
"Swap All Connected Pads" ) );
93 dlg.SetExtendedMessage( details );
95 int ret = dlg.ShowModal();
97 if( ret == wxID_CANCEL )
100 aIncludeConnectedPads = ( ret == wxID_NO );
121 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
126 aCollector.
Remove( item );
139 commit = &localCommit;
141 std::vector<EDA_ITEM*> sorted =
selection.GetItemsSortedBySelectionOrder();
147 for(
size_t i = 0; i < sorted.size() - 1; i++ )
150 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
160 std::swap( aPos, bPos );
183 std::swap( aAngle, bAngle );
193 std::swap( aLayer, bLayer );
199 if( !localCommit.
Empty() )
200 localCommit.
Push(
_(
"Swap" ) );
223 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
224 std::vector<PAD*> pads;
225 const size_t padsCount = orderedPads.size();
228 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
231 std::vector<int> originalNets( padsCount );
232 std::unordered_set<PAD*> selectedPads;
234 for(
size_t i = 0; i < padsCount; ++i )
236 originalNets[i] = pads[i]->GetNetCode();
237 selectedPads.insert( pads[i] );
243 for(
size_t i = 1; i < padsCount; ++i )
245 if( originalNets[i] != originalNets[0] )
256 auto newNetForIndex =
259 return originalNets[( i + 1 ) % padsCount];
267 commit = &localCommit;
270 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
273 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
274 std::vector<PAD*> nonSelectedPadsToChange;
276 for(
size_t i = 0; i < padsCount; ++i )
279 int fromNet = originalNets[i];
280 int toNet = newNetForIndex( i );
297 if( ci->GetNetCode() != fromNet )
301 itemNewNets[ci] = toNet;
305 PAD* otherPad =
static_cast<PAD*
>( ci );
307 if( !selectedPads.count( otherPad ) )
308 nonSelectedPadsToChange.push_back( otherPad );
313 bool includeConnectedPads =
true;
320 for(
size_t i = 0; i < padsCount; ++i )
322 commit->
Modify( pads[i] );
323 pads[i]->SetNetCode( newNetForIndex( i ) );
327 for(
const auto& itemNewNet : itemNewNets )
330 int newNet = itemNewNet.second;
334 PAD* p =
static_cast<PAD*
>( item );
336 if( selectedPads.count( p ) )
339 if( !includeConnectedPads )
347 if( !localCommit.
Empty() )
348 localCommit.
Push(
_(
"Swap Pad Nets" ) );
369 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
394 else if( fp && targetFp != fp )
401 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
411 std::vector<bool> unitHit( units.size(),
false );
412 std::vector<int> unitOrder;
414 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
420 const wxString& padNum =
pad->GetNumber();
423 for(
size_t i = 0; i < units.size(); ++i )
425 for(
const auto& p : units[i].m_pins )
429 unitIdx =
static_cast<int>( i );
432 unitOrder.push_back( unitIdx );
445 std::vector<int> activeUnitIdx;
448 if( unitOrder.size() >= 2 )
450 activeUnitIdx = unitOrder;
451 sourceIdx = unitOrder.front();
454 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
456 sourceIdx = unitOrder.front();
457 wxString targetUnitByName = aEvent.
Parameter<wxString>();
461 for(
size_t i = 0; i < units.size(); ++i )
463 if(
static_cast<int>( i ) == sourceIdx )
466 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
467 targetIdx =
static_cast<int>( i );
476 activeUnitIdx.push_back( sourceIdx );
477 activeUnitIdx.push_back( targetIdx );
486 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
488 for(
int idx : activeUnitIdx )
490 if( units[idx].m_pins.size() != pinCount )
492 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
498 const size_t unitCount = activeUnitIdx.size();
499 std::vector<std::vector<PAD*>> unitPads( unitCount );
500 std::vector<std::vector<int>> unitNets( unitCount );
502 for(
size_t ui = 0; ui < unitCount; ++ui )
504 int uidx = activeUnitIdx[ui];
505 const auto& pins = units[uidx].m_pins;
507 for(
size_t pi = 0; pi < pinCount; ++pi )
513 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
517 unitPads[ui].push_back( p );
525 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
527 int refNet = unitNets[0][pi];
529 for(
size_t ui = 1; ui < unitCount; ++ui )
531 if( unitNets[ui][pi] != refNet )
541 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
550 commit = &localCommit;
552 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
555 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
556 std::vector<PAD*> nonSelectedPadsToChange;
559 std::unordered_set<PAD*> swapPads;
561 for(
const auto& v : unitPads )
562 swapPads.insert( v.begin(), v.end() );
565 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
581 if( ci->GetNetCode() != fromNet )
584 itemNewNets[ ci ] = toNet;
588 PAD* other =
static_cast<PAD*
>( ci );
590 if( !swapPads.count( other ) )
591 nonSelectedPadsToChange.push_back( other );
597 for(
size_t pi = 0; pi < pinCount; ++pi )
599 for(
size_t ui = 0; ui < unitCount; ++ui )
602 size_t toIdx = ( ui + 1 ) % unitCount;
604 PAD* padFrom = unitPads[fromIdx][pi];
605 int fromNet = unitNets[fromIdx][pi];
606 int toNet = unitNets[toIdx][pi];
608 scheduleForPad( padFrom, fromNet, toNet );
612 bool includeConnectedPads =
true;
620 for(
size_t pi = 0; pi < pinCount; ++pi )
623 for(
size_t ui = 0; ui < unitCount; ++ui )
625 size_t toIdx = ( ui + 1 ) % unitCount;
626 PAD*
pad = unitPads[ui][pi];
627 int newNet = unitNets[toIdx][pi];
630 pad->SetNetCode( newNet );
635 for(
const auto&
kv : itemNewNets )
638 int newNet =
kv.second;
642 PAD* p =
static_cast<PAD*
>( item );
644 if( swapPads.count( p ) )
647 if( !includeConnectedPads )
655 if( !localCommit.
Empty() )
656 localCommit.
Push(
_(
"Swap Gate Nets" ) );
682 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
687 aCollector.
Remove( item );
693 std::vector<FOOTPRINT*> footprintsToPack;
696 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
698 if( footprintsToPack.empty() )
701 BOX2I footprintsBbox;
707 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
713 commit.
Push(
_(
"Pack Footprints" ) );
750 localCommit.
Push(
_(
"Move" ) );
767 typedef std::numeric_limits<int> coord_limits;
769 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
770 static const double min = -max;
773 testBox.
Offset( aBBoxOffset );
782 testBox.
Offset( aMovement );
790 if( testBox.
GetTop() < min )
811 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
836 controls->ForceCursorPosition(
false );
838 auto displayConstraintsMessage =
846 msg =
_(
"Angle snap lines: 45°" );
850 msg =
_(
"Angle snap lines: 90°" );
861 auto updateStatusPopup =
862 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
864 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
865 "Press <esc> to cancel all; double-click to finish" );
877 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
885 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
887 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
890 std::vector<BOARD_ITEM*> sel_items;
891 std::vector<BOARD_ITEM*> orig_items;
900 orig_items.push_back( boardItem );
902 sel_items.push_back( boardItem );
910 sel_items.push_back(
pad );
920 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
921 pickedReferencePoint ) )
926 editFrame->
PopTool( pushedEvent );
930 if( moveIndividually )
937 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
940 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
941 statusPopup->Popup();
943 canvas()->SetStatusPopup( statusPopup->GetPanel() );
949 sel_items.push_back( orig_items[ itemIdx ] );
952 bool restore_state =
false;
953 VECTOR2I originalPos = originalCursorPos;
956 bool updateBBox =
true;
962 bool enableLocalRatsnest =
true;
965 bool eatFirstMouseUp =
true;
971 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
972 long lastArrowKeyAction = 0;
975 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
977 if( showCourtyardConflicts )
979 std::shared_ptr<DRC_ENGINE> drcEngine =
m_toolMgr->GetTool<
DRC_TOOL>()->GetDRCEngine();
981 drc_on_move->Init(
board );
984 auto configureAngleSnap =
987 std::vector<VECTOR2I> directions;
1003 grid.SetSnapLineDirections( directions );
1005 if( directions.empty() )
1007 grid.ClearSnapLine();
1011 grid.SetSnapLineOrigin( originalPos );
1015 configureAngleSnap( angleSnapMode );
1016 displayConstraintsMessage( angleSnapMode );
1032 eatFirstMouseUp =
false;
1045 bool redraw3D =
false;
1049 if(
controls->GetSettings().m_lastKeyboardCursorPositionValid )
1051 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1053 grid.SetSnap(
false );
1067 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1071 if( axisLock == AXIS_LOCK::HORIZONTAL )
1077 axisLock = AXIS_LOCK::NONE;
1083 axisLock = AXIS_LOCK::HORIZONTAL;
1088 if( axisLock == AXIS_LOCK::VERTICAL )
1094 axisLock = AXIS_LOCK::NONE;
1100 axisLock = AXIS_LOCK::VERTICAL;
1104 lastArrowKeyAction = action;
1111 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1114 if( axisLock == AXIS_LOCK::HORIZONTAL )
1116 else if( axisLock == AXIS_LOCK::VERTICAL )
1124 originalBBox =
BOX2I();
1143 bboxMovement += movement;
1151 item->Move( movement );
1169 if( redraw3D && allowRedraw3D )
1172 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1175 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1196 enableLocalRatsnest =
false;
1209 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1211 item->RunOnChildren(
1217 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1228 grid.SetAuxAxes(
false );
1243 boardItem->
Move( movement );
1255 if( showCourtyardConflicts )
1257 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1262 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1264 item->RunOnChildren(
1268 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1281 if( moveWithReference )
1283 selection.SetReferencePoint( pickedReferencePoint );
1288 controls->ForceCursorPosition(
true, pickedReferencePoint );
1303 selection.SetReferencePoint( snappedRef );
1308 grid.SetSnapLineOrigin( dragOrigin );
1310 grid.SetAuxAxes(
true, dragOrigin );
1319 originalPos =
selection.GetReferencePoint();
1335 if( enableLocalRatsnest )
1343 restore_state =
true;
1352 restore_state =
true;
1376 eatFirstMouseUp =
false;
1384 eatFirstMouseUp =
false;
1391 orig_items[itemIdx]->SetPosition( originalPos );
1393 view()->Update( orig_items[itemIdx] );
1396 if( ++itemIdx < orig_items.size() )
1404 selection.SetReferencePoint( originalPos );
1409 sel_items.push_back( nextItem );
1410 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1430 if( moveIndividually )
1431 orig_items[itemIdx]->SetPosition( originalPos );
1438 configureAngleSnap( angleSnapMode );
1439 displayConstraintsMessage( angleSnapMode );
1464 }
while( ( evt =
Wait() ) );
1467 if( showCourtyardConflicts )
1468 drc_on_move->ClearConflicts(
m_toolMgr->GetView() );
1470 controls->ForceCursorPosition(
false );
1485 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1493 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1499 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1506 editFrame->
PopTool( pushedEvent );
1509 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