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 );
138 commit = &localCommit;
140 std::vector<EDA_ITEM*> sorted =
selection.GetItemsSortedBySelectionOrder();
146 for(
size_t i = 0; i < sorted.size() - 1; i++ )
149 EDA_ITEM* edaItemB = sorted[( i + 1 ) % sorted.size()];
159 std::swap( aPos, bPos );
182 std::swap( aAngle, bAngle );
192 std::swap( aLayer, bLayer );
198 if( !localCommit.
Empty() )
199 localCommit.
Push(
_(
"Swap" ) );
222 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
223 std::vector<PAD*> pads;
224 const size_t padsCount = orderedPads.size();
227 pads.push_back(
static_cast<PAD*
>(
static_cast<BOARD_ITEM*
>( it ) ) );
230 std::vector<int> originalNets( padsCount );
231 std::unordered_set<PAD*> selectedPads;
233 for(
size_t i = 0; i < padsCount; ++i )
235 originalNets[i] = pads[i]->GetNetCode();
236 selectedPads.insert( pads[i] );
242 for(
size_t i = 1; i < padsCount; ++i )
244 if( originalNets[i] != originalNets[0] )
255 auto newNetForIndex =
258 return originalNets[( i + 1 ) % padsCount];
266 commit = &localCommit;
269 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
272 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
273 std::vector<PAD*> nonSelectedPadsToChange;
275 for(
size_t i = 0; i < padsCount; ++i )
278 int fromNet = originalNets[i];
279 int toNet = newNetForIndex( i );
296 if( ci->GetNetCode() != fromNet )
300 itemNewNets[ci] = toNet;
304 PAD* otherPad =
static_cast<PAD*
>( ci );
306 if( !selectedPads.count( otherPad ) )
307 nonSelectedPadsToChange.push_back( otherPad );
312 bool includeConnectedPads =
true;
319 for(
size_t i = 0; i < padsCount; ++i )
321 commit->
Modify( pads[i] );
322 pads[i]->SetNetCode( newNetForIndex( i ) );
326 for(
const auto& itemNewNet : itemNewNets )
329 int newNet = itemNewNet.second;
333 PAD* p =
static_cast<PAD*
>( item );
335 if( selectedPads.count( p ) )
338 if( !includeConnectedPads )
346 if( !localCommit.
Empty() )
347 localCommit.
Push(
_(
"Swap Pad Nets" ) );
368 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on pads within one multi-gate "
393 else if( fp && targetFp != fp )
400 if( fail || !targetFp || targetFp->
GetUnitInfo().size() < 2 )
410 std::vector<bool> unitHit( units.size(),
false );
411 std::vector<int> unitOrder;
413 std::vector<EDA_ITEM*> orderedPads =
selection.GetItemsSortedBySelectionOrder();
419 const wxString& padNum =
pad->GetNumber();
422 for(
size_t i = 0; i < units.size(); ++i )
424 for(
const auto& p : units[i].m_pins )
428 unitIdx =
static_cast<int>( i );
431 unitOrder.push_back( unitIdx );
444 std::vector<int> activeUnitIdx;
447 if( unitOrder.size() >= 2 )
449 activeUnitIdx = unitOrder;
450 sourceIdx = unitOrder.front();
453 else if( unitOrder.size() == 1 && aEvent.
HasParameter() )
455 sourceIdx = unitOrder.front();
456 wxString targetUnitByName = aEvent.
Parameter<wxString>();
460 for(
size_t i = 0; i < units.size(); ++i )
462 if(
static_cast<int>( i ) == sourceIdx )
465 if( units[i].m_pins.size() == units[sourceIdx].m_pins.size() && units[i].m_unitName == targetUnitByName )
466 targetIdx =
static_cast<int>( i );
475 activeUnitIdx.push_back( sourceIdx );
476 activeUnitIdx.push_back( targetIdx );
485 const size_t pinCount = units[activeUnitIdx.front()].m_pins.size();
487 for(
int idx : activeUnitIdx )
489 if( units[idx].m_pins.size() != pinCount )
491 frame()->ShowInfoBarError(
_(
"Gate swapping must be performed on gates with equal pin counts." ) );
497 const size_t unitCount = activeUnitIdx.size();
498 std::vector<std::vector<PAD*>> unitPads( unitCount );
499 std::vector<std::vector<int>> unitNets( unitCount );
501 for(
size_t ui = 0; ui < unitCount; ++ui )
503 int uidx = activeUnitIdx[ui];
504 const auto& pins = units[uidx].m_pins;
506 for(
size_t pi = 0; pi < pinCount; ++pi )
512 frame()->ShowInfoBarError(
_(
"Gate swapping failed: pad in unit missing from footprint." ) );
516 unitPads[ui].push_back( p );
524 for(
size_t pi = 0; pi < pinCount && allSame; ++pi )
526 int refNet = unitNets[0][pi];
528 for(
size_t ui = 1; ui < unitCount; ++ui )
530 if( unitNets[ui][pi] != refNet )
540 frame()->ShowInfoBarError(
_(
"Gate swapping has no effect: all selected gates have identical nets." ) );
549 commit = &localCommit;
551 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
board()->GetConnectivity();
554 std::unordered_map<BOARD_CONNECTED_ITEM*, int> itemNewNets;
555 std::vector<PAD*> nonSelectedPadsToChange;
558 std::unordered_set<PAD*> swapPads;
560 for(
const auto& v : unitPads )
561 swapPads.insert( v.begin(), v.end() );
564 auto scheduleForPad = [&](
PAD*
pad,
int fromNet,
int toNet )
580 if( ci->GetNetCode() != fromNet )
583 itemNewNets[ ci ] = toNet;
587 PAD* other =
static_cast<PAD*
>( ci );
589 if( !swapPads.count( other ) )
590 nonSelectedPadsToChange.push_back( other );
596 for(
size_t pi = 0; pi < pinCount; ++pi )
598 for(
size_t ui = 0; ui < unitCount; ++ui )
601 size_t toIdx = ( ui + 1 ) % unitCount;
603 PAD* padFrom = unitPads[fromIdx][pi];
604 int fromNet = unitNets[fromIdx][pi];
605 int toNet = unitNets[toIdx][pi];
607 scheduleForPad( padFrom, fromNet, toNet );
611 bool includeConnectedPads =
true;
619 for(
size_t pi = 0; pi < pinCount; ++pi )
622 for(
size_t ui = 0; ui < unitCount; ++ui )
624 size_t toIdx = ( ui + 1 ) % unitCount;
625 PAD*
pad = unitPads[ui][pi];
626 int newNet = unitNets[toIdx][pi];
629 pad->SetNetCode( newNet );
634 for(
const auto&
kv : itemNewNets )
637 int newNet =
kv.second;
641 PAD* p =
static_cast<PAD*
>( item );
643 if( swapPads.count( p ) )
646 if( !includeConnectedPads )
654 if( !localCommit.
Empty() )
655 localCommit.
Push(
_(
"Swap Gate Nets" ) );
681 for(
int i = aCollector.
GetCount() - 1; i >= 0; --i )
686 aCollector.
Remove( item );
692 std::vector<FOOTPRINT*> footprintsToPack;
695 footprintsToPack.push_back(
static_cast<FOOTPRINT*
>( item ) );
697 if( footprintsToPack.empty() )
700 BOX2I footprintsBbox;
706 footprintsBbox.
Merge( fp->GetBoundingBox(
false ) );
712 commit.
Push(
_(
"Pack Footprints" ) );
749 localCommit.
Push(
_(
"Move" ) );
766 typedef std::numeric_limits<int> coord_limits;
768 static const double max = coord_limits::max() - (int)
COORDS_PADDING;
769 static const double min = -max;
772 testBox.
Offset( aBBoxOffset );
781 testBox.
Offset( aMovement );
789 if( testBox.
GetTop() < min )
810 std::unique_ptr<STATUS_TEXT_POPUP> statusPopup;
835 controls->ForceCursorPosition(
false );
837 auto displayConstraintsMessage =
845 msg =
_(
"Angle snap lines: 45°" );
849 msg =
_(
"Angle snap lines: 90°" );
860 auto updateStatusPopup =
861 [&](
EDA_ITEM* item,
size_t ii,
size_t count )
863 wxString popuptext =
_(
"Click to place %s (item %zu of %zu)\n"
864 "Press <esc> to cancel all; double-click to finish" );
876 msg = wxString::Format(
_(
"%s pad %s" ), fp->
GetReference(),
pad->GetNumber() );
884 statusPopup = std::make_unique<STATUS_TEXT_POPUP>(
frame() );
886 statusPopup->SetText( wxString::Format( popuptext, msg, ii, count ) );
889 std::vector<BOARD_ITEM*> sel_items;
890 std::vector<BOARD_ITEM*> orig_items;
899 orig_items.push_back( boardItem );
901 sel_items.push_back( boardItem );
909 sel_items.push_back(
pad );
919 if( moveWithReference && !
pickReferencePoint(
_(
"Select reference point for move..." ),
"",
"",
920 pickedReferencePoint ) )
925 editFrame->
PopTool( pushedEvent );
929 if( moveIndividually )
936 orig_items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
939 updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
940 statusPopup->Popup();
942 canvas()->SetStatusPopup( statusPopup->GetPanel() );
948 sel_items.push_back( orig_items[ itemIdx ] );
951 bool restore_state =
false;
952 VECTOR2I originalPos = originalCursorPos;
955 bool updateBBox =
true;
961 bool enableLocalRatsnest =
true;
964 bool eatFirstMouseUp =
true;
970 AXIS_LOCK axisLock = AXIS_LOCK::NONE;
971 long lastArrowKeyAction = 0;
974 std::unique_ptr<DRC_INTERACTIVE_COURTYARD_CLEARANCE> drc_on_move =
nullptr;
976 if( showCourtyardConflicts )
978 std::shared_ptr<DRC_ENGINE> drcEngine =
m_toolMgr->GetTool<
DRC_TOOL>()->GetDRCEngine();
980 drc_on_move->Init(
board );
983 auto configureAngleSnap =
986 std::vector<VECTOR2I> directions;
1002 grid.SetSnapLineDirections( directions );
1004 if( directions.empty() )
1006 grid.ClearSnapLine();
1010 grid.SetSnapLineOrigin( originalPos );
1014 configureAngleSnap( angleSnapMode );
1015 displayConstraintsMessage( angleSnapMode );
1031 eatFirstMouseUp =
false;
1044 bool redraw3D =
false;
1048 if(
controls->GetSettings().m_lastKeyboardCursorPositionValid )
1050 VECTOR2I keyboardPos(
controls->GetSettings().m_lastKeyboardCursorPosition );
1052 grid.SetSnap(
false );
1066 long action =
controls->GetSettings().m_lastKeyboardCursorCommand;
1070 if( axisLock == AXIS_LOCK::HORIZONTAL )
1076 axisLock = AXIS_LOCK::NONE;
1082 axisLock = AXIS_LOCK::HORIZONTAL;
1087 if( axisLock == AXIS_LOCK::VERTICAL )
1093 axisLock = AXIS_LOCK::NONE;
1099 axisLock = AXIS_LOCK::VERTICAL;
1103 lastArrowKeyAction = action;
1110 m_cursor =
grid.BestSnapAnchor( mousePos, layers, selectionGrid, sel_items );
1113 if( axisLock == AXIS_LOCK::HORIZONTAL )
1115 else if( axisLock == AXIS_LOCK::VERTICAL )
1123 originalBBox =
BOX2I();
1142 bboxMovement += movement;
1150 item->Move( movement );
1168 if( redraw3D && allowRedraw3D )
1171 if( showCourtyardConflicts && drc_on_move->m_FpInMove.size() )
1174 drc_on_move->UpdateConflicts(
m_toolMgr->GetView(),
true );
1195 enableLocalRatsnest =
false;
1208 static_cast<PCB_SHAPE*
>( item )->UpdateHatching();
1210 item->RunOnChildren(
1216 static_cast<PCB_SHAPE*
>( child )->UpdateHatching();
1227 grid.SetAuxAxes(
false );
1242 boardItem->
Move( movement );
1254 if( showCourtyardConflicts )
1256 std::vector<FOOTPRINT*>& FPs = drc_on_move->m_FpInMove;
1261 FPs.push_back(
static_cast<FOOTPRINT*
>( item ) );
1263 item->RunOnChildren(
1267 FPs.push_back(
static_cast<FOOTPRINT*
>( child ) );
1280 if( moveWithReference )
1282 selection.SetReferencePoint( pickedReferencePoint );
1287 controls->ForceCursorPosition(
true, pickedReferencePoint );
1300 selection.SetReferencePoint( dragOrigin );
1304 grid.SetSnapLineOrigin( dragOrigin );
1306 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 PCB_LAYER_ID GetActiveLayer() 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 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