← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Eeschema automatic manage junctions

 

Here is the re-based patchset.  No code changes but I repaired the coding
policy violations.

Best-
Seth

On Wed, Nov 29, 2017 at 7:40 AM, Wayne Stambaugh <stambaughw@xxxxxxxxx>
wrote:

> Seth,
>
> I've done some minimal testing with this patch set and it appears to
> work as expected.  I like the way it handles junctions and it does
> resolve the undo issue I was having with the overlapped wires/buses.  I
> am assuming this is the latest patch set and that there are no other
> patches that need to be applied other than the patches attached to this
> message.  If no one objects, I will merge this.
>
> Thanks,
>
> Wayne
>
> On 11/27/2017 6:03 PM, Seth Hillbrand wrote:
> > Hi Orson-
> >
> > Thanks for the pointer.  I found and corrected the issue and updated the
> > commit messages to remove the NEW/CHANGED tags that didn't reflect
> > user-visible changes.
> >
> > Since we're now in feature freeze, this may be too late for 5.0.
> > However, this does address a number of bug reports so I'm attaching the
> > minimized patchset in case Wayne would like to merge it.  If Wayne
> > decides that this more feature/less bugfix, I'll hold onto the patchset
> > until we have a 6-dev branch.
> >
> > Best-
> > Seth
> >
> > On Sat, Nov 25, 2017 at 7:37 AM, Maciej Suminski
> > <maciej.suminski@xxxxxxx <mailto:maciej.suminski@xxxxxxx>> wrote:
> >
> >     Hi Seth,
> >
> >     I tried this with interf_u demo shipped with the source code. There
> are
> >     four capacitors in the left bottom corner - try dragging one of the
> >     middle ones out. If you cannot reproduce the problem, I will try to
> >     shoot a video.
> >
> >     Cheers,
> >     Orson
> >
> >     On 11/25/2017 03:58 PM, Seth Hillbrand wrote:
> >     > Hi Orson-
> >     >
> >     > Thanks for the test and the clarification of the documentation.
> >     >
> >     > On the schematic, I can't seem to recreate the issue you document
> >     in the
> >     > image.  Is the example schematic one you can share?
> >     >
> >     > Best-
> >     > Seth
> >     >
> >     > On Fri, Nov 24, 2017 at 6:50 AM, Maciej Sumiński
> >     <maciej.suminski@xxxxxxx <mailto:maciej.suminski@xxxxxxx>>
> >     > wrote:
> >     >
> >     >> Hi Seth,
> >     >>
> >     >> I tested the branch and I consider it a significant improvement
> >     to the
> >     >> way junctions are handled. I confirm it fixes the four bugs
> >     mentioned in
> >     >> patch 0011, apart from a single case when a parallel component is
> >     >> dragged out. In this case junctions are not still not auto
> >     deleted (see
> >     >> [1], the rightmost picture "Junction is not auto deleted"), but
> >     at least
> >     >> the overlapping wires are merged. It is not a big deal IMHO,
> >     especially
> >     >> the patch set fixes a lot of other issues.
> >     >>
> >     >> It has not been stated explicitly, but I thought we will use
> >     >> NEW/REMOVE/CHANGE tags for listing changes that are perceived by
> the
> >     >> user. Therefore:
> >     >>
> >     >>   CHANGE: DeleteItem removes junctions that are no longer needed.
> >     >>
> >     >> informs the user about an improvement, but:
> >     >>
> >     >>   CHANGE: DeleteItemsInList shares the code for DeleteItem.
> >     >>
> >     >> seems to be too detailed and does not need to be reflected in the
> >     user
> >     >> documentation.
> >     >>
> >     >> What do you think? I will try to come up with a short paragraph
> >     that can
> >     >> be added to the website to make things clear.
> >     >>
> >     >> Regards,
> >     >> Orson
> >     >>
> >     >> 1.
> >     >> https://launchpadlibrarian.net/213748651/KICAD%20-%20BUG%
> >     <https://launchpadlibrarian.net/213748651/KICAD%20-%20BUG%>
> >     >> 20REPORT%20-%20EESCHEMA%20-%20SOME%20DRAG%20ISSUES.jpg
> >     >>
> >     >> On 11/22/2017 09:07 PM, Seth Hillbrand wrote:
> >     >>> Updated patchset for this proposal, rebased to master.  I've
> >     also updated
> >     >>> the commit messages to match the CHANGE:/NEW: format and added
> >     one new
> >     >> bug
> >     >>> from launchpad that this addresses.
> >     >>>
> >     >>> -Seth
> >     >>>
> >     >>> On Wed, Nov 8, 2017 at 3:59 AM, Nick Østergaard
> >     <oe.nick@xxxxxxxxx <mailto:oe.nick@xxxxxxxxx>>
> >     >> wrote:
> >     >>>
> >     >>>> For that specific issue with the junction drawing, there is a
> >     patch in
> >     >> the
> >     >>>> thread "[Kicad-developers] [PATCH] Draw junctions last"
> >     >>>>
> >     >>>> 2017-11-03 13:12 GMT+01:00 Jon Evans <jon@xxxxxxxxxxxxx
> >     <mailto:jon@xxxxxxxxxxxxx>>:
> >     >>>>
> >     >>>>> I looked at fixing this and some other related things, and
> >     decided to
> >     >>>>> just wait for the GAL port. There will need to be huge
> >     refactoring of
> >     >> the
> >     >>>>> eeschema draw code as part of that effort, so putting much
> >     effort into
> >     >>>>> making the wxDC drawing better seems not worth it.
> >     >>>>>
> >     >>>>> -Jon
> >     >>>>>
> >     >>>>> On Nov 3, 2017 00:08, "Kevin Cozens" <kevin@xxxxxxxxx
> >     <mailto:kevin@xxxxxxxxx>> wrote:
> >     >>>>>
> >     >>>>> On 2017-11-02 06:31 PM, Seth Hillbrand wrote:
> >     >>>>>
> >     >>>>>> Please let me know if there are any additional issues or
> >     suggestions
> >     >> for
> >     >>>>>> improvement.
> >     >>>>>>
> >     >>>>>
> >     >>>>> How difficult would it be to have junctions draw last on
> >     schematics?
> >     >>>>> There is a minor negative visual effect when you have a
> >     component with
> >     >> one
> >     >>>>> end joined to a wire by a junction and you replace the
> component.
> >     >>>>>
> >     >>>>> When you replace the component the pin of the component is now
> >     seen
> >     >>>>> extending through the round disc of the junction to the center
> >     of the
> >     >>>>> junction. I prefer to always see just the full round disc of a
> >     junction
> >     >>>>> mark even if I have replaced a component since placing the
> >     junction.
> >     >>>>>
> >     >>>>> --
> >     >>>>> Cheers!
> >     >>>>>
> >     >>>>> Kevin.
> >     >>>>>
> >     >>>>> http://www.ve3syb.ca/           |"Nerds make the shiny things
> that
> >     >>>>> distract
> >     >>>>> Owner of Elecraft K2 #2172      | the mouth-breathers, and
> >     that's why
> >     >>>>> we're
> >     >>>>>                                 | powerful!"
> >     >>>>> #include <disclaimer/favourite> |             --Chris Hardwick
> >     >>>>>
> >     >>>>>
> >     >>>>> _______________________________________________
> >     >>>>> Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>>>> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >>>>> Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>>>> More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >>>>>
> >     >>>>>
> >     >>>>>
> >     >>>>> _______________________________________________
> >     >>>>> Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>>>> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >>>>> Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>>>> More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >>>>>
> >     >>>>>
> >     >>>>
> >     >>>> _______________________________________________
> >     >>>> Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>>> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >>>> Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>>> More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >>>>
> >     >>>>
> >     >>>
> >     >>>
> >     >>>
> >     >>> _______________________________________________
> >     >>> Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >>> Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >>> More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >>>
> >     >>
> >     >>
> >     >>
> >     >>
> >     >> _______________________________________________
> >     >> Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     >> Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     >> More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >     >>
> >     >>
> >     >
> >
> >
> >
> >
> > _______________________________________________
> > Mailing list: https://launchpad.net/~kicad-developers
> > Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> > Unsubscribe : https://launchpad.net/~kicad-developers
> > More help   : https://help.launchpad.net/ListHelp
> >
>
> _______________________________________________
> Mailing list: https://launchpad.net/~kicad-developers
> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~kicad-developers
> More help   : https://help.launchpad.net/ListHelp
>
From 2b14931d2315beaa985bbdda61e158b12eac1939 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Mon, 27 Nov 2017 11:27:24 -0800
Subject: [PATCH 9/9] Eeschema: Automatically manage junctions

CHANGE: eeschema automatically adds and removes junctions
when required by the schematic

Fixes: lp:593888
* https://bugs.launchpad.net/kicad/+bug/593888

Fixes: lp:1482111
* https://bugs.launchpad.net/kicad/+bug/1482111

Fixes: lp:1563153
* https://bugs.launchpad.net/kicad/+bug/1563153

Fixes: lp:1730219
* https://bugs.launchpad.net/kicad/+bug/1730219

Fixes: lp:1491052
* https://bugs.launchpad.net/kicad/+bug/1491052
---
 eeschema/block.cpp                     |  15 ++-
 eeschema/bus-wire-junction.cpp         | 225 +++++++++++++++++++--------------
 eeschema/class_sch_screen.h            |  14 +-
 eeschema/files-io.cpp                  |  10 ++
 eeschema/hierarch.cpp                  |   8 +-
 eeschema/operations_on_items_lists.cpp |  36 ++++++
 eeschema/project_rescue.cpp            |   1 +
 eeschema/sch_line.cpp                  |  23 ++--
 eeschema/sch_line.h                    |   6 +-
 eeschema/sch_screen.cpp                |  83 +++++++-----
 eeschema/schedit.cpp                   |   3 +
 eeschema/schframe.cpp                  |   9 ++
 eeschema/schframe.h                    |  25 +++-
 13 files changed, 299 insertions(+), 159 deletions(-)

diff --git a/eeschema/block.cpp b/eeschema/block.cpp
index f4aa8517c..b092d523d 100644
--- a/eeschema/block.cpp
+++ b/eeschema/block.cpp
@@ -127,9 +127,8 @@ void SCH_EDIT_FRAME::HandleBlockPlace( wxDC* DC )
         if( m_canvas->IsMouseCaptured() )
             m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
 
-        SaveCopyInUndoList( block->GetItems(), UR_MOVED, false, block->GetMoveVector() );
+        SaveCopyInUndoList( block->GetItems(), UR_CHANGED, false, block->GetMoveVector() );
         MoveItemsInList( block->GetItems(), block->GetMoveVector() );
-        block->ClearItemsList();
         break;
 
     case BLOCK_DUPLICATE:           /* Duplicate */
@@ -141,8 +140,6 @@ void SCH_EDIT_FRAME::HandleBlockPlace( wxDC* DC )
 
         SaveCopyInUndoList( block->GetItems(),
                             ( block->GetCommand() == BLOCK_PRESELECT_MOVE ) ? UR_CHANGED : UR_NEW );
-
-        block->ClearItemsList();
         break;
 
     case BLOCK_PASTE:
@@ -150,13 +147,15 @@ void SCH_EDIT_FRAME::HandleBlockPlace( wxDC* DC )
             m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
 
         PasteListOfItems( DC );
-        block->ClearItemsList();
         break;
 
     default:        // others are handled by HandleBlockEnd()
        break;
     }
 
+    CheckJunctionsInList( block->GetItems(), true );
+    block->ClearItemsList();
+    SchematicCleanUp( true );
     OnModify();
 
     // clear dome flags and pointers
