59#include <wx/richmsgdlg.h>
60#include <wx/choicdlg.h>
61#include <unordered_set>
62#include <unordered_map>
66 const wxString& aDialogTitle,
bool& aIncludeConnectedPads )
70 aIncludeConnectedPads =
true;
74 std::unordered_set<PAD*> uniquePads( aPads.begin(), aPads.end() );
77 msg.Printf(
_(
"%zu unselected pad(s) are connected to these nets. How do you want to proceed?" ),
81 details <<
_(
"Connected tracks, vias, and other non-zone copper items will still swap nets"
82 " even if you ignore the unselected pads." )
84 <<
_(
"Unselected pads:" ) <<
'\n';
86 for(
PAD*
pad : uniquePads )
89 details << wxS(
" • " ) << ( fp ? fp->
GetReference() :
_(
"<no reference designator>" ) ) << wxS(
":" )
90 <<
pad->GetNumber() <<
'\n';
94 wxRichMessageDialog dlg( aFrame, msg, aDialogTitle, wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_WARNING );
95 dlg.SetYesNoLabels(
_(
"Ignore Unselected Pads" ),
_(
"Swap All Connected Pads" ) );
96 dlg.SetExtendedMessage( details );
98 int ret = dlg.ShowModal();
100 if( ret == wxID_CANCEL )
103 aIncludeConnectedPads = ( ret == wxID_NO );
124 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
129 aCollector.
Remove( item );
144 commit = &localCommit;
146 std::vector<EDA_ITEM*> sorted =
selection.GetItemsSortedBySelectionOrder();
152 for(
size_t i = 0; i < sorted.size() - 1; i++ )
155 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
170 std::swap( aLayer, bLayer );
179 std::swap( aPos, bPos );
202 std::swap( aAngle, bAngle );
212 std::swap( aLayer, bLayer );
218 if( !localCommit.
Empty() )
219 localCommit.
Push(
_(
"Swap" ) );
242 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
243 std::vector<PAD*> pads;
244 const size_t padsCount = orderedPads.size();
247 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
250 std::vector<int> originalNets( padsCount );
251 std::unordered_set<PAD*> selectedPads;
253 for(
size_t i = 0; i < padsCount; ++i )
255 originalNets[i] = pads[i]->GetNetCode();
256 selectedPads.insert( pads[i] );
262 for(
size_t i = 1; i < padsCount; ++i )
264 if( originalNets[i] != originalNets[0] )
275 auto newNetForIndex =
278 return originalNets[( i + 1 ) % padsCount];
286 commit = &localCommit;
289 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
292 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
293 std::vector<PAD*> nonSelectedPadsToChange;
295 for(
size_t i = 0; i < padsCount; ++i )
298 int fromNet = originalNets[i];
299 int toNet = newNetForIndex( i );
316 if( ci->GetNetCode() != fromNet )
320 itemNewNets[ci] = toNet;
324 PAD* otherPad =
static_cast<PAD*
>( ci );
326 if( !selectedPads.count( otherPad ) )
327 nonSelectedPadsToChange.push_back( otherPad );
332 bool includeConnectedPads =
true;
339 for(
size_t i = 0; i < padsCount; ++i )
341 commit->
Modify( pads[i] );
342 pads[i]->SetNetCode( newNetForIndex( i ) );
346 for(
const auto& itemNewNet : itemNewNets )
349 int newNet = itemNewNet.second;
353 PAD* p =
static_cast<PAD*
>( item );
355 if( selectedPads.count( p ) )
358 if( !includeConnectedPads )
366 if( !localCommit.
Empty() )
367 localCommit.
Push(
_(
"Swap Pad Nets" ) );
388 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
413 else if( fp && targetFp != fp )
420 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
430 std::vector<bool> unitHit( units.size(),
false );
431 std::vector<int> unitOrder;
433 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
439 const wxString& padNum =
pad->GetNumber();
442 for(
size_t i = 0; i < units.size(); ++i )
444 for(
const auto& p : units[i].m_pins )
448 unitIdx =
static_cast<int>( i );
451 unitOrder.push_back( unitIdx );
464 std::vector<int> activeUnitIdx;
467 if( unitOrder.size() >= 2 )
469 activeUnitIdx = unitOrder;
470 sourceIdx = unitOrder.front();
473 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
475 sourceIdx = unitOrder.front();
476 wxString targetUnitByName = aEvent.
Parameter<wxString>();
480 for(
size_t i = 0; i < units.size(); ++i )
482 if(
static_cast<int>( i ) == sourceIdx )
485 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
486 targetIdx =
static_cast<int>( i );
495 activeUnitIdx.push_back( sourceIdx );
496 activeUnitIdx.push_back( targetIdx );
505 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
507 for(
int idx : activeUnitIdx )
509 if( units[idx].m_pins.size() != pinCount )
511 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
517 const size_t unitCount = activeUnitIdx.size();
518 std::vector<std::vector<PAD*>> unitPads( unitCount );
519 std::vector<std::vector<int>> unitNets( unitCount );
521 for(
size_t ui = 0; ui < unitCount; ++ui )
523 int uidx = activeUnitIdx[ui];
524 const auto& pins = units[uidx].m_pins;
526 for(
size_t pi = 0; pi < pinCount; ++pi )
532 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
536 unitPads[ui].push_back( p );
544 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
546 int refNet = unitNets[0][pi];
548 for(
size_t ui = 1; ui < unitCount; ++ui )
550 if( unitNets[ui][pi] != refNet )
560 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
569 commit = &localCommit;
571 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
574 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
575 std::vector<PAD*> nonSelectedPadsToChange;
578 std::unordered_set<PAD*> swapPads;
580 for(
const auto& v : unitPads )
581 swapPads.insert( v.begin(), v.end() );
584 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
600 if( ci->GetNetCode() != fromNet )
603 itemNewNets[ ci ] = toNet;
607 PAD* other =
static_cast<PAD*
>( ci );
609 if( !swapPads.count( other ) )
610 nonSelectedPadsToChange.push_back( other );
616 for(
size_t pi = 0; pi < pinCount; ++pi )
618 for(
size_t ui = 0; ui < unitCount; ++ui )
621 size_t toIdx = ( ui + 1 ) % unitCount;
623 PAD* padFrom = unitPads[fromIdx][pi];
624 int fromNet = unitNets[fromIdx][pi];
625 int toNet = unitNets[toIdx][pi];
627 scheduleForPad( padFrom, fromNet, toNet );
631 bool includeConnectedPads =
true;
639 for(
size_t pi = 0; pi < pinCount; ++pi )
642 for(
size_t ui = 0; ui < unitCount; ++ui )
644 size_t toIdx = ( ui + 1 ) % unitCount;
645 PAD*
pad = unitPads[ui][pi];
646 int newNet = unitNets[toIdx][pi];
649 pad->SetNetCode( newNet );
654 for(
const auto&
kv : itemNewNets )
657 int newNet =
kv.second;
661 PAD* p =
static_cast<PAD*
>( item );
663 if( swapPads.count( p ) )
666 if( !includeConnectedPads )
674 if( !localCommit.
Empty() )
675 localCommit.
Push(
_(
"Swap Gate Nets" ) );
701 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
706 aCollector.
Remove( item );
714 std::vector<FOOTPRINT*> footprintsToPack;
717 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
719 if( footprintsToPack.empty() )
722 BOX2I footprintsBbox;
728 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
734 commit.
Push(
_(
"Pack Footprints" ) );
771 localCommit.
Push(
_(
"Move" ) );
788 typedef std::numeric_limits<int> coord_limits;
790 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
791 static const double min = -max;
794 testBox.
Offset( aBBoxOffset );
803 testBox.
Offset( aMovement );
811 if( testBox.
GetTop() < min )
832 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
862 controls->ForceCursorPosition(
false );
864 auto displayConstraintsMessage =
872 msg =
_(
"Angle snap lines: 45°" );
876 msg =
_(
"Angle snap lines: 90°" );
887 auto updateStatusPopup =
888 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
890 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
891 "Press <esc> to cancel all; double-click to finish" );
903 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
911 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
913 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
916 std::vector<BOARD_ITEM*> sel_items;
917 std::vector<BOARD_ITEM*> orig_items;
926 orig_items.push_back( boardItem );
928 sel_items.push_back( boardItem );
936 sel_items.push_back(
pad );
946 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
947 pickedReferencePoint ) )
952 editFrame->
PopTool( pushedEvent );
958 if( moveIndividually )
965 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
968 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
969 statusPopup->Popup();
971 canvas()->SetStatusPopup( statusPopup->GetPanel() );
977 sel_items.push_back( orig_items[ itemIdx ] );
980 bool restore_state =
false;
981 VECTOR2I originalPos = originalCursorPos;
984 bool updateBBox =
true;
990 bool enableLocalRatsnest =
true;
993 bool eatFirstMouseUp =
true;
999 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
1000 long lastArrowKeyAction = 0;
1003 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
1005 if( showCourtyardConflicts )
1007 std::shared_ptr<DRC_ENGINE> drcEngine =
m_toolMgr->GetTool<
DRC_TOOL>()->GetDRCEngine();
1009 drc_on_move->Init(
board );
1013 std::unique_ptr<CREEPAGE_OVERLAY> creepage_on_move = std::make_unique<CREEPAGE_OVERLAY>(
1016 auto configureAngleSnap =
1019 std::vector<VECTOR2I> directions;
1035 grid.SetSnapLineDirections( directions );
1037 if( directions.empty() )
1039 grid.ClearSnapLine();
1043 grid.SetSnapLineOrigin( originalPos );
1047 configureAngleSnap( angleSnapMode );
1048 displayConstraintsMessage( angleSnapMode );
1064 eatFirstMouseUp =
false;
1077 bool redraw3D =
false;
1081 if(
controls->GetSettings().m_lastKeyboardCursorPositionValid )
1083 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1085 grid.SetSnap(
false );
1099 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1103 if( axisLock == AXIS_LOCK::HORIZONTAL )
1109 axisLock = AXIS_LOCK::NONE;
1115 axisLock = AXIS_LOCK::HORIZONTAL;
1120 if( axisLock == AXIS_LOCK::VERTICAL )
1126 axisLock = AXIS_LOCK::NONE;
1132 axisLock = AXIS_LOCK::VERTICAL;
1136 lastArrowKeyAction = action;
1143 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1146 if( axisLock == AXIS_LOCK::HORIZONTAL )
1148 else if( axisLock == AXIS_LOCK::VERTICAL )
1156 originalBBox =
BOX2I();
1175 bboxMovement += movement;
1183 item->Move( movement );
1201 if( redraw3D && allowRedraw3D )
1204 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1207 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1210 creepage_on_move->Update();
1230 enableLocalRatsnest =
false;
1243 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1245 item->RunOnChildren(
1251 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1262 grid.SetAuxAxes(
false );
1277 boardItem->
Move( movement );
1289 if( showCourtyardConflicts )
1291 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1296 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1298 item->RunOnChildren(
1302 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1308 creepage_on_move->Start( sel_items );
1317 if( moveWithReference )
1319 selection.SetReferencePoint( pickedReferencePoint );
1324 controls->ForceCursorPosition(
true, pickedReferencePoint );
1331 selection.SetReferencePoint( dragOrigin );
1334 grid.SetSnapLineOrigin( dragOrigin );
1336 grid.SetAuxAxes(
true, dragOrigin );
1344 originalPos =
selection.GetReferencePoint();
1360 if( enableLocalRatsnest )
1368 restore_state =
true;
1377 restore_state =
true;
1401 eatFirstMouseUp =
false;
1409 eatFirstMouseUp =
false;
1416 orig_items[itemIdx]->SetPosition( originalPos );
1418 view()->Update( orig_items[itemIdx] );
1421 if( ++itemIdx < orig_items.size() )
1429 selection.SetReferencePoint( originalPos );
1434 sel_items.push_back( nextItem );
1435 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1455 if( moveIndividually )
1456 orig_items[itemIdx]->SetPosition( originalPos );
1463 configureAngleSnap( angleSnapMode );
1464 displayConstraintsMessage( angleSnapMode );
1489 }
while( ( evt =
Wait() ) );
1492 if( showCourtyardConflicts )
1493 drc_on_move->ClearConflicts(
m_toolMgr->GetView() );
1495 creepage_on_move->Stop();
1497 controls->ForceCursorPosition(
false );
1512 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1520 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1526 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1533 editFrame->
PopTool( pushedEvent );
1537 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