Move wire/junction actions out of SCHEMATIC

They are tool actions and require that the tool holder is valid, so they
really need to be where this is only ever true

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21510
This commit is contained in:
Seth Hillbrand 2025-08-19 15:46:40 -07:00
parent cea77b7e9b
commit 0586f02f00
7 changed files with 121 additions and 117 deletions

View File

@ -31,6 +31,7 @@
#include <sch_no_connect.h>
#include <sch_commit.h>
#include <tool/tool_manager.h>
#include <tools/sch_line_wire_bus_tool.h>
#include <tools/sch_selection_tool.h>
#include <trigo.h>
@ -56,6 +57,7 @@ bool SCH_EDIT_FRAME::TrimWire( SCH_COMMIT* aCommit, const VECTOR2I& aStart, cons
std::vector<SCH_LINE*> wires;
BOX2I bb( aStart );
SCH_LINE_WIRE_BUS_TOOL* lwbTool = m_toolManager->GetTool<SCH_LINE_WIRE_BUS_TOOL>();
bb.Merge( aEnd );
// We cannot modify the RTree while iterating, so push the possible
@ -90,14 +92,14 @@ bool SCH_EDIT_FRAME::TrimWire( SCH_COMMIT* aCommit, const VECTOR2I& aStart, cons
// Step 1: break the segment on one end.
// Ensure that *line points to the segment containing aEnd
SCH_LINE* new_line;
Schematic().BreakSegment( aCommit, line, aStart, &new_line, screen );
lwbTool->BreakSegment( aCommit, line, aStart, &new_line, screen );
if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aEnd ) )
line = new_line;
// Step 2: break the remaining segment.
// Ensure that *line _also_ contains aStart. This is our overlapping segment
Schematic().BreakSegment( aCommit, line, aEnd, &new_line, screen );
lwbTool->BreakSegment( aCommit, line, aEnd, &new_line, screen );
if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aStart ) )
line = new_line;
@ -184,20 +186,6 @@ void SCH_EDIT_FRAME::DeleteJunction( SCH_COMMIT* aCommit, SCH_ITEM* aJunction )
}
}
SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen,
const VECTOR2I& aPos )
{
SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
AddToScreen( junction, aScreen );
aCommit->Added( junction, aScreen );
Schematic().BreakSegments( aCommit, aPos, aScreen );
return junction;
}
void SCH_EDIT_FRAME::UpdateHopOveredWires( SCH_ITEM* aItem )
{
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;

View File

@ -1038,67 +1038,6 @@ bool SCHEMATIC::IsComplexHierarchy() const
}
void SCHEMATIC::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint,
SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
{
// Save the copy of aSegment before breaking it
aCommit->Modify( aSegment, aScreen );
SCH_LINE* newSegment = aSegment->BreakAt( aCommit, aPoint );
aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
newSegment->SetFlags( IS_NEW | IS_BROKEN );
if( m_schematicHolder )
m_schematicHolder->AddToScreen( newSegment, aScreen );
aCommit->Added( newSegment, aScreen );
*aNewSegment = newSegment;
}
bool SCHEMATIC::BreakSegments( SCH_COMMIT* aCommit, const VECTOR2I& aPos, SCH_SCREEN* aScreen )
{
bool brokenSegments = false;
SCH_LINE* new_line;
for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPos, true ) )
{
BreakSegment( aCommit, wire, aPos, &new_line, aScreen );
brokenSegments = true;
}
return brokenSegments;
}
bool SCHEMATIC::BreakSegmentsOnJunctions( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
{
bool brokenSegments = false;
std::set<VECTOR2I> point_set;
for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
point_set.insert( item->GetPosition() );
for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
{
SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
point_set.insert( entry->GetPosition() );
point_set.insert( entry->GetEnd() );
}
for( const VECTOR2I& pt : point_set )
{
BreakSegments( aCommit, pt, aScreen );
brokenSegments = true;
}
return brokenSegments;
}
void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
{
SCH_SELECTION_TOOL* selectionTool = m_schematicHolder ? m_schematicHolder->GetSelectionTool() : nullptr;
@ -1130,7 +1069,6 @@ void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
}
};
BreakSegmentsOnJunctions( aCommit, aScreen );
for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
{

View File

@ -287,39 +287,6 @@ public:
*/
void FixupJunctionsAfterImport();
/**
* Break a single segment into two at the specified point.
*
* @param aCommit Transaction container used to record changes for undo/redo
* @param aSegment Line segment to break
* @param aPoint Point at which to break the segment
* @param aNewSegment Pointer to the newly created segment (if created)
* @param aScreen is the screen to examine
*/
void BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint, SCH_LINE** aNewSegment,
SCH_SCREEN* aScreen );
/**
* Check every wire and bus for a intersection at \a aPoint and break into two segments
* at \a aPoint if an intersection is found.
*
* @param aCommit Transaction container used to record changes for undo/redo
* @param aPoint Test this point for an intersection.
* @param aScreen is the screen to examine.
* @return True if any wires or buses were broken.
*/
bool BreakSegments( SCH_COMMIT* aCommit, const VECTOR2I& aPoint, SCH_SCREEN* aScreen );
/**
* Test all junctions and bus entries in the schematic for intersections with wires and
* buses and breaks any intersections into multiple segments.
*
* @param aCommit Transaction container used to record changes for undo/redo
* @param aScreen is the screen to examine.
* @return True if any wires or buses were broken.
*/
bool BreakSegmentsOnJunctions( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen );
/**
* Scan existing markers and record data from any that are Excluded.
*/