@@ -218,6 +217,8 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 SetCrossHairPosition( rotationPoint );
                 SaveCopyInUndoList( block->GetItems(), UR_ROTATED, false, rotationPoint );
                 RotateListOfItems( block->GetItems(), rotationPoint );
+                CheckJunctionsInList( block->GetItems(), true );
+                SchematicCleanUp( true );
                 OnModify();
             }
 
@@ -270,6 +271,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
             if( block->GetCount() )
             {
                 DeleteItemsInList( block->GetItems() );
+                SchematicCleanUp( true );
                 OnModify();
             }
             block->ClearItemsList();
@@ -301,6 +303,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 copyBlockItems( block->GetItems() );
                 MoveItemsInList( m_blockItems.GetItems(), move_vector );
                 DeleteItemsInList( block->GetItems() );
+                SchematicCleanUp( true );
                 OnModify();
             }
 
@@ -329,6 +332,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 SetCrossHairPosition( mirrorPoint );
                 SaveCopyInUndoList( block->GetItems(), UR_MIRRORED_X, false, mirrorPoint );
                 MirrorX( block->GetItems(), mirrorPoint );
+                SchematicCleanUp( true );
                 OnModify();
             }
 
@@ -348,6 +352,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 SetCrossHairPosition( mirrorPoint );
                 SaveCopyInUndoList( block->GetItems(), UR_MIRRORED_Y, false, mirrorPoint );
                 MirrorY( block->GetItems(), mirrorPoint );
+                SchematicCleanUp( true );
                 OnModify();
             }
 
diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp
index f3e2f2171..c5882810a 100644
--- a/eeschema/bus-wire-junction.cpp
+++ b/eeschema/bus-wire-junction.cpp
@@ -267,81 +267,77 @@ void SCH_EDIT_FRAME::BeginSegment( wxDC* DC, int type )
 }
 
 
+void SCH_EDIT_FRAME::GetSchematicConnections( std::vector< wxPoint >& aConnections )
+{
+    for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = item->Next() )
+        item->GetConnectionPoints( aConnections );
+
+    // We always have some overlapping connection points.  Drop duplicates here
+    std::sort( aConnections.begin(), aConnections.end(),
+            []( wxPoint& a, wxPoint& b ) -> bool
+            { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
+    aConnections.erase( unique( aConnections.begin(), aConnections.end() ), aConnections.end() );
+}
+
+
 void SCH_EDIT_FRAME::EndSegment()
 {
     SCH_SCREEN* screen = GetScreen();
     SCH_LINE* segment = (SCH_LINE*) screen->GetCurItem();
+    PICKED_ITEMS_LIST itemList;
 
     if( segment == NULL || segment->Type() != SCH_LINE_T || !segment->IsNew() )
         return;
 
-    // Delete zero length segments and clear item flags.
-    SCH_ITEM* item = s_wires.begin();
-
-    while( item )
-    {
-        item->ClearFlags();
-
-        wxCHECK_RET( item->Type() == SCH_LINE_T, wxT( "Unexpected object type in wire list." ) );
-
-        segment = (SCH_LINE*) item;
-        item = item->Next();
-
-        if( segment->IsNull() )
-            delete s_wires.Remove( segment );
-    }
+    // Remove segments backtracking over others
+    RemoveBacktracks( s_wires );
 
     if( s_wires.GetCount() == 0 )
         return;
 
-    // Get the last non-null wire (this is the last created segment).
-    SetRepeatItem( segment = (SCH_LINE*) s_wires.GetLast() );
+    // Collect the possible connection points for the new lines
+    std::vector< wxPoint > connections;
+    std::vector< wxPoint > new_ends;
+    GetSchematicConnections( connections );
 
-    screen->SetCurItem( NULL );
-    m_canvas->EndMouseCapture( -1, -1, wxEmptyString, false );
-
-    // store the terminal point of this last segment: a junction could be needed
-    // (the last wire could be merged/deleted/modified, and lost)
-    wxPoint endpoint = segment->GetEndPoint();
-
-    // store the starting point of this first segment: a junction could be needed
-    SCH_LINE* firstsegment = (SCH_LINE*) s_wires.GetFirst();
-    wxPoint startPoint = firstsegment->GetStartPoint();
+    // Check each new segment for possible junctions and add/split if needed
+    for( SCH_ITEM* wire = s_wires.GetFirst(); wire; wire=wire->Next() )
+    {
+        SCH_LINE* test_line = (SCH_LINE*) wire;
+        if( wire->GetFlags() & SKIP_STRUCT )
+            continue;
 
-    // Save the old wires for the undo command
-    DLIST< SCH_ITEM > oldWires;                     // stores here the old wires
-    GetScreen()->ExtractWires( oldWires, true );    // Save them in oldWires list
-    // Put the snap shot of the previous wire, buses, and junctions in the undo/redo list.
-    PICKED_ITEMS_LIST oldItems;
-    oldItems.m_Status = UR_WIRE_IMAGE;
+        wire->GetConnectionPoints( new_ends );
 
-    while( oldWires.GetCount() != 0 )
-    {
-        ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
-        oldItems.PushItem( picker );
+        for( auto i : connections )
+        {
+            if( IsPointOnSegment( test_line->GetStartPoint(), test_line->GetEndPoint(), i ) )
+            {
+                new_ends.push_back( i );
+            }
+        }
+        itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) );
     }
 
-    SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );
-
-    // Remove segments backtracking over others
-    RemoveBacktracks( s_wires );
+    // Get the last non-null wire (this is the last created segment).
+    SetRepeatItem( segment = (SCH_LINE*) s_wires.GetLast() );
 
     // Add the new wires
     screen->Append( s_wires );
+    SaveCopyInUndoList(itemList, UR_NEW);
 
     // Correct and remove segments that need to be merged.
-    SchematicCleanUp();
-
-    // A junction could be needed to connect the end point of the last created segment.
-    if( screen->IsJunctionNeeded( endpoint ) )
-        screen->Append( AddJunction( endpoint ) );
-
-    // A junction could be needed to connect the start point of the set of new created wires
-    if( screen->IsJunctionNeeded( startPoint ) )
-        screen->Append( AddJunction( startPoint ) );
-
-    m_canvas->Refresh();
+    SchematicCleanUp( true );
+    for( auto i : new_ends )
+    {
+        if( screen->IsJunctionNeeded( i, true ) )
+            AddJunction( i, true );
+    }
 
+    screen->TestDanglingEnds();
+    screen->ClearDrawingState();
+    screen->SetCurItem( NULL );
+    m_canvas->EndMouseCapture( -1, -1, wxEmptyString, false );
     OnModify();
 }
 
@@ -441,54 +437,100 @@ void SCH_EDIT_FRAME::SaveWireImage()
 }
 
 
-bool SCH_EDIT_FRAME::SchematicCleanUp()
+bool SCH_EDIT_FRAME::SchematicCleanUp( bool aAppend )
 {
-    bool      modified = false;
+    SCH_ITEM*           item = NULL;
+    SCH_ITEM*           secondItem = NULL;
+    PICKED_ITEMS_LIST   itemList;
+    SCH_SCREEN*         screen = GetScreen();
 
-    for( SCH_ITEM* item = GetScreen()->GetDrawItems() ; item; item = item->Next() )
+    auto remove_item = [ &itemList, screen ]( SCH_ITEM* aItem ) -> void
     {
-        if( ( item->Type() != SCH_LINE_T ) && ( item->Type() != SCH_JUNCTION_T ) )
+        aItem->SetFlags( STRUCT_DELETED );
+        itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
+    };
+
+    BreakSegmentsOnJunctions( true );
+
+    for( item = screen->GetDrawItems(); item; item = item->Next() )
+    {
+        if( ( item->Type() != SCH_LINE_T ) &&
+            ( item->Type() != SCH_JUNCTION_T ) &&
+            ( item->Type() != SCH_NO_CONNECT_T ))
             continue;
 
-        bool restart;
+        if( item->GetFlags() & STRUCT_DELETED )
+            continue;
 
-        for( SCH_ITEM* testItem = item->Next(); testItem; testItem = restart ? GetScreen()->GetDrawItems() : testItem->Next() )
+        // Remove unneeded junctions
+        if( ( item->Type() == SCH_JUNCTION_T )
+                && ( !screen->IsJunctionNeeded( item->GetPosition() ) ) )
         {
-            restart = false;
+            remove_item( item );
+            continue;
+        }
+        // Remove zero-length lines
+        if( item->Type() == SCH_LINE_T
+                && ( (SCH_LINE*) item )->IsNull() )
+        {
+            remove_item( item );
+            continue;
+        }
 
-            if( ( item->Type() == SCH_LINE_T ) && ( testItem->Type() == SCH_LINE_T ) )
-            {
-                SCH_LINE* line = (SCH_LINE*) item;
+        for( secondItem = item->Next(); secondItem; secondItem = secondItem->Next() )
+        {
+            if( item->Type() != secondItem->Type() || ( secondItem->GetFlags() & STRUCT_DELETED ) )
+                continue;
 
-                if( line->MergeOverlap( (SCH_LINE*) testItem ) )
-                {
-                    // Keep the current flags, because the deleted segment can be flagged.
-                    item->SetFlags( testItem->GetFlags() );
-                    DeleteItem( testItem );
-                    restart = true;
-                    modified = true;
-                }
-            }
-            else if ( ( ( item->Type() == SCH_JUNCTION_T )
-                      && ( testItem->Type() == SCH_JUNCTION_T ) ) && ( testItem != item ) )
+            // Merge overlapping lines
+            if( item->Type() == SCH_LINE_T )
             {
-                if ( testItem->HitTest( item->GetPosition() ) )
+                SCH_LINE* firstLine = (SCH_LINE*) item;
+                SCH_LINE* secondLine = (SCH_LINE*) secondItem;
+                SCH_LINE* line = NULL;
+                bool needed = false;
+
+                if( !secondLine->IsParallel( firstLine ) )
+                    continue;
+
+                // Check if a junction needs to be kept
+                // This can only happen if:
+                //   1) the endpoints overlap,
+                //   2) the lines are not pointing in the same direction AND
+                //   3) IsJunction Needed is false
+                if( secondLine->IsEndPoint( firstLine->GetStartPoint() )
+                        && !secondLine->IsSameQuadrant( firstLine, firstLine->GetStartPoint() ) )
+                    needed = screen->IsJunctionNeeded( firstLine->GetStartPoint() );
+                else if( secondLine->IsEndPoint( firstLine->GetEndPoint() )
+                        && !secondLine->IsSameQuadrant( firstLine, firstLine->GetEndPoint() ) )
+                    needed = screen->IsJunctionNeeded( firstLine->GetEndPoint() );
+
+                if( !needed && ( line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) ) )
                 {
-                    // Keep the current flags, because the deleted segment can be flagged.
-                    item->SetFlags( testItem->GetFlags() );
-                    DeleteItem( testItem );
-                    restart = true;
-                    modified = true;
+                    remove_item( item );
+                    remove_item( secondItem );
+                    itemList.PushItem( ITEM_PICKER( line, UR_NEW ) );
+                    screen->Append( (SCH_ITEM*) line );
+                    break;
                 }
             }
+            // Remove duplicate junctions and no-connects
+            else if( secondItem->GetPosition() == item->GetPosition() )
+                remove_item( secondItem );
         }
     }
+    for( item = screen->GetDrawItems(); item; item = secondItem )
+    {
+        secondItem = item->Next();
+        if( item->GetFlags() & STRUCT_DELETED )
+            screen->Remove( item );
+    }
+    SaveCopyInUndoList( itemList, UR_CHANGED, aAppend );
 
-    GetScreen()->TestDanglingEnds();
-
-    return modified;
+    return !!( itemList.GetCount() );
 }
 
