summaryrefslogtreecommitdiff
path: root/eeschema/bus-wire-junction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'eeschema/bus-wire-junction.cpp')
-rw-r--r--eeschema/bus-wire-junction.cpp505
1 files changed, 505 insertions, 0 deletions
diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp
new file mode 100644
index 0000000..18e3bd1
--- /dev/null
+++ b/eeschema/bus-wire-junction.cpp
@@ -0,0 +1,505 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2004 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
+ * Copyright (C) 2004-2011 KiCad Developers, see change_log.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file bus-wire-junction.cpp
+ * @brief Code for editing buses, wires, and junctions.
+ */
+
+#include <fctsys.h>
+#include <gr_basic.h>
+#include <class_drawpanel.h>
+#include <schframe.h>
+
+#include <lib_draw_item.h>
+#include <lib_pin.h>
+#include <general.h>
+#include <sch_bus_entry.h>
+#include <sch_junction.h>
+#include <sch_line.h>
+#include <sch_no_connect.h>
+#include <sch_text.h>
+#include <sch_component.h>
+#include <sch_sheet.h>
+
+
+static void AbortCreateNewLine( EDA_DRAW_PANEL* aPanel, wxDC* aDC );
+static void ComputeBreakPoint( SCH_LINE* segment, const wxPoint& new_pos );
+
+static DLIST< SCH_ITEM > s_wires; // when creating a new set of wires,
+ // stores here the new wires.
+
+
+/**
+ * In a contiguous list of wires, remove wires that backtrack over the previous
+ * wire. Example:
+ *
+ * Wire is added:
+ * ---------------------------------------->
+ *
+ * A second wire backtracks over it:
+ * -------------------<====================>
+ *
+ * RemoveBacktracks is called:
+ * ------------------->
+ */
+static void RemoveBacktracks( DLIST<SCH_ITEM>& aWires )
+{
+ SCH_LINE* last_line = NULL;
+
+ EDA_ITEM* first = aWires.GetFirst();
+ for( EDA_ITEM* p = first; p; )
+ {
+ SCH_LINE *line = dynamic_cast<SCH_LINE*>( p );
+ if( !line )
+ {
+ wxFAIL_MSG( "RemoveBacktracks() requires SCH_LINE items" );
+ break;
+ }
+ p = line->Next();
+
+ if( last_line )
+ {
+ wxASSERT_MSG( last_line->GetEndPoint() == line->GetStartPoint(),
+ "RemoveBacktracks() requires contiguous lines" );
+ if( IsPointOnSegment( last_line->GetStartPoint(), line->GetStartPoint(),
+ line->GetEndPoint() ) )
+ {
+ last_line->SetEndPoint( line->GetEndPoint() );
+ delete s_wires.Remove( line );
+ }
+ else
+ last_line = line;
+ }
+ else
+ last_line = line;
+ }
+}
+
+
+/**
+ * Mouse capture callback for drawing line segments.
+ */
+static void DrawSegment( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition,
+ bool aErase )
+{
+ SCH_LINE* segment;
+
+ if( s_wires.GetCount() == 0 )
+ return;
+
+ segment = (SCH_LINE*) s_wires.begin();
+ EDA_COLOR_T color = GetLayerColor( segment->GetLayer() );
+ ColorChangeHighlightFlag( &color, !(color & HIGHLIGHT_FLAG) );
+
+ if( aErase )
+ {
+ while( segment )
+ {
+ if( !segment->IsNull() ) // Redraw if segment length != 0
+ segment->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, color );
+
+ segment = segment->Next();
+ }
+ }
+
+ SCH_EDIT_FRAME* frame = (SCH_EDIT_FRAME*) aPanel->GetParent();
+
+ wxPoint endpos = frame->GetCrossHairPosition();
+
+ if( frame->GetForceHVLines() ) /* Coerce the line to vertical or horizontal one: */
+ ComputeBreakPoint( (SCH_LINE*) s_wires.GetLast()->Back(), endpos );
+ else
+ ( (SCH_LINE*) s_wires.GetLast() )->SetEndPoint( endpos );
+
+ segment = (SCH_LINE*) s_wires.begin();
+
+ while( segment )
+ {
+ if( !segment->IsNull() ) // Redraw if segment length != 0
+ segment->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, color );
+
+ segment = segment->Next();
+ }
+}
+
+
+void SCH_EDIT_FRAME::BeginSegment( wxDC* DC, int type )
+{
+ SCH_LINE* segment;
+ SCH_LINE* nextSegment;
+ wxPoint cursorpos = GetCrossHairPosition();
+
+ // We should know if a segment is currently in progress
+ segment = (SCH_LINE*) GetScreen()->GetCurItem();
+ if( segment ) // a current item exists, but not necessary a currently edited item
+ {
+ if( !segment->GetFlags() || ( segment->Type() != SCH_LINE_T ) )
+ {
+ if( segment->GetFlags() )
+ {
+ wxLogDebug( wxT( "BeginSegment: item->GetFlags()== %X" ),
+ segment->GetFlags() );
+ }
+ // no wire, bus or graphic line in progress
+ segment = NULL;
+ }
+ }
+
+ if( !segment ) // first point : Create the first wire or bus segment
+ {
+ switch( type )
+ {
+ default:
+ segment = new SCH_LINE( cursorpos, LAYER_NOTES );
+ break;
+
+ case LAYER_WIRE:
+ segment = new SCH_LINE( cursorpos, LAYER_WIRE );
+
+ /* A junction will be created later, when we'll know the
+ * segment end position, and if the junction is really needed */
+ break;
+
+ case LAYER_BUS:
+ segment = new SCH_LINE( cursorpos, LAYER_BUS );
+ break;
+ }
+
+ segment->SetFlags( IS_NEW );
+ s_wires.PushBack( segment );
+ GetScreen()->SetCurItem( segment );
+
+ // We need 2 segments to go from a given start pin to an end point when the horizontal
+ // and vertical lines only switch is on.
+ if( GetForceHVLines() )
+ {
+ nextSegment = new SCH_LINE( *segment );
+ nextSegment->SetFlags( IS_NEW );
+ s_wires.PushBack( nextSegment );
+ GetScreen()->SetCurItem( nextSegment );
+ }
+
+ m_canvas->SetMouseCapture( DrawSegment, AbortCreateNewLine );
+ SetRepeatItem( NULL );
+ }
+ else // A segment is in progress: terminates the current segment and add a new segment.
+ {
+ SCH_LINE* prevSegment = segment->Back();
+
+ // Be aware prevSegment can be null when the horizontal and vertical lines only switch is off
+ // when we create the first segment.
+
+ if( !GetForceHVLines() )
+ {
+ // If only one segment is needed and it has a zero length, do not create a new one.
+ if( segment->IsNull() )
+ return;
+ }
+ else
+ {
+ wxCHECK_RET( prevSegment != NULL, wxT( "Failed to create second line segment." ) );
+
+ // If two segments are required and they both have zero length, do not
+ // create a new one.
+ if( prevSegment && prevSegment->IsNull() && segment->IsNull() )
+ return;
+ }
+
+ m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
+
+ // 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 );
+ return;
+ }
+
+ // Create a new segment, and chain it after the current new segment.
+ nextSegment = new SCH_LINE( *segment );
+ nextSegment->SetStartPoint( cursorpos );
+ s_wires.PushBack( nextSegment );
+
+ segment->SetEndPoint( cursorpos );
+ segment->ClearFlags( IS_NEW );
+ segment->SetFlags( SELECTED );
+ nextSegment->SetFlags( IS_NEW );
+ GetScreen()->SetCurItem( nextSegment );
+ m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
+ }
+}
+
+
+void SCH_EDIT_FRAME::EndSegment( wxDC* DC )
+{
+ SCH_SCREEN* screen = GetScreen();
+ SCH_LINE* segment = (SCH_LINE*) screen->GetCurItem();
+
+ 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 );
+ }
+
+ 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() );
+
+ 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();
+
+ // 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;
+
+ while( oldWires.GetCount() != 0 )
+ {
+ ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
+ oldItems.PushItem( picker );
+ }
+
+ SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );
+
+ // Remove segments backtracking over others
+ RemoveBacktracks( s_wires );
+
+ // Add the new wires
+ screen->Append( s_wires );
+
+ // Correct and remove segments that need to be merged.
+ screen->SchematicCleanUp( NULL, 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 ) );
+
+ // 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 ) );
+
+ m_canvas->Refresh();
+
+ OnModify();
+}
+
+
+/**
+ * Function ComputeBreakPoint
+ * computes the middle coordinate for 2 segments from the start point to \a aPosition
+ * with the segments kept in the horizontal or vertical axis only.
+ *
+ * @param aSegment A pointer to a #SCH_LINE object containing the first line break point
+ * to compute.
+ * @param aPosition A reference to a wxPoint object containing the coordinates of the
+ * position used to calculate the line break point.
+ */
+static void ComputeBreakPoint( SCH_LINE* aSegment, const wxPoint& aPosition )
+{
+ wxCHECK_RET( aSegment != NULL, wxT( "Cannot compute break point of NULL line segment." ) );
+
+ SCH_LINE* nextSegment = aSegment->Next();
+ wxPoint midPoint = aPosition;
+
+ wxCHECK_RET( nextSegment != NULL,
+ wxT( "Cannot compute break point of NULL second line segment." ) );
+
+#if 0
+ if( ABS( midPoint.x - aSegment->GetStartPoint().x ) <
+ ABS( midPoint.y - aSegment->GetStartPoint().y ) )
+ midPoint.x = aSegment->GetStartPoint().x;
+ else
+ midPoint.y = aSegment->GetStartPoint().y;
+#else
+ int iDx = aSegment->GetEndPoint().x - aSegment->GetStartPoint().x;
+ int iDy = aSegment->GetEndPoint().y - aSegment->GetStartPoint().y;
+
+ if( iDy != 0 ) // keep the first segment orientation (currently horizontal)
+ {
+ midPoint.x = aSegment->GetStartPoint().x;
+ }
+ else if( iDx != 0 ) // keep the first segment orientation (currently vertical)
+ {
+ midPoint.y = aSegment->GetStartPoint().y;
+ }
+ else
+ {
+ if( std::abs( midPoint.x - aSegment->GetStartPoint().x ) <
+ std::abs( midPoint.y - aSegment->GetStartPoint().y ) )
+ midPoint.x = aSegment->GetStartPoint().x;
+ else
+ midPoint.y = aSegment->GetStartPoint().y;
+ }
+#endif
+
+ aSegment->SetEndPoint( midPoint );
+ nextSegment->SetStartPoint( midPoint );
+ nextSegment->SetEndPoint( aPosition );
+}
+
+
+void SCH_EDIT_FRAME::DeleteCurrentSegment( wxDC* DC )
+{
+ SCH_SCREEN* screen = GetScreen();
+
+ SetRepeatItem( NULL );
+
+ if( ( screen->GetCurItem() == NULL ) || !screen->GetCurItem()->IsNew() )
+ return;
+
+ DrawSegment( m_canvas, DC, wxDefaultPosition, false );
+
+ screen->Remove( screen->GetCurItem() );
+ m_canvas->SetMouseCaptureCallback( NULL );
+ screen->SetCurItem( NULL );
+}
+
+
+SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( wxDC* aDC, 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 );
+ SaveCopyInUndoList( junction, UR_NEW );
+ OnModify();
+ }
+
+ return junction;
+}
+
+
+SCH_NO_CONNECT* SCH_EDIT_FRAME::AddNoConnect( wxDC* aDC, const wxPoint& aPosition )
+{
+ SCH_NO_CONNECT* no_connect = new SCH_NO_CONNECT( aPosition );
+
+ SetRepeatItem( no_connect );
+ GetScreen()->Append( no_connect );
+ GetScreen()->SchematicCleanUp( m_canvas, aDC );
+ OnModify();
+ m_canvas->Refresh();
+ SaveCopyInUndoList( no_connect, UR_NEW );
+ return no_connect;
+}
+
+
+/* Abort function for wire, bus or line creation
+ */
+static void AbortCreateNewLine( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
+{
+ SCH_SCREEN* screen = (SCH_SCREEN*) aPanel->GetScreen();
+
+ if( screen->GetCurItem() )
+ {
+ s_wires.DeleteAll(); // Free the list, for a future usage
+ screen->SetCurItem( NULL );
+ aPanel->Refresh();
+ }
+ else
+ {
+ SCH_EDIT_FRAME* parent = ( SCH_EDIT_FRAME* ) aPanel->GetParent();
+ parent->SetRepeatItem( NULL );
+ }
+
+ // Clear flags used in edit functions.
+ screen->ClearDrawingState();
+}
+
+
+void SCH_EDIT_FRAME::RepeatDrawItem( wxDC* DC )
+{
+ SCH_ITEM* repeater = GetRepeatItem();
+
+ if( !repeater )
+ return;
+
+ //D( repeater>Show( 0, std::cout ); )
+
+ // clone the repeater, move it, insert into display list, then save a copy
+ // via SetRepeatItem();
+
+ SCH_ITEM* my_clone = (SCH_ITEM*) repeater->Clone();
+
+ // If cloning a component then put into 'move' mode.
+ if( my_clone->Type() == SCH_COMPONENT_T )
+ {
+ wxPoint pos = GetCrossHairPosition() -
+ ( (SCH_COMPONENT*) my_clone )->GetPosition();
+
+ my_clone->SetFlags( IS_NEW );
+ ( (SCH_COMPONENT*) my_clone )->SetTimeStamp( GetNewTimeStamp() );
+ my_clone->Move( pos );
+ my_clone->Draw( m_canvas, DC, wxPoint( 0, 0 ), g_XorMode );
+ PrepareMoveItem( my_clone, DC );
+ }
+ else
+ {
+ my_clone->Move( GetRepeatStep() );
+
+ if( my_clone->CanIncrementLabel() )
+ ( (SCH_TEXT*) my_clone )->IncrementLabel( GetRepeatDeltaLabel() );
+
+ GetScreen()->Append( my_clone );
+ GetScreen()->TestDanglingEnds();
+ my_clone->Draw( m_canvas, DC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
+ SaveCopyInUndoList( my_clone, UR_NEW );
+ my_clone->ClearFlags();
+ }
+
+ // clone my_clone, now that it has been moved, thus saving new position.
+ SetRepeatItem( my_clone );
+}