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 );
141 commit = &localCommit;
143 std::vector<EDA_ITEM*> sorted =
selection.GetItemsSortedBySelectionOrder();
149 for(
size_t i = 0; i < sorted.size() - 1; i++ )
152 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
167 std::swap( aLayer, bLayer );
176 std::swap( aPos, bPos );
199 std::swap( aAngle, bAngle );
209 std::swap( aLayer, bLayer );
215 if( !localCommit.
Empty() )
216 localCommit.
Push(
_(
"Swap" ) );
239 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
240 std::vector<PAD*> pads;
241 const size_t padsCount = orderedPads.size();
244 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
247 std::vector<int> originalNets( padsCount );
248 std::unordered_set<PAD*> selectedPads;
250 for(
size_t i = 0; i < padsCount; ++i )
252 originalNets[i] = pads[i]->GetNetCode();
253 selectedPads.insert( pads[i] );
259 for(
size_t i = 1; i < padsCount; ++i )
261 if( originalNets[i] != originalNets[0] )
272 auto newNetForIndex =
275 return originalNets[( i + 1 ) % padsCount];
283 commit = &localCommit;
286 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
289 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
290 std::vector<PAD*> nonSelectedPadsToChange;
292 for(
size_t i = 0; i < padsCount; ++i )
295 int fromNet = originalNets[i];
296 int toNet = newNetForIndex( i );
313 if( ci->GetNetCode() != fromNet )
317 itemNewNets[ci] = toNet;
321 PAD* otherPad =
static_cast<PAD*
>( ci );
323 if( !selectedPads.count( otherPad ) )
324 nonSelectedPadsToChange.push_back( otherPad );
329 bool includeConnectedPads =
true;
336 for(
size_t i = 0; i < padsCount; ++i )
338 commit->
Modify( pads[i] );
339 pads[i]->SetNetCode( newNetForIndex( i ) );
343 for(
const auto& itemNewNet : itemNewNets )
346 int newNet = itemNewNet.second;
350 PAD* p =
static_cast<PAD*
>( item );
352 if( selectedPads.count( p ) )
355 if( !includeConnectedPads )
363 if( !localCommit.
Empty() )
364 localCommit.
Push(
_(
"Swap Pad Nets" ) );
385 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
410 else if( fp && targetFp != fp )
417 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
427 std::vector<bool> unitHit( units.size(),
false );
428 std::vector<int> unitOrder;
430 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
436 const wxString& padNum =
pad->GetNumber();
439 for(
size_t i = 0; i < units.size(); ++i )
441 for(
const auto& p : units[i].m_pins )
445 unitIdx =
static_cast<int>( i );
448 unitOrder.push_back( unitIdx );
461 std::vector<int> activeUnitIdx;
464 if( unitOrder.size() >= 2 )
466 activeUnitIdx = unitOrder;
467 sourceIdx = unitOrder.front();
470 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
472 sourceIdx = unitOrder.front();
473 wxString targetUnitByName = aEvent.
Parameter<wxString>();
477 for(
size_t i = 0; i < units.size(); ++i )
479 if(
static_cast<int>( i ) == sourceIdx )
482 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
483 targetIdx =
static_cast<int>( i );
492 activeUnitIdx.push_back( sourceIdx );
493 activeUnitIdx.push_back( targetIdx );
502 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
504 for(
int idx : activeUnitIdx )
506 if( units[idx].m_pins.size() != pinCount )
508 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
514 const size_t unitCount = activeUnitIdx.size();
515 std::vector<std::vector<PAD*>> unitPads( unitCount );
516 std::vector<std::vector<int>> unitNets( unitCount );
518 for(
size_t ui = 0; ui < unitCount; ++ui )
520 int uidx = activeUnitIdx[ui];
521 const auto& pins = units[uidx].m_pins;
523 for(
size_t pi = 0; pi < pinCount; ++pi )
529 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
533 unitPads[ui].push_back( p );
541 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
543 int refNet = unitNets[0][pi];
545 for(
size_t ui = 1; ui < unitCount; ++ui )
547 if( unitNets[ui][pi] != refNet )
557 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
566 commit = &localCommit;
568 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
571 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
572 std::vector<PAD*> nonSelectedPadsToChange;
575 std::unordered_set<PAD*> swapPads;
577 for(
const auto& v : unitPads )
578 swapPads.insert( v.begin(), v.end() );
581 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
597 if( ci->GetNetCode() != fromNet )
600 itemNewNets[ ci ] = toNet;
604 PAD* other =
static_cast<PAD*
>( ci );
606 if( !swapPads.count( other ) )
607 nonSelectedPadsToChange.push_back( other );
613 for(
size_t pi = 0; pi < pinCount; ++pi )
615 for(
size_t ui = 0; ui < unitCount; ++ui )
618 size_t toIdx = ( ui + 1 ) % unitCount;
620 PAD* padFrom = unitPads[fromIdx][pi];
621 int fromNet = unitNets[fromIdx][pi];
622 int toNet = unitNets[toIdx][pi];
624 scheduleForPad( padFrom, fromNet, toNet );
628 bool includeConnectedPads =
true;
636 for(
size_t pi = 0; pi < pinCount; ++pi )
639 for(
size_t ui = 0; ui < unitCount; ++ui )
641 size_t toIdx = ( ui + 1 ) % unitCount;
642 PAD*
pad = unitPads[ui][pi];
643 int newNet = unitNets[toIdx][pi];
646 pad->SetNetCode( newNet );
651 for(
const auto&
kv : itemNewNets )
654 int newNet =
kv.second;
658 PAD* p =
static_cast<PAD*
>( item );
660 if( swapPads.count( p ) )
663 if( !includeConnectedPads )
671 if( !localCommit.
Empty() )
672 localCommit.
Push(
_(
"Swap Gate Nets" ) );
698 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
703 aCollector.
Remove( item );
709 std::vector<FOOTPRINT*> footprintsToPack;
712 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
714 if( footprintsToPack.empty() )
717 BOX2I footprintsBbox;
723 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
729 commit.
Push(
_(
"Pack Footprints" ) );
766 localCommit.
Push(
_(
"Move" ) );
783 typedef std::numeric_limits<int> coord_limits;
785 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
786 static const double min = -max;
789 testBox.
Offset( aBBoxOffset );
798 testBox.
Offset( aMovement );
806 if( testBox.
GetTop() < min )
827 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
852 controls->ForceCursorPosition(
false );
854 auto displayConstraintsMessage =
862 msg =
_(
"Angle snap lines: 45°" );
866 msg =
_(
"Angle snap lines: 90°" );
877 auto updateStatusPopup =
878 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
880 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
881 "Press <esc> to cancel all; double-click to finish" );
893 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
901 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
903 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
906 std::vector<BOARD_ITEM*> sel_items;
907 std::vector<BOARD_ITEM*> orig_items;
916 orig_items.push_back( boardItem );
918 sel_items.push_back( boardItem );
926 sel_items.push_back(
pad );
936 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
937 pickedReferencePoint ) )
942 editFrame->
PopTool( pushedEvent );
946 if( moveIndividually )
953 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
956 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
957 statusPopup->Popup();
959 canvas()->SetStatusPopup( statusPopup->GetPanel() );
965 sel_items.push_back( orig_items[ itemIdx ] );
968 bool restore_state =
false;
969 VECTOR2I originalPos = originalCursorPos;
972 bool updateBBox =
true;
978 bool enableLocalRatsnest =
true;
981 bool eatFirstMouseUp =
true;
987 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
988 long lastArrowKeyAction = 0;
991 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
993 if( showCourtyardConflicts )
995 std::shared_ptr<DRC_ENGINE> drcEngine =
m_toolMgr->GetTool<
DRC_TOOL>()->GetDRCEngine();
997 drc_on_move->Init(
board );
1000 auto configureAngleSnap =
1003 std::vector<VECTOR2I> directions;
1019 grid.SetSnapLineDirections( directions );
1021 if( directions.empty() )
1023 grid.ClearSnapLine();
1027 grid.SetSnapLineOrigin( originalPos );
1031 configureAngleSnap( angleSnapMode );
1032 displayConstraintsMessage( angleSnapMode );
1048 eatFirstMouseUp =
false;
1061 bool redraw3D =
false;
1065 if(
controls->GetSettings().m_lastKeyboardCursorPositionValid )
1067 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1069 grid.SetSnap(
false );
1083 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1087 if( axisLock == AXIS_LOCK::HORIZONTAL )
1093 axisLock = AXIS_LOCK::NONE;
1099 axisLock = AXIS_LOCK::HORIZONTAL;
1104 if( axisLock == AXIS_LOCK::VERTICAL )
1110 axisLock = AXIS_LOCK::NONE;
1116 axisLock = AXIS_LOCK::VERTICAL;
1120 lastArrowKeyAction = action;
1127 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1130 if( axisLock == AXIS_LOCK::HORIZONTAL )
1132 else if( axisLock == AXIS_LOCK::VERTICAL )
1140 originalBBox =
BOX2I();
1159 bboxMovement += movement;
1167 item->Move( movement );
1185 if( redraw3D && allowRedraw3D )
1188 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1191 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1212 enableLocalRatsnest =
false;
1225 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1227 item->RunOnChildren(
1233 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1244 grid.SetAuxAxes(
false );
1259 boardItem->
Move( movement );
1271 if( showCourtyardConflicts )
1273 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1278 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1280 item->RunOnChildren(
1284 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1297 if( moveWithReference )
1299 selection.SetReferencePoint( pickedReferencePoint );
1304 controls->ForceCursorPosition(
true, pickedReferencePoint );
1311 selection.SetReferencePoint( dragOrigin );
1314 grid.SetSnapLineOrigin( dragOrigin );
1316 grid.SetAuxAxes(
true, dragOrigin );
1324 originalPos =
selection.GetReferencePoint();
1340 if( enableLocalRatsnest )
1348 restore_state =
true;
1357 restore_state =
true;
1381 eatFirstMouseUp =
false;
1389 eatFirstMouseUp =
false;
1396 orig_items[itemIdx]->SetPosition( originalPos );
1398 view()->Update( orig_items[itemIdx] );
1401 if( ++itemIdx < orig_items.size() )
1409 selection.SetReferencePoint( originalPos );
1414 sel_items.push_back( nextItem );
1415 updateStatusPopup( nextItem, itemIdx + 1, orig_items.size() );
1435 if( moveIndividually )
1436 orig_items[itemIdx]->SetPosition( originalPos );
1443 configureAngleSnap( angleSnapMode );
1444 displayConstraintsMessage( angleSnapMode );
1469 }
while( ( evt =
Wait() ) );
1472 if( showCourtyardConflicts )
1473 drc_on_move->ClearConflicts(
m_toolMgr->GetView() );
1475 controls->ForceCursorPosition(
false );
1490 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1498 if( sel_items.size() == 1 && sel_items.back()->Type() ==
PCB_GENERATOR_T )
1504 EDA_ITEMS oItems( orig_items.begin(), orig_items.end() );
1511 editFrame->
PopTool( pushedEvent );
1514 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.
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