+
 bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE *aSegment, const wxPoint& aPoint, bool aAppend )
 {
     if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
@@ -552,20 +594,17 @@ bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( bool aAppend )
 }
 
 
-SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition,
-                                           bool aPutInUndoList )
+SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition, bool aAppend )
 {
     SCH_JUNCTION* junction = new SCH_JUNCTION( aPosition );
+    SCH_SCREEN* screen = GetScreen();
+    bool broken_segments = false;
 
-    SetRepeatItem( junction );
-
-    if( aPutInUndoList )
-    {
-        GetScreen()->Append( junction );
-        SaveCopyInUndoList( junction, UR_NEW );
-        OnModify();
-    }
-
+    screen->Append( junction );
+    broken_segments = BreakSegments( aPosition, aAppend );
+    screen->TestDanglingEnds();
+    OnModify();
+    SaveCopyInUndoList( junction, UR_NEW, broken_segments || aAppend );
     return junction;
 }
 
diff --git a/eeschema/class_sch_screen.h b/eeschema/class_sch_screen.h
index dd11205e0..a90763f35 100644
--- a/eeschema/class_sch_screen.h
+++ b/eeschema/class_sch_screen.h
@@ -336,19 +336,19 @@ public:
     /**
      * Test if a junction is required for the items at \a aPosition on the screen.
      * <p>
-     * A junction is required at \a aPosition if the following criteria are satisfied:
+     * A junction is required at \a aPosition if one of the following criteria is satisfied:
      * <ul>
-     * <li>one wire midpoint, one or more wire endpoints and no junction.</li>
-     * <li>three or more wire endpoints and no junction.</li>
-     * <li>two wire midpoints and no junction</li>
-     * <li>one wire midpoint, a component pin, and no junction.</li>
-     * <li>three wire endpoints, a component pin, and no junction.</li>
+     * <li>one wire midpoint and one or more wire endpoints;</li>
+     * <li>three or more wire endpoints;</li>
+     * <li>one wire midpoint and a component pin;</li>
+     * <li>two or more wire endpoints and a component pin.</li>
      * </ul>
      * </p>
      * @param aPosition The position to test.
+     * @param aNew Checks if a _new_ junction is needed, i.e. there isn't one already
      * @return True if a junction is required at \a aPosition.
      */
-    bool IsJunctionNeeded( const wxPoint& aPosition );
+    bool IsJunctionNeeded( const wxPoint& aPosition, bool aNew = false );
 
     /**
      * Test if \a aPosition is a connection point on \a aLayer.
diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp
index 931210b2c..94b0daeee 100644
--- a/eeschema/files-io.cpp
+++ b/eeschema/files-io.cpp
@@ -356,6 +356,11 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
         }
 
         schematic.UpdateSymbolLinks();      // Update all symbol library links for all sheets.
+
+        // Ensure the schematic is fully segmented on first display
+        BreakSegmentsOnJunctions();
+        SchematicCleanUp( true );
+        GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
         GetScreen()->TestDanglingEnds();    // Only perform the dangling end test on root sheet.
     }
 
@@ -768,6 +773,11 @@ bool SCH_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
 
                 UpdateFileHistory( fullFileName );
                 schematic.UpdateSymbolLinks();      // Update all symbol library links for all sheets.
+
+                // Ensure the schematic is fully segmented on first display
+                BreakSegmentsOnJunctions();
+                SchematicCleanUp( true );
+                GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
                 GetScreen()->TestDanglingEnds();    // Only perform the dangling end test on root sheet.
 
                 GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
diff --git a/eeschema/hierarch.cpp b/eeschema/hierarch.cpp
index c41c82164..c65f388c4 100644
--- a/eeschema/hierarch.cpp
+++ b/eeschema/hierarch.cpp
@@ -299,7 +299,13 @@ void SCH_EDIT_FRAME::DisplayCurrentSheet()
         screen->m_FirstRedraw = false;
         SetCrossHairPosition( GetScrollCenterPosition() );
         m_canvas->MoveCursorToCrossHair();
-        SchematicCleanUp();
+
+        // Ensure the schematic is fully segmented on first display
+        BreakSegmentsOnJunctions();
+        SchematicCleanUp( true );
+        screen->ClearUndoORRedoList( screen->m_UndoList, 1 );
+
+        screen->TestDanglingEnds();
     }
     else
     {
diff --git a/eeschema/operations_on_items_lists.cpp b/eeschema/operations_on_items_lists.cpp
index ad8313d38..938ce6062 100644
--- a/eeschema/operations_on_items_lists.cpp
+++ b/eeschema/operations_on_items_lists.cpp
@@ -115,6 +115,42 @@ void MoveItemsInList( PICKED_ITEMS_LIST& aItemsList, const wxPoint& aMoveVector
 }
 
 
+void SCH_EDIT_FRAME::CheckJunctionsInList( PICKED_ITEMS_LIST& aItemsList, bool aAppend )
+{
+    std::vector< wxPoint > pts;
+    std::vector< wxPoint > connections;
+
+    GetSchematicConnections( connections );
+    for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
+    {
+        SCH_ITEM* item = (SCH_ITEM*) aItemsList.GetPickedItem( ii );
+        item->GetConnectionPoints( pts );
+        if( item->Type() == SCH_LINE_T )
+        {
+            SCH_LINE* line = (SCH_LINE*) item;
+            for( auto i : connections )
+                if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), i ) )
+                    pts.push_back( i );
+        }
+    }
+
+    // We always have some overlapping connection points.  Drop duplicates here
+    std::sort( pts.begin(), pts.end(),
+            []( wxPoint& a, wxPoint& b ) -> bool
+            { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
+    pts.erase( unique( pts.begin(), pts.end() ), pts.end() );
+
+    for( auto point : pts )
+    {
+        if( GetScreen()->IsJunctionNeeded( point, true ) )
+        {
+            AddJunction( point, aAppend );
+            aAppend = true;
+        }
+    }
+}
+
+
 void SCH_EDIT_FRAME::DeleteItemsInList( PICKED_ITEMS_LIST& aItemsList, bool aAppend )
 {
     PICKED_ITEMS_LIST  itemsList;
diff --git a/eeschema/project_rescue.cpp b/eeschema/project_rescue.cpp
index 0af8db517..61a096ac0 100644
--- a/eeschema/project_rescue.cpp
+++ b/eeschema/project_rescue.cpp
@@ -557,6 +557,7 @@ bool SCH_EDIT_FRAME::rescueProject( RESCUER& aRescuer, bool aRunningOnDemand )
 
     // Clean up wire ends
     SchematicCleanUp();
+    GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
     m_canvas->Refresh( true );
     OnModify();
 
diff --git a/eeschema/sch_line.cpp b/eeschema/sch_line.cpp
index 7a05fee98..ce4ba4cb0 100644
--- a/eeschema/sch_line.cpp
+++ b/eeschema/sch_line.cpp
@@ -388,7 +388,8 @@ bool SCH_LINE::IsParallel( SCH_LINE* aLine )
     return !( (long long) firstSeg.x * secondSeg.y - (long long) firstSeg.y * secondSeg.x );
 }
 
-bool SCH_LINE::MergeOverlap( SCH_LINE* aLine )
+
+EDA_ITEM* SCH_LINE::MergeOverlap( SCH_LINE* aLine )
 {
     auto less = []( const wxPoint& lhs, const wxPoint& rhs ) -> bool
     {
@@ -396,12 +397,11 @@ bool SCH_LINE::MergeOverlap( SCH_LINE* aLine )
             return lhs.y < rhs.y;
         return lhs.x < rhs.x;
     };
-
-    wxCHECK_MSG( aLine != NULL && aLine->Type() == SCH_LINE_T, false,
+    wxCHECK_MSG( aLine != NULL && aLine->Type() == SCH_LINE_T, NULL,
                  wxT( "Cannot test line segment for overlap." ) );
 
     if( this == aLine || GetLayer() != aLine->GetLayer() )
-        return false;
+        return NULL;
 
     SCH_LINE leftmost = SCH_LINE( *aLine );
     SCH_LINE rightmost = SCH_LINE( *this );
@@ -428,16 +428,14 @@ bool SCH_LINE::MergeOverlap( SCH_LINE* aLine )
     // If we end one before the beginning of the other, no overlap is possible
     if( less( leftmost.m_end, other.m_start ) )
     {
-        return false;
+        return NULL;
     }
 
     // Search for a common end:
     if( ( leftmost.m_start == other.m_start )
             && ( leftmost.m_end == other.m_end ) )     // Trivial case
     {
-        m_start = leftmost.m_start;
-        m_end = leftmost.m_end;
-        return true;
+        return new SCH_LINE( leftmost );
     }
 
     bool colinear = false;
@@ -471,12 +469,11 @@ bool SCH_LINE::MergeOverlap( SCH_LINE* aLine )
     // Make a new segment that merges the 2 segments
     if( colinear )
     {
-        m_start = leftmost.m_start;
-        m_end = rightmost.m_end;
-        return true;
+        leftmost.m_end = rightmost.m_end;
+        return new SCH_LINE( leftmost );
     }
 
-    return false;
+    return NULL;
 }
 
 
@@ -696,6 +693,7 @@ bool SCH_LINE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy )
     return rect.Intersects( m_start, m_end );
 }
 
+
 void SCH_LINE::SwapData( SCH_ITEM* aItem )
 {
     SCH_LINE* item = (SCH_LINE*) aItem;
@@ -711,6 +709,7 @@ void SCH_LINE::SwapData( SCH_ITEM* aItem )
     std::swap( m_color, item->m_color );
 }
 
+
 bool SCH_LINE::doIsConnected( const wxPoint& aPosition ) const
 {
     if( m_Layer != LAYER_WIRE && m_Layer != LAYER_BUS )
diff --git a/eeschema/sch_line.h b/eeschema/sch_line.h
index 6b7594c31..e82d48268 100644
--- a/eeschema/sch_line.h
+++ b/eeschema/sch_line.h
@@ -133,14 +133,14 @@ public:
     /**
      * Check line against \a aLine to see if it overlaps and merge if it does.
      *
-     * This method will change the line to be equivalent of the line and \a aLine if the
+     * This method will return an equivalent of the union of line and \a aLine if the
      * two lines overlap.  This method is used to merge multiple line segments into a single
      * line.
      *
      * @param aLine - Line to compare.
-     * @return True if lines overlap and the line was merged with \a aLine.
+     * @return New line that combines the two or NULL on non-overlapping segments.
      */
