diff options
Diffstat (limited to 'eeschema/bus-wire-junction.cpp')
-rw-r--r-- | eeschema/bus-wire-junction.cpp | 505 |
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 ); +} |