View File

@ -2874,6 +2874,8 @@ int SCH_EDIT_TOOL::BreakWire( const TOOL_EVENT& aEvent )
SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_LINE_T } );
SCH_SCREEN* screen = m_frame->GetScreen();
SCH_COMMIT commit( m_toolMgr );
SCH_LINE_WIRE_BUS_TOOL* lwbTool = m_toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>();
std::vector<SCH_LINE*> lines;
// Save the current orthogonal mode so we can restore it later
@ -2908,9 +2910,9 @@ int SCH_EDIT_TOOL::BreakWire( const TOOL_EVENT& aEvent )
// We let the user select the break point if they're on a single line
if( lines.size() == 1 && line->HitTest( cursorPos ) && !line->IsEndPoint( cursorPos ) )
m_frame->Schematic().BreakSegment( &commit, line, cursorPos, &newLine, screen );
lwbTool->BreakSegment( &commit, line, cursorPos, &newLine, screen );
else
m_frame->Schematic().BreakSegment( &commit, line, line->GetMidPoint(), &newLine, screen );
lwbTool->BreakSegment( &commit, line, line->GetMidPoint(), &newLine, screen );
// Make sure both endpoints are deselected
newLine->ClearFlags( ENDPOINT | STARTPOINT );

View File

@ -1288,7 +1288,7 @@ void SCH_LINE_WIRE_BUS_TOOL::finishSegments( SCH_COMMIT& aCommit )
for( const VECTOR2I& pt : new_ends )
{
if( m_frame->GetScreen()->IsExplicitJunctionNeeded( pt ) )
m_frame->AddJunction( &aCommit, m_frame->GetScreen(), pt );
AddJunction( &aCommit, m_frame->GetScreen(), pt );
}
if( m_busUnfold.in_progress )
@ -1363,12 +1363,85 @@ int SCH_LINE_WIRE_BUS_TOOL::AddJunctionsIfNeeded( SCH_COMMIT* aCommit, SCH_SELEC
}
for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
m_frame->AddJunction( aCommit, m_frame->GetScreen(), point );
AddJunction( aCommit, m_frame->GetScreen(), point );
return 0;
}
void SCH_LINE_WIRE_BUS_TOOL::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint,
SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
{
// Save the copy of aSegment before breaking it
aCommit->Modify( aSegment, aScreen );
SCH_LINE* newSegment = aSegment->BreakAt( aCommit, aPoint );
aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
newSegment->SetFlags( IS_NEW | IS_BROKEN );
m_frame->AddToScreen( newSegment, aScreen );
aCommit->Added( newSegment, aScreen );
*aNewSegment = newSegment;
}
bool SCH_LINE_WIRE_BUS_TOOL::BreakSegments( SCH_COMMIT* aCommit, const VECTOR2I& aPos, SCH_SCREEN* aScreen )
{
bool brokenSegments = false;
SCH_LINE* new_line;
for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPos, true ) )
{
BreakSegment( aCommit, wire, aPos, &new_line, aScreen );
brokenSegments = true;
}
return brokenSegments;
}
bool SCH_LINE_WIRE_BUS_TOOL::BreakSegmentsOnJunctions( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
{
bool brokenSegments = false;
std::set<VECTOR2I> point_set;
for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
point_set.insert( item->GetPosition() );
for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
{
SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
point_set.insert( entry->GetPosition() );
point_set.insert( entry->GetEnd() );
}
for( const VECTOR2I& pt : point_set )
{
BreakSegments( aCommit, pt, aScreen );
brokenSegments = true;
}
return brokenSegments;
}
SCH_JUNCTION* SCH_LINE_WIRE_BUS_TOOL::AddJunction( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen,
const VECTOR2I& aPos )
{
SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
m_frame->AddToScreen( junction, aScreen );
aCommit->Added( junction, aScreen );
BreakSegments( aCommit, aPos, aScreen );
return junction;
}
void SCH_LINE_WIRE_BUS_TOOL::setTransitions()
{
Go( &SCH_LINE_WIRE_BUS_TOOL::DrawSegments, SCH_ACTIONS::drawWire.MakeEvent() );

View File

@ -79,6 +79,39 @@ public:
/// @copydoc TOOL_INTERACTIVE::Init()
bool Init() override;
/**
* Break a single segment into two at the specified point.
*
* @param aCommit Transaction container used to record changes for undo/redo
* @param aSegment Line segment to break
* @param aPoint Point at which to break the segment
* @param aNewSegment Pointer to the newly created segment (if created)
* @param aScreen is the screen to examine
*/
void BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint, SCH_LINE** aNewSegment,
SCH_SCREEN* aScreen );
/**
* Check every wire and bus for a intersection at \a aPoint and break into two segments
* at \a aPoint if an intersection is found.
*
* @param aCommit Transaction container used to record changes for undo/redo
* @param aPoint Test this point for an intersection.
* @param aScreen is the screen to examine.
* @return True if any wires or buses were broken.
*/
bool BreakSegments( SCH_COMMIT* aCommit, const VECTOR2I& aPoint, SCH_SCREEN* aScreen );
/**
* Test all junctions and bus entries in the schematic for intersections with wires and
* buses and breaks any intersections into multiple segments.
*
* @param aCommit Transaction container used to record changes for undo/redo
* @param aScreen is the screen to examine.
* @return True if any wires or buses were broken.
*/
bool BreakSegmentsOnJunctions( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen );
int DrawSegments( const TOOL_EVENT& aEvent );
int UnfoldBus( const TOOL_EVENT& aEvent );
@ -90,6 +123,8 @@ public:
*/
int AddJunctionsIfNeeded( SCH_COMMIT* aCommit, SCH_SELECTION* aSelection );
SCH_JUNCTION* AddJunction( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen, const VECTOR2I& aPos );
/**
* Logic to remove wires when overlapping correct items
*/

View File

@ -1075,15 +1075,16 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
m_selectionTool->RemoveItemsFromSel( &m_dragAdditions, QUIET_MODE );
SCH_LINE_WIRE_BUS_TOOL* lwbTool = m_toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>();
// If we move items away from a junction, we _may_ want to add a junction there
// to denote the state.
for( const DANGLING_END_ITEM& it : internalPoints )
{
if( m_frame->GetScreen()->IsExplicitJunctionNeeded( it.GetPosition()) )
m_frame->AddJunction( aCommit, m_frame->GetScreen(), it.GetPosition() );
lwbTool->AddJunction( aCommit, m_frame->GetScreen(), it.GetPosition() );
}
SCH_LINE_WIRE_BUS_TOOL* lwbTool = m_toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>();
lwbTool->TrimOverLappingWires( aCommit, &selectionCopy );
lwbTool->AddJunctionsIfNeeded( aCommit, &selectionCopy );