-    bool MergeOverlap( SCH_LINE* aLine );
+    EDA_ITEM* MergeOverlap( SCH_LINE* aLine );
 
     /**
      * Check if two lines are in the same quadrant as each other, using a reference point as
diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp
index c263787d7..f3f26b07d 100644
--- a/eeschema/sch_screen.cpp
+++ b/eeschema/sch_screen.cpp
@@ -55,6 +55,7 @@
 #include <lib_pin.h>
 #include <symbol_lib_table.h>
 
+#include <boost/foreach.hpp>
 
 #define EESCHEMA_FILE_STAMP   "EESchema"
 
@@ -346,20 +347,60 @@ void SCH_SCREEN::MarkConnections( SCH_LINE* aSegment )
 }
 
 
-bool SCH_SCREEN::IsJunctionNeeded( const wxPoint& aPosition )
+bool SCH_SCREEN::IsJunctionNeeded( const wxPoint& aPosition, bool aNew )
 {
-    if( GetItem( aPosition, 0, SCH_JUNCTION_T ) )
-        return false;
+    bool has_line = false;
+    bool has_nonparallel = false;
+    int end_count = 0;
+
+    std::vector< SCH_LINE* > lines;
 
-    if( GetWire( aPosition, 0, EXCLUDE_END_POINTS_T ) )
+    for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() )
     {
-        if( GetWire( aPosition, 0, END_POINTS_ONLY_T ) )
-            return true;
+        if( item->GetFlags() & STRUCT_DELETED )
+            continue;
+        if( aNew && ( item->Type() == SCH_JUNCTION_T ) && ( item->HitTest( aPosition ) ) )
+            return false;
 
-        if( GetPin( aPosition, NULL, true ) )
-            return true;
+        if( item->Type() != SCH_LINE_T )
+            continue;
+
+        if( item->GetLayer() != LAYER_WIRE )
+            continue;
+
+        if( item->HitTest( aPosition, 0 ) )
+            lines.push_back( (SCH_LINE*) item );
+    }
+
+    BOOST_FOREACH( SCH_LINE* line, lines)
+    {
+        if( !line->IsEndPoint( aPosition ) )
+            has_line = true;
+        else
+            end_count++;
+        BOOST_REVERSE_FOREACH( SCH_LINE* second_line, lines )
+        {
+            if( line == second_line )
+                break;
+            if( line->IsEndPoint( second_line->GetStartPoint() )
+                    && line->IsEndPoint( second_line->GetEndPoint() ) )
+                end_count--;
+            if( !line->IsParallel( second_line ) )
+                has_nonparallel = true;
+        }
     }
 
+    int has_pin = !!( GetPin( aPosition, NULL, true ) );
+
+    // If there is line intersecting a pin or non-parallel end
+    if( has_pin && ( has_line || end_count > 1 ) )
+        return true;
+
+    // If there is at least one segment that ends on a non-parallel line or
+    // junction of two other lines
+    if( has_nonparallel && (has_line || end_count > 2 ) )
+        return true;
+
     return false;
 }
 
@@ -1098,30 +1139,6 @@ int SCH_SCREEN::GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aLis
             }
         }
 
-        // Get redundant junctions (junctions which connect < 3 end wires
-        // and no pin)
-        for( item = m_drawList.begin(); item; item = item->Next() )
-        {
-            if( item->GetFlags() & STRUCT_DELETED )
-                continue;
-
-            if( !(item->GetFlags() & CANDIDATE) )
-                continue;
-
-            if( item->Type() != SCH_JUNCTION_T )
-                continue;
-
-            SCH_JUNCTION* junction = (SCH_JUNCTION*) item;
-
-            if( CountConnectedItems( junction->GetPosition(), false ) <= 2 )
-            {
-                item->SetFlags( STRUCT_DELETED );
-
-                ITEM_PICKER picker( item, UR_DELETED );
-                aList.PushItem( picker );
-            }
-        }
-
         for( item = m_drawList.begin(); item;  item = item->Next() )
         {
             if( item->GetFlags() & STRUCT_DELETED )
@@ -1132,7 +1149,7 @@ int SCH_SCREEN::GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aLis
 
             tmp = GetWireOrBus( ( (SCH_TEXT*) item )->GetPosition() );
 
-            if( tmp && tmp->GetFlags() & STRUCT_DELETED )
+            if( tmp && ( tmp->GetFlags() & STRUCT_DELETED ) )
             {
                 item->SetFlags( STRUCT_DELETED );
 
diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp
index 37a9fd652..b9dad1587 100644
--- a/eeschema/schedit.cpp
+++ b/eeschema/schedit.cpp
@@ -190,6 +190,7 @@ void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
     case ID_POPUP_SCH_DELETE_CONNECTION:
         m_canvas->MoveCursorToCrossHair();
         DeleteConnection( id == ID_POPUP_SCH_DELETE_CONNECTION );
+        SchematicCleanUp( true );
         screen->SetCurItem( NULL );
         SetRepeatItem( NULL );
 
@@ -214,6 +215,7 @@ void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
             break;
 
         DeleteItem( item );
+        SchematicCleanUp( true );
         screen->SetCurItem( NULL );
         SetRepeatItem( NULL );
         screen->TestDanglingEnds();
@@ -630,6 +632,7 @@ void SCH_EDIT_FRAME::DeleteConnection( bool aFullConnection )
     if( screen->GetConnection( pos, pickList, aFullConnection ) != 0 )
     {
         DeleteItemsInList( pickList );
+        SchematicCleanUp( true );
         OnModify();
     }
 }
diff --git a/eeschema/schframe.cpp b/eeschema/schframe.cpp
index ea0043ad9..f2f28d51b 100644
--- a/eeschema/schframe.cpp
+++ b/eeschema/schframe.cpp
@@ -1443,7 +1443,16 @@ void SCH_EDIT_FRAME::addCurrentItemToList( bool aRedraw )
     m_canvas->EndMouseCapture();
 
     if( item->IsConnectable() )
+    {
+        std::vector< wxPoint > pts;
+        item->GetConnectionPoints( pts );
+        for( auto i : pts )
+        {
+            if( screen->IsJunctionNeeded( i, true ) )
+                AddJunction( i, true );
+        }
         screen->TestDanglingEnds();
+    }
 
     if( aRedraw )
         GetCanvas()->Refresh();
diff --git a/eeschema/schframe.h b/eeschema/schframe.h
index 7f7aa260d..90366584d 100644
--- a/eeschema/schframe.h
+++ b/eeschema/schframe.h
@@ -937,19 +937,25 @@ private:
     SCH_JUNCTION* AddJunction( const wxPoint& aPosition, bool aPutInUndoList = false );
 
     /**
-     * Function SaveWireImage
-     * saves a copy of the current wire image in the undo list
+     * Save a copy of the current wire image in the undo list.
      */
     void SaveWireImage();
 
     /**
-     * Function SchematicCleanUp
-     * performs routine schematic cleaning including breaking wire and buses and
+     * Collects a unique list of all possible connection points in the schematic.
+     *
+     * @param aConnections vector of connections
+     */
+    void GetSchematicConnections( std::vector< wxPoint >& aConnections );
+
+    /**
+     * Performs routine schematic cleaning including breaking wire and buses and
      * deleting identical objects superimposed on top of each other.
      *
+     * @param aAppend The changes to the schematic should be appended to the previous undo
      * @return True if any schematic clean up was performed.
      */
-    bool SchematicCleanUp();
+    bool SchematicCleanUp( bool aAppend = false );
 
     /**
      * Start moving \a aItem using the mouse.
@@ -1128,6 +1134,15 @@ public:
      */
     void DeleteItemsInList( PICKED_ITEMS_LIST& aItemsList, bool aAppend = false );
 
+    /**
+     * Adds junctions if needed to each item in the list after they have been
+     * moved.
+     *
+     * @param aItemsList The list of items to check
+     * @param aAppend True if we are updating a previous commit
+     */
+    void CheckJunctionsInList( PICKED_ITEMS_LIST& aItemsList, bool aAppend = false );
+
     int GetLabelIncrement() const { return m_repeatLabelDelta; }
 
 private:
-- 
2.11.0

From ad8e8dc045098f00def9bc9a6eba403c740d65f8 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Thu, 26 Oct 2017 08:25:47 -0700
Subject: [PATCH 8/9] Eeschema: Moving BreakSegment into SCH_EDIT_FRAME

BreakSegment now breaks a known segment and BreakSegments
breaks all segments. This allows functions to break a
segment without needing to iterate through the whole list.
---
 eeschema/block.cpp             |  3 --
 eeschema/bus-wire-junction.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++
 eeschema/class_sch_screen.h    | 17 ---------
 eeschema/sch_screen.cpp        | 59 -----------------------------
 eeschema/schedit.cpp           | 23 +----------
 eeschema/schframe.h            | 35 +++++++++++++++++
 6 files changed, 123 insertions(+), 100 deletions(-)

diff --git a/eeschema/block.cpp b/eeschema/block.cpp
index f0a52f795..f4aa8517c 100644
--- a/eeschema/block.cpp
+++ b/eeschema/block.cpp
@@ -228,9 +228,6 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
 
         case BLOCK_DRAG:
         case BLOCK_DRAG_ITEM:   // Drag from a drag command
-            GetScreen()->BreakSegmentsOnJunctions();
-            // fall through
-
         case BLOCK_MOVE:
         case BLOCK_DUPLICATE:
             if( block->GetCommand() == BLOCK_DRAG_ITEM &&
diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp
index ea215c302..f3e2f2171 100644
--- a/eeschema/bus-wire-junction.cpp
+++ b/eeschema/bus-wire-junction.cpp
@@ -417,6 +417,30 @@ void SCH_EDIT_FRAME::DeleteCurrentSegment( wxDC* DC )
 }
 
 
+void SCH_EDIT_FRAME::SaveWireImage()
+{
+    DLIST< SCH_ITEM > oldWires;
+
+    oldWires.SetOwnership( false );      // Prevent DLIST for deleting items in destructor.
+    GetScreen()->ExtractWires( oldWires, true );
+
+    if( oldWires.GetCount() != 0 )
+    {
+        PICKED_ITEMS_LIST oldItems;
+
+        oldItems.m_Status = UR_WIRE_IMAGE;
+
+        while( oldWires.GetCount() != 0 )
+        {
+            ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
+            oldItems.PushItem( picker );
+        }
+
+        SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );
+    }
+}
+
+
 bool SCH_EDIT_FRAME::SchematicCleanUp()
 {
     bool      modified = false;
@@ -465,6 +489,68 @@ bool SCH_EDIT_FRAME::SchematicCleanUp()
     return modified;
 }
 
+bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE *aSegment, const wxPoint& aPoint, bool aAppend )
+{
+    if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
+            || aSegment->IsEndPoint( aPoint ) )
+        return false;
+
+    SaveCopyInUndoList( aSegment, UR_CHANGED, aAppend );
+    SCH_LINE* newSegment = new SCH_LINE( *aSegment );
+    SaveCopyInUndoList( newSegment, UR_NEW, true );
+
+    newSegment->SetStartPoint( aPoint );
+    aSegment->SetEndPoint( aPoint );
+    GetScreen()->Append( newSegment );
+
+    return true;
+}
+
+
+bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, bool aAppend )
+{
+    bool brokenSegments = false;
+
+    for( SCH_ITEM* segment = GetScreen()->GetDrawItems(); segment; segment = segment->Next() )
+    {
+        if( ( segment->Type() != SCH_LINE_T ) || ( segment->GetLayer() == LAYER_NOTES ) )
+            continue;
+
+        brokenSegments |= BreakSegment( (SCH_LINE*) segment, aPoint, aAppend || brokenSegments );
+    }
+
+    return brokenSegments;
+}
+
+
+bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( bool aAppend )
+{
+    bool brokenSegments = false;
+
+    for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = item->Next() )
+    {
+        if( item->Type() == SCH_JUNCTION_T )
+        {
+            SCH_JUNCTION* junction = ( SCH_JUNCTION* ) item;
+
+            if( BreakSegments( junction->GetPosition(), brokenSegments || aAppend ) )
+                brokenSegments = true;
+        }
+        else
+        {
+            SCH_BUS_ENTRY_BASE* busEntry = dynamic_cast<SCH_BUS_ENTRY_BASE*>( item );
+            if( busEntry )
+            {
+                if( BreakSegments( busEntry->GetPosition(), brokenSegments || aAppend )
+                 || BreakSegments( busEntry->m_End(), brokenSegments || aAppend ) )
+                    brokenSegments = true;
+            }
+        }
+    }
+
+    return brokenSegments;
+}
+
 
 SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition,
                                            bool aPutInUndoList )
diff --git a/eeschema/class_sch_screen.h b/eeschema/class_sch_screen.h
index 7bd647ff5..dd11205e0 100644
--- a/eeschema/class_sch_screen.h
+++ b/eeschema/class_sch_screen.h
@@ -308,23 +308,6 @@ public:
      */
     int GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aList, bool aFullConnection );
 
-    /**
-     * Checks every wire and bus for a intersection at \a aPoint and break into two segments
-     * at \a aPoint if an intersection is found.
-     *
-     * @param aPoint Test this point for an intersection.
-     * @return True if any wires or buses were broken.
-     */
-    bool BreakSegment( const wxPoint& aPoint );
-
-    /**
-     * Tests all junctions and bus entries in the schematic for intersections with wires and
-     * buses and breaks any intersections into multiple segments.
-     *
-     * @return True if any wires or buses were broken.
-     */
-    bool BreakSegmentsOnJunctions();
-
     /* full undo redo management : */
     // use BASE_SCREEN::PushCommandToUndoList( PICKED_ITEMS_LIST* aItem )
     // use BASE_SCREEN::PushCommandToRedoList( PICKED_ITEMS_LIST* aItem )
diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp
index 11837dc80..c263787d7 100644
--- a/eeschema/sch_screen.cpp
+++ b/eeschema/sch_screen.cpp
@@ -845,64 +845,6 @@ bool SCH_SCREEN::TestDanglingEnds()
 }
 
 
-bool SCH_SCREEN::BreakSegment( const wxPoint& aPoint )
-{
-    SCH_LINE* segment;
-    SCH_LINE* newSegment;
-    bool brokenSegments = false;
-
-    for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() )
-    {
-        if( (item->Type() != SCH_LINE_T) || (item->GetLayer() == LAYER_NOTES) )
-            continue;
-
-        segment = (SCH_LINE*) item;
-
-        if( !segment->HitTest( aPoint, 0 ) || segment->IsEndPoint( aPoint ) )
-            continue;
-
-        // Break the segment at aPoint and create a new segment.
-        newSegment = new SCH_LINE( *segment );
-        newSegment->SetStartPoint( aPoint );
-        segment->SetEndPoint( aPoint );
-        m_drawList.Insert( newSegment, segment->Next() );
-        item = newSegment;
-        brokenSegments = true;
-    }
-
-    return brokenSegments;
-}
-
-
-bool SCH_SCREEN::BreakSegmentsOnJunctions()
-{
-    bool brokenSegments = false;
-
-    for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() )
-    {
-        if( item->Type() == SCH_JUNCTION_T )
-        {
-            SCH_JUNCTION* junction = ( SCH_JUNCTION* ) item;
-
-            if( BreakSegment( junction->GetPosition() ) )
-                brokenSegments = true;
-        }
-        else
-        {
-            SCH_BUS_ENTRY_BASE* busEntry = dynamic_cast<SCH_BUS_ENTRY_BASE*>( item );
-            if( busEntry )
-            {
-                if( BreakSegment( busEntry->GetPosition() )
-                 || BreakSegment( busEntry->m_End() ) )
-                    brokenSegments = true;
-            }
-        }
-    }
-
-    return brokenSegments;
-}
-
-
 int SCH_SCREEN::GetNode( const wxPoint& aPosition, EDA_ITEMS& aList )
 {
     for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() )
@@ -1047,7 +989,6 @@ int SCH_SCREEN::GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aLis
 
     // Clear flags member for all items.
     ClearDrawingState();
-    BreakSegmentsOnJunctions();
 
     if( GetNode( aPosition, list ) == 0 )
         return 0;
diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp
index 61d9bf6e8..37a9fd652 100644
--- a/eeschema/schedit.cpp
+++ b/eeschema/schedit.cpp
@@ -200,28 +200,9 @@ void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
 
     case ID_POPUP_SCH_BREAK_WIRE:
         {
-            DLIST< SCH_ITEM > oldWires;
-
-            oldWires.SetOwnership( false );      // Prevent DLIST for deleting items in destructor.
+            SaveWireImage();
             m_canvas->MoveCursorToCrossHair();
-            screen->ExtractWires( oldWires, true );
-            screen->BreakSegment( GetCrossHairPosition() );
-
-            if( oldWires.GetCount() != 0 )
-            {
-                PICKED_ITEMS_LIST oldItems;
-
-                oldItems.m_Status = UR_WIRE_IMAGE;
-
-                while( oldWires.GetCount() != 0 )
-                {
-                    ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
-                    oldItems.PushItem( picker );
-                }
-
-                SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );
-            }
-
+            BreakSegments( GetCrossHairPosition() );
             if( screen->TestDanglingEnds() )
                 m_canvas->Refresh();
         }
diff --git a/eeschema/schframe.h b/eeschema/schframe.h
index 76013d54d..7f7aa260d 100644
--- a/eeschema/schframe.h
+++ b/eeschema/schframe.h
@@ -466,6 +466,35 @@ public:
                                     bool            aWarpMouse );
 
     /**
+     * Breaks a single segment into two at the specified point
+     *
+     * @param aSegment Line segment to break
+     * @param aPoint Point at which to break the segment
+     * @param aAppend Add the changes to the previous undo state
+     * @return True if any wires or buses were broken.
+     */
+    bool BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint, bool aAppend = false );
+
+    /**
+     * Checks every wire and bus for a intersection at \a aPoint and break into two segments
+     * at \a aPoint if an intersection is found.
+     *
+     * @param aPoint Test this point for an intersection.
+     * @param aAppend Add the changes to the previous undo state
+     * @return True if any wires or buses were broken.
+     */
+    bool BreakSegments( const wxPoint& aPoint, bool aAppend = false );
+
+    /**
+     * Tests all junctions and bus entries in the schematic for intersections with wires and
+     * buses and breaks any intersections into multiple segments.
+     *
+     * @param aAppend Add the changes to the previous undo state
+     * @return True if any wires or buses were broken.
+     */
+    bool BreakSegmentsOnJunctions( bool aApped = false );
+
+    /**
      * Send a message to Pcbnew via a socket connection.
      *
      * Commands are:
@@ -908,6 +937,12 @@ private:
     SCH_JUNCTION* AddJunction( const wxPoint& aPosition, bool aPutInUndoList = false );
 
     /**
+     * Function SaveWireImage
+     * saves a copy of the current wire image in the undo list
+     */
+    void SaveWireImage();
+
+    /**
      * Function SchematicCleanUp
      * performs routine schematic cleaning including breaking wire and buses and
      * deleting identical objects superimposed on top of each other.
-- 
2.11.0

From 52ca487afd228eaf58cdb516ab707fe28eb6eb52 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Thu, 16 Nov 2017 12:39:59 -0800
Subject: [PATCH 7/9] Eeschema: Removing DC dependencies

---
 eeschema/bus-wire-junction.cpp | 16 ++++++----------
 eeschema/onleftclick.cpp       |  6 +++---
 eeschema/schedit.cpp           |  4 ++--
 eeschema/schframe.h            |  7 +++----
 4 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp
index 410f830f6..ea215c302 100644
--- a/eeschema/bus-wire-junction.cpp
+++ b/eeschema/bus-wire-junction.cpp
@@ -248,7 +248,7 @@ void SCH_EDIT_FRAME::BeginSegment( wxDC* DC, int type )
         // Terminate the command if the end point is on a pin, junction, or another wire or bus.
         if( GetScreen()->IsTerminalPoint( cursorpos, segment->GetLayer() ) )
         {
-            EndSegment( DC );
+            EndSegment();
             return;
         }
 
@@ -267,7 +267,7 @@ void SCH_EDIT_FRAME::BeginSegment( wxDC* DC, int type )
 }
 
 
-void SCH_EDIT_FRAME::EndSegment( wxDC* DC )
+void SCH_EDIT_FRAME::EndSegment()
 {
     SCH_SCREEN* screen = GetScreen();
     SCH_LINE* segment = (SCH_LINE*) screen->GetCurItem();
@@ -334,11 +334,11 @@ void SCH_EDIT_FRAME::EndSegment( wxDC* DC )
 
     // A junction could be needed to connect the end point of the last created segment.
     if( screen->IsJunctionNeeded( endpoint ) )
-        screen->Append( AddJunction( DC, endpoint ) );
+        screen->Append( AddJunction( endpoint ) );
 
     // A junction could be needed to connect the start point of the set of new created wires
     if( screen->IsJunctionNeeded( startPoint ) )
-        screen->Append( AddJunction( DC, startPoint ) );
+        screen->Append( AddJunction( startPoint ) );
 
     m_canvas->Refresh();
 
@@ -466,17 +466,13 @@ bool SCH_EDIT_FRAME::SchematicCleanUp()
 }
 
 
-SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( wxDC* aDC, const wxPoint& aPosition,
+SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition,
                                            bool aPutInUndoList )
 {
     SCH_JUNCTION* junction = new SCH_JUNCTION( aPosition );
 
     SetRepeatItem( junction );
 
-    m_canvas->CrossHairOff( aDC );     // Erase schematic cursor
-    junction->Draw( m_canvas, aDC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
-    m_canvas->CrossHairOn( aDC );      // Display schematic cursor
-
     if( aPutInUndoList )
     {
         GetScreen()->Append( junction );
@@ -488,7 +484,7 @@ SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( wxDC* aDC, const wxPoint& aPosition,
 }
 
 
-SCH_NO_CONNECT* SCH_EDIT_FRAME::AddNoConnect( wxDC* aDC, const wxPoint& aPosition )
+SCH_NO_CONNECT* SCH_EDIT_FRAME::AddNoConnect( const wxPoint& aPosition )
 {
     SCH_NO_CONNECT* no_connect = new SCH_NO_CONNECT( aPosition );
 
diff --git a/eeschema/onleftclick.cpp b/eeschema/onleftclick.cpp
index ed349001b..2cfc9e045 100644
--- a/eeschema/onleftclick.cpp
+++ b/eeschema/onleftclick.cpp
@@ -120,7 +120,7 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition )
         {
             if( GetScreen()->GetItem( gridPosition, 0, SCH_NO_CONNECT_T ) == NULL )
             {
-                SCH_NO_CONNECT*  no_connect = AddNoConnect( aDC, gridPosition );
+                SCH_NO_CONNECT*  no_connect = AddNoConnect( gridPosition );
                 SetRepeatItem( no_connect );
                 GetScreen()->SetCurItem( no_connect );
                 m_canvas->SetAutoPanRequest( true );
@@ -137,7 +137,7 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition )
         {
             if( GetScreen()->GetItem( gridPosition, 0, SCH_JUNCTION_T ) == NULL )
             {
-                SCH_JUNCTION* junction = AddJunction( aDC, gridPosition, true );
+                SCH_JUNCTION* junction = AddJunction( gridPosition );
                 SetRepeatItem( junction );
                 GetScreen()->SetCurItem( junction );
                 m_canvas->SetAutoPanRequest( true );
@@ -442,7 +442,7 @@ void SCH_EDIT_FRAME::OnLeftDClick( wxDC* aDC, const wxPoint& aPosition )
     case ID_WIRE_BUTT:
     case ID_LINE_COMMENT_BUTT:
         if( item && item->IsNew() )
-            EndSegment( aDC );
+            EndSegment();
 
         break;
     }
diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp
index 80b17570c..61d9bf6e8 100644
--- a/eeschema/schedit.cpp
+++ b/eeschema/schedit.cpp
@@ -169,7 +169,7 @@ void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
 
     case ID_POPUP_END_LINE:
         m_canvas->MoveCursorToCrossHair();
-        EndSegment( &dc );
+        EndSegment();
         break;
 
     case ID_POPUP_SCH_BEGIN_WIRE:
@@ -372,7 +372,7 @@ void SCH_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
 
     case ID_POPUP_SCH_ADD_JUNCTION:
         m_canvas->MoveCursorToCrossHair();
-        screen->SetCurItem( AddJunction( &dc, GetCrossHairPosition(), true ) );
+        screen->SetCurItem( AddJunction( GetCrossHairPosition() ) );
 
         if( screen->TestDanglingEnds() )
             m_canvas->Refresh();
diff --git a/eeschema/schframe.h b/eeschema/schframe.h
index a583ab46f..76013d54d 100644
--- a/eeschema/schframe.h
+++ b/eeschema/schframe.h
@@ -897,16 +897,15 @@ private:
     /**
      * Add no connect item to the current schematic sheet at \a aPosition.
      *
-     * @param aDC The device context to draw the no connect to.
      * @param aPosition The position in logical (drawing) units to add the no connect.
      * @return The no connect item added.
      */
-    SCH_NO_CONNECT* AddNoConnect( wxDC* aDC, const wxPoint& aPosition );
+    SCH_NO_CONNECT* AddNoConnect( const wxPoint& aPosition );
 
     /**
      * Add a new junction at \a aPosition.
      */
-    SCH_JUNCTION* AddJunction( wxDC* aDC, const wxPoint& aPosition, bool aPutInUndoList = false );
+    SCH_JUNCTION* AddJunction( const wxPoint& aPosition, bool aPutInUndoList = false );
 
     /**
      * Function SchematicCleanUp
@@ -950,7 +949,7 @@ private:
     /**
      * Terminate a bus, wire, or line creation.
      */
-    void EndSegment( wxDC* DC );
+    void EndSegment();
 
     /**
      * Erase the last segment at the current mouse position.
-- 
2.11.0

From 2e4053c1571b707f95fd4e89fc4dd5cf1048fe37 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Wed, 25 Oct 2017 15:21:42 -0700
Subject: [PATCH 6/9] Eeschema: Moving SchematicCleanup to SCH_EDIT_FRAME

SchematicCleanup function moved from SCH_SCREEN to
SCH_EDIT_FRAME to allow access to the undo stack as we
are changing the schematic.
---
 eeschema/bus-wire-junction.cpp | 53 ++++++++++++++++++++++++++++++++++--
 eeschema/class_sch_screen.h    | 14 ----------
 eeschema/hierarch.cpp          |  2 +-
 eeschema/project_rescue.cpp    |  2 +-
 eeschema/sch_screen.cpp        | 62 ------------------------------------------
 eeschema/schframe.cpp          |  2 +-
 eeschema/schframe.h            | 10 +++++++
 7 files changed, 64 insertions(+), 81 deletions(-)

diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp
index fa0175463..410f830f6 100644
--- a/eeschema/bus-wire-junction.cpp
+++ b/eeschema/bus-wire-junction.cpp
@@ -330,7 +330,7 @@ void SCH_EDIT_FRAME::EndSegment( wxDC* DC )
     screen->Append( s_wires );
 
     // Correct and remove segments that need to be merged.
-    screen->SchematicCleanUp();
+    SchematicCleanUp();
 
     // A junction could be needed to connect the end point of the last created segment.
     if( screen->IsJunctionNeeded( endpoint ) )
@@ -417,6 +417,55 @@ void SCH_EDIT_FRAME::DeleteCurrentSegment( wxDC* DC )
 }
 
 
+bool SCH_EDIT_FRAME::SchematicCleanUp()
+{
+    bool      modified = false;
+
+    for( SCH_ITEM* item = GetScreen()->GetDrawItems() ; item; item = item->Next() )
+    {
+        if( ( item->Type() != SCH_LINE_T ) && ( item->Type() != SCH_JUNCTION_T ) )
+            continue;
+
+        bool restart;
+
+        for( SCH_ITEM* testItem = item->Next(); testItem; testItem = restart ? GetScreen()->GetDrawItems() : testItem->Next() )
+        {
+            restart = false;
+
+            if( ( item->Type() == SCH_LINE_T ) && ( testItem->Type() == SCH_LINE_T ) )
+            {
+                SCH_LINE* line = (SCH_LINE*) item;
+
+                if( line->MergeOverlap( (SCH_LINE*) testItem ) )
+                {
+                    // Keep the current flags, because the deleted segment can be flagged.
+                    item->SetFlags( testItem->GetFlags() );
+                    DeleteItem( testItem );
+                    restart = true;
+                    modified = true;
+                }
+            }
+            else if ( ( ( item->Type() == SCH_JUNCTION_T )
+                      && ( testItem->Type() == SCH_JUNCTION_T ) ) && ( testItem != item ) )
+            {
+                if ( testItem->HitTest( item->GetPosition() ) )
+                {
+                    // Keep the current flags, because the deleted segment can be flagged.
+                    item->SetFlags( testItem->GetFlags() );
+                    DeleteItem( testItem );
+                    restart = true;
+                    modified = true;
+                }
+            }
+        }
+    }
+
+    GetScreen()->TestDanglingEnds();
+
+    return modified;
+}
+
+
 SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( wxDC* aDC, const wxPoint& aPosition,
                                            bool aPutInUndoList )
 {
@@ -445,7 +494,7 @@ SCH_NO_CONNECT* SCH_EDIT_FRAME::AddNoConnect( wxDC* aDC, const wxPoint& aPositio
 
     SetRepeatItem( no_connect );
     GetScreen()->Append( no_connect );
-    GetScreen()->SchematicCleanUp();
+    SchematicCleanUp();
     OnModify();
     m_canvas->Refresh();
     SaveCopyInUndoList( no_connect, UR_NEW );
diff --git a/eeschema/class_sch_screen.h b/eeschema/class_sch_screen.h
index 92c564d83..7bd647ff5 100644
--- a/eeschema/class_sch_screen.h
+++ b/eeschema/class_sch_screen.h
@@ -262,16 +262,7 @@ public:
     bool CheckIfOnDrawList( SCH_ITEM* st );
 
     /**
-     * Perform routine schematic cleaning including breaking wire and buses and
-     * deleting identical objects superimposed on top of each other.
-     *
-     * @return True if any schematic clean up was performed.
-     */
-    bool SchematicCleanUp();
-
-    /**
      * Test all of the connectable objects in the schematic for unused connection points.
-     *
      * @return True if any connection state changes were made.
      */
     bool TestDanglingEnds();
@@ -542,11 +533,6 @@ public:
     void ClearAnnotation();
 
     /**
-     * Merge and break wire segments in the entire schematic hierarchy.
-     */
-    void SchematicCleanUp();
-
-    /**
      * Test all sheet and component objects in the schematic for duplicate time stamps
      * and replaces them as necessary.
      * Time stamps must be unique in order for complex hierarchies know which components go
diff --git a/eeschema/hierarch.cpp b/eeschema/hierarch.cpp
index d66aa4ea9..c41c82164 100644
--- a/eeschema/hierarch.cpp
+++ b/eeschema/hierarch.cpp
@@ -299,7 +299,7 @@ void SCH_EDIT_FRAME::DisplayCurrentSheet()
         screen->m_FirstRedraw = false;
         SetCrossHairPosition( GetScrollCenterPosition() );
         m_canvas->MoveCursorToCrossHair();
-        screen->SchematicCleanUp();
+        SchematicCleanUp();
     }
     else
     {
diff --git a/eeschema/project_rescue.cpp b/eeschema/project_rescue.cpp
index ed2485d58..0af8db517 100644
--- a/eeschema/project_rescue.cpp
+++ b/eeschema/project_rescue.cpp
@@ -556,7 +556,7 @@ bool SCH_EDIT_FRAME::rescueProject( RESCUER& aRescuer, bool aRunningOnDemand )
         viewer->ReCreateListLib();
 
     // Clean up wire ends
-    GetScreen()->SchematicCleanUp();
+    SchematicCleanUp();
     m_canvas->Refresh( true );
     OnModify();
 
diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp
index edc90bc4b..11837dc80 100644
--- a/eeschema/sch_screen.cpp
+++ b/eeschema/sch_screen.cpp
@@ -435,55 +435,6 @@ bool SCH_SCREEN::IsTerminalPoint( const wxPoint& aPosition, int aLayer )
 }
 
 
-bool SCH_SCREEN::SchematicCleanUp()
-{
-    bool      modified = false;
-
-    for( SCH_ITEM* item = m_drawList.begin() ; item; item = item->Next() )
-    {
-        if( ( item->Type() != SCH_LINE_T ) && ( item->Type() != SCH_JUNCTION_T ) )
-            continue;
-
-        bool restart;
-
-        for( SCH_ITEM* testItem = item->Next(); testItem; testItem = restart ? m_drawList.begin() : testItem->Next() )
-        {
-            restart = false;
-
-            if( ( item->Type() == SCH_LINE_T ) && ( testItem->Type() == SCH_LINE_T ) )
-            {
-                SCH_LINE* line = (SCH_LINE*) item;
-
-                if( line->MergeOverlap( (SCH_LINE*) testItem ) )
-                {
-                    // Keep the current flags, because the deleted segment can be flagged.
-                    item->SetFlags( testItem->GetFlags() );
-                    DeleteItem( testItem );
-                    restart = true;
-                    modified = true;
-                }
-            }
-            else if ( ( ( item->Type() == SCH_JUNCTION_T )
-                      && ( testItem->Type() == SCH_JUNCTION_T ) ) && ( testItem != item ) )
-            {
-                if ( testItem->HitTest( item->GetPosition() ) )
-                {
-                    // Keep the current flags, because the deleted segment can be flagged.
-                    item->SetFlags( testItem->GetFlags() );
-                    DeleteItem( testItem );
-                    restart = true;
-                    modified = true;
-                }
-            }
-        }
-    }
-
-    TestDanglingEnds();
-
-    return modified;
-}
-
-
 void SCH_SCREEN::UpdateSymbolLinks( bool aForce )
 {
     // Initialize or reinitialize the pointer to the LIB_PART for each component
@@ -1375,19 +1326,6 @@ void SCH_SCREENS::ClearAnnotation()
         m_screens[i]->ClearAnnotation( NULL );
 }
 
-
-void SCH_SCREENS::SchematicCleanUp()
-{
-    for( size_t i = 0;  i < m_screens.size();  i++ )
-    {
-        // if wire list has changed, delete the undo/redo list to avoid
-        // pointer problems with deleted data.
-        if( m_screens[i]->SchematicCleanUp() )
-            m_screens[i]->ClearUndoRedoList();
-    }
-}
-
-
 int SCH_SCREENS::ReplaceDuplicateTimeStamps()
 {
     EDA_ITEMS items;
diff --git a/eeschema/schframe.cpp b/eeschema/schframe.cpp
index c6808858a..ea0043ad9 100644
--- a/eeschema/schframe.cpp
+++ b/eeschema/schframe.cpp
@@ -1249,7 +1249,7 @@ void SCH_EDIT_FRAME::OnOpenLibraryEditor( wxCommandEvent& event )
         libeditFrame->LoadComponentAndSelectLib( id );
     }
 
-    GetScreen()->SchematicCleanUp();
+    SchematicCleanUp();
     m_canvas->Refresh();
 }
 
diff --git a/eeschema/schframe.h b/eeschema/schframe.h
index 1e3e46dd8..a583ab46f 100644
--- a/eeschema/schframe.h
+++ b/eeschema/schframe.h
@@ -909,6 +909,15 @@ private:
     SCH_JUNCTION* AddJunction( wxDC* aDC, const wxPoint& aPosition, bool aPutInUndoList = false );
 
     /**
+     * Function SchematicCleanUp
+     * performs routine schematic cleaning including breaking wire and buses and
+     * deleting identical objects superimposed on top of each other.
+     *
+     * @return True if any schematic clean up was performed.
+     */
+    bool SchematicCleanUp();
+
+    /**
      * Start moving \a aItem using the mouse.
      *
      * @param aItem A pointer to an SCH_ITEM to move.
@@ -1072,6 +1081,7 @@ public:
      * Remove \a aItem from the current screen and saves it in the undo list.
      *
      * @param aItem The item to remove from the current screen.
+     * @param aAppend True if we are updating a previous Undo state
      */
     void DeleteItem( SCH_ITEM* aItem, bool aAppend = false );
 
-- 
2.11.0

From f55b91d04f92a68c8f37a1a3253699b9886cbad3 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Tue, 31 Oct 2017 12:33:00 -0700
Subject: [PATCH 5/9] Eeschema: Remove zero-length wires in RemoveBacktracks

RemoveBacktracks call removes zero-length wires as well
---
 eeschema/bus-wire-junction.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp
index 528d09d68..fa0175463 100644
--- a/eeschema/bus-wire-junction.cpp
+++ b/eeschema/bus-wire-junction.cpp
@@ -74,6 +74,12 @@ static void RemoveBacktracks( DLIST<SCH_ITEM>& aWires )
         SCH_LINE *line = static_cast<SCH_LINE*>( p );
         p = line->Next();
 
+        if( line->IsNull() )
+        {
+            delete s_wires.Remove( line );
+            continue;
+        }
+
         if( !last_lines.empty() )
         {
             SCH_LINE* last_line = last_lines[last_lines.size() - 1];
-- 
2.11.0

From e246b18a823d58cfd56529083aaf371f16b48786 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Fri, 20 Oct 2017 09:31:02 -0700
Subject: [PATCH 4/9] Eeschema: don't cleanup unseen schematics

Changes to the schematic shouldn't be made
where the user isn't looking.  Removing the
cleanup in ERC and netlisting prevents
unintended consequences

CHANGE: ERC and Netlist calls do not modify schematic
---
 eeschema/dialogs/dialog_erc.cpp | 9 ---------
 eeschema/netlist.cpp            | 4 ----
 2 files changed, 13 deletions(-)

diff --git a/eeschema/dialogs/dialog_erc.cpp b/eeschema/dialogs/dialog_erc.cpp
index 8e811a40d..47c9d686b 100644
--- a/eeschema/dialogs/dialog_erc.cpp
+++ b/eeschema/dialogs/dialog_erc.cpp
@@ -478,15 +478,6 @@ void DIALOG_ERC::TestErc( wxArrayString* aMessagesList )
     // Erase all previous DRC markers.
     screens.DeleteAllMarkers( MARKER_BASE::MARKER_ERC );
 
-    for( SCH_SCREEN* screen = screens.GetFirst(); screen != NULL; screen = screens.GetNext() )
-    {
-        /* Ff wire list has changed, delete Undo Redo list to avoid pointers on deleted
-         * data problems.
-         */
-        if( screen->SchematicCleanUp() )
-            screen->ClearUndoRedoList();
-    }
-
     /* Test duplicate sheet names inside a given sheet, one cannot have sheets with
      * duplicate names (file names can be duplicated).
      */
diff --git a/eeschema/netlist.cpp b/eeschema/netlist.cpp
index 6252f27c4..ef5d0a5f5 100644
--- a/eeschema/netlist.cpp
+++ b/eeschema/netlist.cpp
@@ -83,9 +83,6 @@ bool SCH_EDIT_FRAME::prepareForNetlist()
             return false;
     }
 
-    // Cleanup the entire hierarchy
-    schematic.SchematicCleanUp();
-
     return true;
 }
 
@@ -123,7 +120,6 @@ bool SCH_EDIT_FRAME::CreateNetlist( int aFormat, const wxString& aFullFileName,
         schematic.UpdateSymbolLinks();
         SCH_SHEET_LIST sheets( g_RootSheet );
         sheets.AnnotatePowerSymbols();
-        schematic.SchematicCleanUp();
     }
 
     std::unique_ptr<NETLIST_OBJECT_LIST> connectedItemsList( BuildNetListBase() );
-- 
2.11.0

From 80edf017f3714dd27d3a2ce5d9c69acadd91e74c Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Mon, 16 Oct 2017 14:38:57 -0700
Subject: [PATCH 3/9] Eeschema: Add two utility functions to sch_line

Add IsSameQuandrant and IsParallel functions
---
 eeschema/sch_line.cpp | 36 ++++++++++++++++++++++++++++++++++++
 eeschema/sch_line.h   | 12 ++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/eeschema/sch_line.cpp b/eeschema/sch_line.cpp
index 4e3246c2a..7a05fee98 100644
--- a/eeschema/sch_line.cpp
+++ b/eeschema/sch_line.cpp
@@ -352,6 +352,42 @@ void SCH_LINE::Rotate( wxPoint aPosition )
 }
 
 
+bool SCH_LINE::IsSameQuadrant( SCH_LINE* aLine, const wxPoint& aPosition )
+{
+    wxPoint first;
+    wxPoint second;
+
+    if( m_start == aPosition )
+        first = m_end - aPosition;
+    else if( m_end == aPosition )
+        first = m_start - aPosition;
+    else
+        return false;
+
+    if( aLine->m_start == aPosition )
+        second = aLine->m_end - aPosition;
+    else if( aLine->m_end == aPosition )
+        second = aLine->m_start - aPosition;
+    else
+        return false;
+
+    return (std::signbit( first.x ) == std::signbit( second.x ) &&
+            std::signbit( first.y ) == std::signbit( second.y ) );
+}
+
+
+bool SCH_LINE::IsParallel( SCH_LINE* aLine )
+{
+    wxCHECK_MSG( aLine != NULL && aLine->Type() == SCH_LINE_T, false,
+                 wxT( "Cannot test line segment for overlap." ) );
+
+    wxPoint firstSeg   = m_end - m_start;
+    wxPoint secondSeg = aLine->m_end - aLine->m_start;
+
+    // Use long long here to avoid overflow in calculations
+    return !( (long long) firstSeg.x * secondSeg.y - (long long) firstSeg.y * secondSeg.x );
+}
+
 bool SCH_LINE::MergeOverlap( SCH_LINE* aLine )
 {
     auto less = []( const wxPoint& lhs, const wxPoint& rhs ) -> bool
diff --git a/eeschema/sch_line.h b/eeschema/sch_line.h
index ac84a2a6c..6b7594c31 100644
--- a/eeschema/sch_line.h
+++ b/eeschema/sch_line.h
@@ -142,6 +142,18 @@ public:
      */
     bool MergeOverlap( SCH_LINE* aLine );
 
+    /**
+     * Check if two lines are in the same quadrant as each other, using a reference point as
+     * the origin
+     *
+     * @param aLine - Line to compare
+     * @param aPosition - Point to reference against lines
+     * @return true if lines are mostly in different quadrants of aPosition, false otherwise
+     */
+    bool IsSameQuadrant( SCH_LINE* aLine, const wxPoint& aPosition );
+
+    bool IsParallel( SCH_LINE* aLine );
+
     void GetEndPoints( std::vector<DANGLING_END_ITEM>& aItemList ) override;
 
     bool IsDanglingStateChanged( std::vector< DANGLING_END_ITEM >& aItemList ) override;
-- 
2.11.0

From 20aed6e1a750f59405e2b00cd84657b25844f063 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Thu, 12 Oct 2017 16:41:25 -0700
Subject: [PATCH 2/9] Eeschema: Unify delete operations

DeleteItemsInList now shares the code for DeleteItem.
SCH_FRAME::DeleteItem removes junctions that are no longer needed.
---
 eeschema/block.cpp                     |  4 +--
 eeschema/operations_on_items_lists.cpp | 56 ++++++++++++++++++++++------------
 eeschema/schedit.cpp                   |  2 +-
 eeschema/schframe.h                    | 11 ++++++-
 4 files changed, 49 insertions(+), 24 deletions(-)

diff --git a/eeschema/block.cpp b/eeschema/block.cpp
index 2d2af403f..f0a52f795 100644
--- a/eeschema/block.cpp
+++ b/eeschema/block.cpp
@@ -272,7 +272,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
 
             if( block->GetCount() )
             {
-                DeleteItemsInList( m_canvas, block->GetItems() );
+                DeleteItemsInList( block->GetItems() );
                 OnModify();
             }
             block->ClearItemsList();
@@ -303,7 +303,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 wxPoint move_vector = -GetScreen()->m_BlockLocate.GetLastCursorPosition();
                 copyBlockItems( block->GetItems() );
                 MoveItemsInList( m_blockItems.GetItems(), move_vector );
-                DeleteItemsInList( m_canvas, block->GetItems() );
+                DeleteItemsInList( block->GetItems() );
                 OnModify();
             }
 
diff --git a/eeschema/operations_on_items_lists.cpp b/eeschema/operations_on_items_lists.cpp
index a470c5d6f..ad8313d38 100644
--- a/eeschema/operations_on_items_lists.cpp
+++ b/eeschema/operations_on_items_lists.cpp
@@ -115,10 +115,8 @@ void MoveItemsInList( PICKED_ITEMS_LIST& aItemsList, const wxPoint& aMoveVector
 }
 
 
-void DeleteItemsInList( EDA_DRAW_PANEL* panel, PICKED_ITEMS_LIST& aItemsList )
+void SCH_EDIT_FRAME::DeleteItemsInList( PICKED_ITEMS_LIST& aItemsList, bool aAppend )
 {
-    SCH_SCREEN*        screen = (SCH_SCREEN*) panel->GetScreen();
-    SCH_EDIT_FRAME*    frame  = (SCH_EDIT_FRAME*) panel->GetParent();
     PICKED_ITEMS_LIST  itemsList;
 
     for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
@@ -126,46 +124,64 @@ void DeleteItemsInList( EDA_DRAW_PANEL* panel, PICKED_ITEMS_LIST& aItemsList )
         SCH_ITEM* item = (SCH_ITEM*) aItemsList.GetPickedItem( ii );
         ITEM_PICKER itemWrapper( item, UR_DELETED );
 
-        if( item->Type() == SCH_SHEET_PIN_T )
-        {
-            /* this item is depending on a sheet, and is not in global list */
-            wxMessageBox( wxT( "DeleteItemsInList() err: unexpected SCH_SHEET_PIN_T" ) );
-        }
-        else
-        {
-            screen->Remove( item );
+        if( item->GetFlags() & STRUCT_DELETED )
+            continue;
 
-            /* Unlink the structure */
-            itemsList.PushItem( itemWrapper );
-        }
+        DeleteItem( item, aAppend );
+        aAppend = true;
     }
 
-    frame->SaveCopyInUndoList( itemsList, UR_DELETED );
+    GetScreen()->ClearDrawingState();
 }
 
 
-void SCH_EDIT_FRAME::DeleteItem( SCH_ITEM* aItem )
+void SCH_EDIT_FRAME::DeleteItem( SCH_ITEM* aItem, bool aAppend )
 {
     wxCHECK_RET( aItem != NULL, wxT( "Cannot delete invalid item." ) );
+    wxCHECK_RET( !( aItem->GetFlags() & STRUCT_DELETED ),
+                 wxT( "Cannot delete item that is already deleted." ) );
 
     // Here, aItem is not null.
-
     SCH_SCREEN* screen = GetScreen();
 
     if( aItem->Type() == SCH_SHEET_PIN_T )
     {
-        // This iten is attached to a node, and is not accessible by the global list directly.
+        // This item is attached to a node, and is not accessible by the global list directly.
         SCH_SHEET* sheet = (SCH_SHEET*) aItem->GetParent();
         wxCHECK_RET( (sheet != NULL) && (sheet->Type() == SCH_SHEET_T),
                      wxT( "Sheet label has invalid parent item." ) );
-        SaveCopyInUndoList( (SCH_ITEM*) sheet, UR_CHANGED );
+        SaveCopyInUndoList( (SCH_ITEM*) sheet, UR_CHANGED, aAppend );
         sheet->RemovePin( (SCH_SHEET_PIN*) aItem );
         m_canvas->RefreshDrawingRect( sheet->GetBoundingBox() );
     }
     else
     {
+        PICKED_ITEMS_LIST itemsList;
+        ITEM_PICKER picker( aItem, UR_DELETED );
+
+        aItem->SetFlags( STRUCT_DELETED );
+        itemsList.PushItem( picker );
         screen->Remove( aItem );
-        SaveCopyInUndoList( aItem, UR_DELETED );
+
+        if( aItem->IsConnectable() && aItem->Type() != SCH_JUNCTION_T )
+        {
+            std::vector< wxPoint > pts;
+            aItem->GetConnectionPoints( pts );
+            for( auto point : pts )
+            {
+                SCH_ITEM* junction;
+                if( !screen->IsJunctionNeeded( point )
+                        && ( junction = screen->GetItem( point, 0, SCH_JUNCTION_T ) ) )
+                {
+                    ITEM_PICKER picker_juction( junction, UR_DELETED );
+                    junction->SetFlags( STRUCT_DELETED );
+                    itemsList.PushItem( picker_juction );
+                    screen->Remove( junction );
+                }
+            }
+        }
+
+        SaveCopyInUndoList( itemsList, UR_DELETED, aAppend );
         m_canvas->RefreshDrawingRect( aItem->GetBoundingBox() );
     }
 }
diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp
index 102f1fcbe..80b17570c 100644
--- a/eeschema/schedit.cpp
+++ b/eeschema/schedit.cpp
@@ -648,7 +648,7 @@ void SCH_EDIT_FRAME::DeleteConnection( bool aFullConnection )
 
     if( screen->GetConnection( pos, pickList, aFullConnection ) != 0 )
     {
-        DeleteItemsInList( m_canvas, pickList );
+        DeleteItemsInList( pickList );
         OnModify();
     }
 }
diff --git a/eeschema/schframe.h b/eeschema/schframe.h
index bd0f08a06..1e3e46dd8 100644
--- a/eeschema/schframe.h
+++ b/eeschema/schframe.h
@@ -1073,7 +1073,16 @@ public:
      *
      * @param aItem The item to remove from the current screen.
      */
-    void DeleteItem( SCH_ITEM* aItem );
+    void DeleteItem( SCH_ITEM* aItem, bool aAppend = false );
+
+    /**
+     * Removes all items (and unused junctions that connect to them) and saves
+     * each in the undo list
+     *
+     * @param aItemsList The list of items to delete
+     * @param aAppend True if we are updating a previous commit
+     */
+    void DeleteItemsInList( PICKED_ITEMS_LIST& aItemsList, bool aAppend = false );
 
     int GetLabelIncrement() const { return m_repeatLabelDelta; }
 
-- 
2.11.0

From 40bd0427bdff60c88a7ab280e02b7fb43032e787 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Thu, 16 Nov 2017 12:36:09 -0800
Subject: [PATCH 1/9] Eeschema: Add 'append' option to undo

Allows eeschema to add elements to previous undo commits,
permitting stacking of changes through multiple calls
---
 eeschema/block.cpp               |  8 +++----
 eeschema/edit_bitmap.cpp         |  2 +-
 eeschema/schematic_undo_redo.cpp | 47 +++++++++++++++++++++++++++++++++++-----
 eeschema/schframe.h              |  4 ++++
 4 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/eeschema/block.cpp b/eeschema/block.cpp
index b6d0d7824..2d2af403f 100644
--- a/eeschema/block.cpp
+++ b/eeschema/block.cpp
@@ -127,7 +127,7 @@ void SCH_EDIT_FRAME::HandleBlockPlace( wxDC* DC )
         if( m_canvas->IsMouseCaptured() )
             m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
 
-        SaveCopyInUndoList( block->GetItems(), UR_MOVED, block->GetMoveVector() );
+        SaveCopyInUndoList( block->GetItems(), UR_MOVED, false, block->GetMoveVector() );
         MoveItemsInList( block->GetItems(), block->GetMoveVector() );
         block->ClearItemsList();
         break;
@@ -216,7 +216,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 wxPoint rotationPoint = block->Centre();
                 rotationPoint = GetNearestGridPosition( rotationPoint );
                 SetCrossHairPosition( rotationPoint );
-                SaveCopyInUndoList( block->GetItems(), UR_ROTATED, rotationPoint );
+                SaveCopyInUndoList( block->GetItems(), UR_ROTATED, false, rotationPoint );
                 RotateListOfItems( block->GetItems(), rotationPoint );
                 OnModify();
             }
@@ -330,7 +330,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 wxPoint mirrorPoint = block->Centre();
                 mirrorPoint = GetNearestGridPosition( mirrorPoint );
                 SetCrossHairPosition( mirrorPoint );
-                SaveCopyInUndoList( block->GetItems(), UR_MIRRORED_X, mirrorPoint );
+                SaveCopyInUndoList( block->GetItems(), UR_MIRRORED_X, false, mirrorPoint );
                 MirrorX( block->GetItems(), mirrorPoint );
                 OnModify();
             }
@@ -349,7 +349,7 @@ bool SCH_EDIT_FRAME::HandleBlockEnd( wxDC* aDC )
                 wxPoint mirrorPoint = block->Centre();
                 mirrorPoint = GetNearestGridPosition( mirrorPoint );
                 SetCrossHairPosition( mirrorPoint );
-                SaveCopyInUndoList( block->GetItems(), UR_MIRRORED_Y, mirrorPoint );
+                SaveCopyInUndoList( block->GetItems(), UR_MIRRORED_Y, false, mirrorPoint );
                 MirrorY( block->GetItems(), mirrorPoint );
                 OnModify();
             }
diff --git a/eeschema/edit_bitmap.cpp b/eeschema/edit_bitmap.cpp
index ad2a2b93a..d49efcda8 100644
--- a/eeschema/edit_bitmap.cpp
+++ b/eeschema/edit_bitmap.cpp
@@ -161,7 +161,7 @@ void SCH_EDIT_FRAME::MoveImage( SCH_BITMAP* aImageItem, wxDC* aDC )
 void SCH_EDIT_FRAME::RotateImage( SCH_BITMAP* aItem )
 {
     if( aItem->GetFlags( ) == 0 )
-        SaveCopyInUndoList( aItem, UR_ROTATED, aItem->GetPosition() );
+        SaveCopyInUndoList( aItem, UR_ROTATED, false, aItem->GetPosition() );
 
     aItem->Rotate( aItem->GetPosition() );
     OnModify();
diff --git a/eeschema/schematic_undo_redo.cpp b/eeschema/schematic_undo_redo.cpp
index 09a83a6bc..75c1731eb 100644
--- a/eeschema/schematic_undo_redo.cpp
+++ b/eeschema/schematic_undo_redo.cpp
@@ -108,8 +108,11 @@
 
 void SCH_EDIT_FRAME::SaveCopyInUndoList( SCH_ITEM*      aItem,
                                          UNDO_REDO_T    aCommandType,
+                                         bool           aAppend,
                                          const wxPoint& aTransformPoint )
 {
+     PICKED_ITEMS_LIST* commandToUndo = NULL;
+
     /* Does not save a null item or a UR_WIRE_IMAGE command type.  UR_WIRE_IMAGE commands
      * are handled by the overloaded version of SaveCopyInUndoList that takes a reference
      * to a PICKED_ITEMS_LIST.
@@ -117,8 +120,14 @@ void SCH_EDIT_FRAME::SaveCopyInUndoList( SCH_ITEM*      aItem,
     if( aItem == NULL || aCommandType == UR_WIRE_IMAGE )
         return;
 
-    PICKED_ITEMS_LIST* commandToUndo = new PICKED_ITEMS_LIST();
-    commandToUndo->m_TransformPoint = aTransformPoint;
+    if( aAppend )
+        commandToUndo = GetScreen()->PopCommandFromUndoList();
+
+    if( !commandToUndo )
+    {
+        commandToUndo = new PICKED_ITEMS_LIST();
+        commandToUndo->m_TransformPoint = aTransformPoint;
+    }
 
     ITEM_PICKER itemWrapper( aItem, aCommandType );
     itemWrapper.SetFlags( aItem->GetFlags() );
@@ -160,15 +169,41 @@ void SCH_EDIT_FRAME::SaveCopyInUndoList( SCH_ITEM*      aItem,
 
 void SCH_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsList,
                                          UNDO_REDO_T        aTypeCommand,
+                                         bool               aAppend,
                                          const wxPoint&     aTransformPoint )
 {
-    PICKED_ITEMS_LIST* commandToUndo = new PICKED_ITEMS_LIST();
+    PICKED_ITEMS_LIST* commandToUndo = NULL;
+
+    if( !aItemsList.GetCount() )
+        return;
+
+    // Can't append a WIRE IMAGE, so fail to a new undo point
+    if( aAppend && ( aTypeCommand != UR_WIRE_IMAGE ) )
+    {
+        commandToUndo = GetScreen()->PopCommandFromUndoList();
+        if( commandToUndo && commandToUndo->m_Status == UR_WIRE_IMAGE )
+        {
+            GetScreen()->PushCommandToUndoList( commandToUndo );
+            commandToUndo = NULL;
+        }
+    }
 
-    commandToUndo->m_TransformPoint = aTransformPoint;
-    commandToUndo->m_Status = aTypeCommand;
+    if( !commandToUndo )
+    {
+        commandToUndo = new PICKED_ITEMS_LIST();
+        commandToUndo->m_TransformPoint = aTransformPoint;
+        commandToUndo->m_Status = aTypeCommand;
+    }
 
     // Copy picker list:
-    commandToUndo->CopyList( aItemsList );
+    if( !commandToUndo->GetCount() )
+        commandToUndo->CopyList( aItemsList );
+    else
+    {
+        // Unless we are appending, in which case, get the picker items
+        for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
+            commandToUndo->PushItem( aItemsList.GetItemWrapper( ii) );
+    }
 
     // Verify list, and creates data if needed
     for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
diff --git a/eeschema/schframe.h b/eeschema/schframe.h
index e80e9bbeb..bd0f08a06 100644
--- a/eeschema/schframe.h
+++ b/eeschema/schframe.h
@@ -1161,11 +1161,13 @@ public:
      *
      * @param aItemToCopy = the schematic item modified by the command to undo
      * @param aTypeCommand = command type (see enum UNDO_REDO_T)
+     * @param aAppend = add the item to the previous undo list
      * @param aTransformPoint = the reference point of the transformation,
      *                          for commands like move
      */
     void SaveCopyInUndoList( SCH_ITEM* aItemToCopy,
                              UNDO_REDO_T aTypeCommand,
+                             bool aAppend = false,
                              const wxPoint& aTransformPoint = wxPoint( 0, 0 ) );
 
     /**
@@ -1173,11 +1175,13 @@ public:
      *
      * @param aItemsList = the list of items modified by the command to undo
      * @param aTypeCommand = command type (see enum UNDO_REDO_T)
+     * @param aAppend = add the item to the previous undo list
      * @param aTransformPoint = the reference point of the transformation,
      *                          for commands like move
      */
     void SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsList,
                              UNDO_REDO_T aTypeCommand,
+                             bool aAppend = false,
                              const wxPoint& aTransformPoint = wxPoint( 0, 0 ) );
 
 private:
-- 
2.11.0


Follow ups

References