diff options
Diffstat (limited to 'eeschema/sch_screen.cpp')
-rw-r--r-- | eeschema/sch_screen.cpp | 1563 |
1 files changed, 1563 insertions, 0 deletions
diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp new file mode 100644 index 0000000..c2f8201 --- /dev/null +++ b/eeschema/sch_screen.cpp @@ -0,0 +1,1563 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> + * Copyright (C) 2008-2015 Wayne Stambaugh <stambaughw@verizon.net> + * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.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 sch_screen.cpp + * @brief Implementation of SCH_SCREEN and SCH_SCREENS classes. + */ + +#include <fctsys.h> +#include <gr_basic.h> +#include <common.h> +#include <kicad_string.h> +#include <eeschema_id.h> +#include <pgm_base.h> +#include <kiway.h> +#include <class_drawpanel.h> +#include <sch_item_struct.h> +#include <schframe.h> +#include <plot_common.h> + +#include <netlist.h> +#include <class_netlist_object.h> +#include <class_library.h> +#include <sch_junction.h> +#include <sch_bus_entry.h> +#include <sch_line.h> +#include <sch_marker.h> +#include <sch_no_connect.h> +#include <sch_sheet.h> +#include <sch_component.h> +#include <sch_text.h> +#include <lib_pin.h> + +#include <boost/foreach.hpp> + +#define EESCHEMA_FILE_STAMP "EESchema" + +/* Default zoom values. Limited to these values to keep a decent size + * to menus + */ +static double SchematicZoomList[] = +{ + 0.5, 0.7, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 11.0, + 13.0, 16.0, 20.0, 26.0, 32.0, 48.0, 64.0, 80.0, 128.0 +}; + +#define MM_TO_SCH_UNITS 1000.0 / 25.4 //schematic internal unites are mils + + +/* Default grid sizes for the schematic editor. + * Do NOT add others values (mainly grid values in mm), because they + * can break the schematic: Because wires and pins are considered as + * connected when the are to the same coordinate we cannot mix + * coordinates in mils (internal units) and mm (that cannot exactly + * converted in mils in many cases). In fact schematic must only use + * 50 and 25 mils to place labels, wires and components others values + * are useful only for graphic items (mainly in library editor) so use + * integer values in mils only. The 100 mil grid is added to help + * conform to the KiCad Library Convention. Which states: "Using a + * 100mil grid, pin ends and origin must lie on grid nodes IEC-60617" +*/ +static GRID_TYPE SchematicGridList[] = { + { ID_POPUP_GRID_LEVEL_100, wxRealPoint( 100, 100 ) }, + { ID_POPUP_GRID_LEVEL_50, wxRealPoint( 50, 50 ) }, + { ID_POPUP_GRID_LEVEL_25, wxRealPoint( 25, 25 ) }, + { ID_POPUP_GRID_LEVEL_10, wxRealPoint( 10, 10 ) }, + { ID_POPUP_GRID_LEVEL_5, wxRealPoint( 5, 5 ) }, + { ID_POPUP_GRID_LEVEL_2, wxRealPoint( 2, 2 ) }, + { ID_POPUP_GRID_LEVEL_1, wxRealPoint( 1, 1 ) }, +}; + + +SCH_SCREEN::SCH_SCREEN( KIWAY* aKiway ) : + BASE_SCREEN( SCH_SCREEN_T ), + KIWAY_HOLDER( aKiway ), + m_paper( wxT( "A4" ) ) +{ + m_modification_sync = 0; + + SetZoom( 32 ); + + for( unsigned i = 0; i < DIM( SchematicZoomList ); i++ ) + m_ZoomList.push_back( SchematicZoomList[i] ); + + for( unsigned i = 0; i < DIM( SchematicGridList ); i++ ) + AddGrid( SchematicGridList[i] ); + + SetGrid( wxRealPoint( 50, 50 ) ); // Default grid size. + m_refCount = 0; + + // Suitable for schematic only. For libedit and viewlib, must be set to true + m_Center = false; + + InitDataPoints( m_paper.GetSizeIU() ); +} + + +SCH_SCREEN::~SCH_SCREEN() +{ + ClearUndoRedoList(); + FreeDrawList(); +} + + +void SCH_SCREEN::IncRefCount() +{ + m_refCount++; +} + + +void SCH_SCREEN::DecRefCount() +{ + wxCHECK_RET( m_refCount != 0, + wxT( "Screen reference count already zero. Bad programmer!" ) ); + m_refCount--; +} + + +void SCH_SCREEN::Clear() +{ + FreeDrawList(); + + // Clear the project settings + m_ScreenNumber = m_NumberOfScreens = 1; + + m_titles.Clear(); +} + + +void SCH_SCREEN::FreeDrawList() +{ + m_drawList.DeleteAll(); +} + + +void SCH_SCREEN::Remove( SCH_ITEM* aItem ) +{ + m_drawList.Remove( aItem ); +} + + +void SCH_SCREEN::DeleteItem( SCH_ITEM* aItem ) +{ + wxCHECK_RET( aItem, wxT( "Cannot delete invalid item from screen." ) ); + + SetModify(); + + if( aItem->Type() == SCH_SHEET_PIN_T ) + { + // This structure is attached to a sheet, get the parent sheet object. + SCH_SHEET_PIN* sheetPin = (SCH_SHEET_PIN*) aItem; + SCH_SHEET* sheet = sheetPin->GetParent(); + wxCHECK_RET( sheet, + wxT( "Sheet label parent not properly set, bad programmer!" ) ); + sheet->RemovePin( sheetPin ); + return; + } + else + { + delete m_drawList.Remove( aItem ); + } +} + + +bool SCH_SCREEN::CheckIfOnDrawList( SCH_ITEM* aItem ) +{ + SCH_ITEM* itemList = m_drawList.begin(); + + while( itemList ) + { + if( itemList == aItem ) + return true; + + itemList = itemList->Next(); + } + + return false; +} + + +SCH_ITEM* SCH_SCREEN::GetItem( const wxPoint& aPosition, int aAccuracy, KICAD_T aType ) const +{ + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->HitTest( aPosition, aAccuracy ) && (aType == NOT_USED) ) + return item; + + if( (aType == SCH_FIELD_T) && (item->Type() == SCH_COMPONENT_T) ) + { + SCH_COMPONENT* component = (SCH_COMPONENT*) item; + + for( int i = REFERENCE; i < component->GetFieldCount(); i++ ) + { + SCH_FIELD* field = component->GetField( i ); + + if( field->HitTest( aPosition, aAccuracy ) ) + return (SCH_ITEM*) field; + } + } + else if( (aType == SCH_SHEET_PIN_T) && (item->Type() == SCH_SHEET_T) ) + { + SCH_SHEET* sheet = (SCH_SHEET*)item; + + SCH_SHEET_PIN* label = sheet->GetPin( aPosition ); + + if( label ) + return (SCH_ITEM*) label; + } + else if( (item->Type() == aType) && item->HitTest( aPosition, aAccuracy ) ) + { + return item; + } + } + + return NULL; +} + + +void SCH_SCREEN::ExtractWires( DLIST< SCH_ITEM >& aList, bool aCreateCopy ) +{ + SCH_ITEM* item; + SCH_ITEM* next_item; + + for( item = m_drawList.begin(); item; item = next_item ) + { + next_item = item->Next(); + + switch( item->Type() ) + { + case SCH_JUNCTION_T: + case SCH_LINE_T: + m_drawList.Remove( item ); + aList.Append( item ); + + if( aCreateCopy ) + m_drawList.Insert( (SCH_ITEM*) item->Clone(), next_item ); + + break; + + default: + break; + } + } +} + + +void SCH_SCREEN::ReplaceWires( DLIST< SCH_ITEM >& aWireList ) +{ + SCH_ITEM* item; + SCH_ITEM* next_item; + + for( item = m_drawList.begin(); item; item = next_item ) + { + next_item = item->Next(); + + switch( item->Type() ) + { + case SCH_JUNCTION_T: + case SCH_LINE_T: + Remove( item ); + delete item; + break; + + default: + break; + } + } + + m_drawList.Append( aWireList ); +} + + +void SCH_SCREEN::MarkConnections( SCH_LINE* aSegment ) +{ + wxCHECK_RET( (aSegment) && (aSegment->Type() == SCH_LINE_T), + wxT( "Invalid object pointer." ) ); + + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->GetFlags() & CANDIDATE ) + continue; + + if( item->Type() == SCH_JUNCTION_T ) + { + SCH_JUNCTION* junction = (SCH_JUNCTION*) item; + + if( aSegment->IsEndPoint( junction->GetPosition() ) ) + item->SetFlags( CANDIDATE ); + + continue; + } + + if( item->Type() != SCH_LINE_T ) + continue; + + SCH_LINE* segment = (SCH_LINE*) item; + + if( aSegment->IsEndPoint( segment->GetStartPoint() ) + && !GetPin( segment->GetStartPoint(), NULL, true ) ) + { + item->SetFlags( CANDIDATE ); + MarkConnections( segment ); + } + + if( aSegment->IsEndPoint( segment->GetEndPoint() ) + && !GetPin( segment->GetEndPoint(), NULL, true ) ) + { + item->SetFlags( CANDIDATE ); + MarkConnections( segment ); + } + } +} + + +bool SCH_SCREEN::IsJunctionNeeded( const wxPoint& aPosition ) +{ + if( GetItem( aPosition, 0, SCH_JUNCTION_T ) ) + return false; + + if( GetWire( aPosition, 0, EXCLUDE_END_POINTS_T ) ) + { + if( GetWire( aPosition, 0, END_POINTS_ONLY_T ) ) + return true; + + if( GetPin( aPosition, NULL, true ) ) + return true; + } + + return false; +} + + +bool SCH_SCREEN::IsTerminalPoint( const wxPoint& aPosition, int aLayer ) +{ + wxCHECK_MSG( aLayer == LAYER_NOTES || aLayer == LAYER_BUS || aLayer == LAYER_WIRE, false, + wxT( "Invalid layer type passed to SCH_SCREEN::IsTerminalPoint()." ) ); + + SCH_SHEET_PIN* label; + SCH_TEXT* text; + + switch( aLayer ) + { + case LAYER_BUS: + + if( GetBus( aPosition ) ) + return true; + + label = GetSheetLabel( aPosition ); + + if( label && IsBusLabel( label->GetText() ) && label->IsConnected( aPosition ) ) + return true; + + text = GetLabel( aPosition ); + + if( text && IsBusLabel( text->GetText() ) && text->IsConnected( aPosition ) + && (text->Type() != SCH_LABEL_T) ) + return true; + + break; + + case LAYER_NOTES: + + if( GetLine( aPosition ) ) + return true; + + break; + + case LAYER_WIRE: + if( GetItem( aPosition, std::max( GetDefaultLineThickness(), 3 ), SCH_BUS_WIRE_ENTRY_T) ) + return true; + + if( GetItem( aPosition, std::max( GetDefaultLineThickness(), 3 ), SCH_BUS_BUS_ENTRY_T) ) + return true; + + if( GetItem( aPosition, std::max( GetDefaultLineThickness(), 3 ), SCH_JUNCTION_T ) ) + return true; + + if( GetPin( aPosition, NULL, true ) ) + return true; + + if( GetWire( aPosition ) ) + return true; + + text = GetLabel( aPosition ); + + if( text && text->IsConnected( aPosition ) && !IsBusLabel( text->GetText() ) ) + return true; + + label = GetSheetLabel( aPosition ); + + if( label && label->IsConnected( aPosition ) && !IsBusLabel( label->GetText() ) ) + return true; + + break; + + default: + break; + } + + return false; +} + + +bool SCH_SCREEN::SchematicCleanUp( EDA_DRAW_PANEL* aCanvas, wxDC* aDC ) +{ + SCH_ITEM* item, * testItem; + bool modified = false; + + item = m_drawList.begin(); + + for( ; item; item = item->Next() ) + { + if( ( item->Type() != SCH_LINE_T ) && ( item->Type() != SCH_JUNCTION_T ) ) + continue; + + testItem = item->Next(); + + while( testItem ) + { + 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 ); + testItem = m_drawList.begin(); + modified = true; + } + else + { + testItem = testItem->Next(); + } + } + 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 ); + testItem = m_drawList.begin(); + modified = true; + } + else + { + testItem = testItem->Next(); + } + } + else + { + testItem = testItem->Next(); + } + } + } + + TestDanglingEnds( aCanvas, aDC ); + + if( aCanvas && modified ) + aCanvas->Refresh(); + + return modified; +} + + +bool SCH_SCREEN::Save( FILE* aFile ) const +{ + // Creates header + if( fprintf( aFile, "%s %s %d\n", EESCHEMA_FILE_STAMP, + SCHEMATIC_HEAD_STRING, EESCHEMA_VERSION ) < 0 ) + return false; + + BOOST_FOREACH( const PART_LIB& lib, *Prj().SchLibs() ) + { + if( fprintf( aFile, "LIBS:%s\n", TO_UTF8( lib.GetName() ) ) < 0 ) + return false; + } + + // This section is not used, but written for file compatibility + if( fprintf( aFile, "EELAYER %d %d\n", LAYERSCH_ID_COUNT, 0 ) < 0 + || fprintf( aFile, "EELAYER END\n" ) < 0 ) + return false; + + /* Write page info, ScreenNumber and NumberOfScreen; not very meaningful for + * SheetNumber and Sheet Count in a complex hierarchy, but useful in + * simple hierarchy and flat hierarchy. Used also to search the root + * sheet ( ScreenNumber = 1 ) within the files + */ + const TITLE_BLOCK& tb = GetTitleBlock(); + + if( fprintf( aFile, "$Descr %s %d %d%s\n", TO_UTF8( m_paper.GetType() ), + m_paper.GetWidthMils(), + m_paper.GetHeightMils(), + !m_paper.IsCustom() && m_paper.IsPortrait() ? + " portrait" : "" + ) < 0 + || fprintf( aFile, "encoding utf-8\n") < 0 + || fprintf( aFile, "Sheet %d %d\n", m_ScreenNumber, m_NumberOfScreens ) < 0 + || fprintf( aFile, "Title %s\n", EscapedUTF8( tb.GetTitle() ).c_str() ) < 0 + || fprintf( aFile, "Date %s\n", EscapedUTF8( tb.GetDate() ).c_str() ) < 0 + || fprintf( aFile, "Rev %s\n", EscapedUTF8( tb.GetRevision() ).c_str() ) < 0 + || fprintf( aFile, "Comp %s\n", EscapedUTF8( tb.GetCompany() ).c_str() ) < 0 + || fprintf( aFile, "Comment1 %s\n", EscapedUTF8( tb.GetComment1() ).c_str() ) < 0 + || fprintf( aFile, "Comment2 %s\n", EscapedUTF8( tb.GetComment2() ).c_str() ) < 0 + || fprintf( aFile, "Comment3 %s\n", EscapedUTF8( tb.GetComment3() ).c_str() ) < 0 + || fprintf( aFile, "Comment4 %s\n", EscapedUTF8( tb.GetComment4() ).c_str() ) < 0 + || fprintf( aFile, "$EndDescr\n" ) < 0 ) + return false; + + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( !item->Save( aFile ) ) + return false; + } + + if( fprintf( aFile, "$EndSCHEMATC\n" ) < 0 ) + return false; + + return true; +} + + +void SCH_SCREEN::CheckComponentsToPartsLinks() +{ + // Initialize or reinitialize the pointer to the LIB_PART for each component + // found in m_drawList, but only if needed (change in lib or schematic) + // therefore the calculation time is usually very low. + + if( m_drawList.GetCount() ) + { + PART_LIBS* libs = Prj().SchLibs(); + int mod_hash = libs->GetModifyHash(); + + // Must we resolve? + if( m_modification_sync != mod_hash ) + { + SCH_TYPE_COLLECTOR c; + + c.Collect( GetDrawItems(), SCH_COLLECTOR::ComponentsOnly ); + + SCH_COMPONENT::ResolveAll( c, libs ); + + m_modification_sync = mod_hash; // note the last mod_hash + + // guard against unneeded runs through this code path by printing trace + DBG(printf("%s: resync-ing %s\n", __func__, TO_UTF8( GetFileName() ) );) + } + } +} + + +void SCH_SCREEN::Draw( EDA_DRAW_PANEL* aCanvas, wxDC* aDC, GR_DRAWMODE aDrawMode, EDA_COLOR_T aColor ) +{ + /* note: SCH_SCREEN::Draw is useful only for schematic. + * library editor and library viewer do not use m_drawList, and therefore + * their SCH_SCREEN::Draw() draws nothing + */ + + CheckComponentsToPartsLinks(); + + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->IsMoving() || item->IsResized() ) + continue; + + // uncomment line below when there is a virtual + // EDA_ITEM::GetBoundingBox() + // if( panel->GetClipBox().Intersects( Structs->GetBoundingBox() + // ) ) + item->Draw( aCanvas, aDC, wxPoint( 0, 0 ), aDrawMode, aColor ); + } +} + + +/* note: SCH_SCREEN::Plot is useful only for schematic. + * library editor and library viewer do not use a draw list, and therefore + * SCH_SCREEN::Plot plots nothing + */ +void SCH_SCREEN::Plot( PLOTTER* aPlotter ) +{ + CheckComponentsToPartsLinks(); + + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + aPlotter->SetCurrentLineWidth( item->GetPenSize() ); + item->Plot( aPlotter ); + } +} + + +void SCH_SCREEN::ClearUndoORRedoList( UNDO_REDO_CONTAINER& aList, int aItemCount ) +{ + if( aItemCount == 0 ) + return; + + unsigned icnt = aList.m_CommandsList.size(); + + if( aItemCount > 0 ) + icnt = aItemCount; + + for( unsigned ii = 0; ii < icnt; ii++ ) + { + if( aList.m_CommandsList.size() == 0 ) + break; + + PICKED_ITEMS_LIST* curr_cmd = aList.m_CommandsList[0]; + aList.m_CommandsList.erase( aList.m_CommandsList.begin() ); + + curr_cmd->ClearListAndDeleteItems(); + delete curr_cmd; // Delete command + } +} + + +void SCH_SCREEN::ClearDrawingState() +{ + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + item->ClearFlags(); +} + + +LIB_PIN* SCH_SCREEN::GetPin( const wxPoint& aPosition, SCH_COMPONENT** aComponent, + bool aEndPointOnly ) const +{ + SCH_ITEM* item; + SCH_COMPONENT* component = NULL; + LIB_PIN* pin = NULL; + + for( item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->Type() != SCH_COMPONENT_T ) + continue; + + component = (SCH_COMPONENT*) item; + + if( aEndPointOnly ) + { + pin = NULL; + + LIB_PART* part = Prj().SchLibs()->FindLibPart( component->GetPartName() ); + + if( !part ) + continue; + + for( pin = part->GetNextPin(); pin; pin = part->GetNextPin( pin ) ) + { + // Skip items not used for this part. + if( component->GetUnit() && pin->GetUnit() && + ( pin->GetUnit() != component->GetUnit() ) ) + continue; + + if( component->GetConvert() && pin->GetConvert() && + ( pin->GetConvert() != component->GetConvert() ) ) + continue; + + if(component->GetPinPhysicalPosition( pin ) == aPosition ) + break; + } + if( pin ) + break; + } + else + { + pin = (LIB_PIN*) component->GetDrawItem( aPosition, LIB_PIN_T ); + + if( pin ) + break; + } + } + + if( pin && aComponent ) + *aComponent = component; + + return pin; +} + + +SCH_SHEET* SCH_SCREEN::GetSheet( const wxString& aName ) +{ + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->Type() != SCH_SHEET_T ) + continue; + + SCH_SHEET* sheet = (SCH_SHEET*) item; + + if( aName.CmpNoCase( sheet->GetName() ) == 0 ) + return sheet; + } + + return NULL; +} + + +SCH_SHEET_PIN* SCH_SCREEN::GetSheetLabel( const wxPoint& aPosition ) +{ + SCH_SHEET_PIN* sheetPin = NULL; + + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->Type() != SCH_SHEET_T ) + continue; + + SCH_SHEET* sheet = (SCH_SHEET*) item; + sheetPin = sheet->GetPin( aPosition ); + + if( sheetPin ) + break; + } + + return sheetPin; +} + + +int SCH_SCREEN::CountConnectedItems( const wxPoint& aPos, bool aTestJunctions ) const +{ + SCH_ITEM* item; + int count = 0; + + for( item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->Type() == SCH_JUNCTION_T && !aTestJunctions ) + continue; + + if( item->IsConnected( aPos ) ) + count++; + } + + return count; +} + + +void SCH_SCREEN::ClearAnnotation( SCH_SHEET_PATH* aSheetPath ) +{ + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->Type() == SCH_COMPONENT_T ) + { + SCH_COMPONENT* component = (SCH_COMPONENT*) item; + + component->ClearAnnotation( aSheetPath ); + + // Clear the modified component flag set by component->ClearAnnotation + // because we do not use it here and we should not leave this flag set, + // when an edition is finished: + component->ClearFlags(); + } + } +} + + +void SCH_SCREEN::GetHierarchicalItems( EDA_ITEMS& aItems ) +{ + SCH_ITEM* item = m_drawList.begin(); + + while( item ) + { + if( ( item->Type() == SCH_SHEET_T ) || ( item->Type() == SCH_COMPONENT_T ) ) + aItems.push_back( item ); + + item = item->Next(); + } +} + + +void SCH_SCREEN::SelectBlockItems() +{ + PICKED_ITEMS_LIST* pickedlist = &m_BlockLocate.GetItems(); + + if( pickedlist->GetCount() == 0 ) + return; + + ClearDrawingState(); + + for( unsigned ii = 0; ii < pickedlist->GetCount(); ii++ ) + { + SCH_ITEM* item = (SCH_ITEM*) pickedlist->GetPickedItem( ii ); + item->SetFlags( SELECTED ); + } + + if( !m_BlockLocate.IsDragging() ) + return; + + // Select all the items in the screen connected to the items in the block. + // be sure end lines that are on the block limits are seen inside this block + m_BlockLocate.Inflate( 1 ); + unsigned last_select_id = pickedlist->GetCount(); + + for( unsigned ii = 0; ii < last_select_id; ii++ ) + { + SCH_ITEM* item = (SCH_ITEM*)pickedlist->GetPickedItem( ii ); + item->SetFlags( IS_DRAGGED ); + + if( item->Type() == SCH_LINE_T ) + { + item->IsSelectStateChanged( m_BlockLocate ); + + if( !item->IsSelected() ) + { // This is a special case: + // this selected wire has no ends in block. + // But it was selected (because it intersects the selecting area), + // so we must keep it selected and select items connected to it + // Note: an other option could be: remove it from drag list + item->SetFlags( SELECTED | SKIP_STRUCT ); + std::vector< wxPoint > connections; + item->GetConnectionPoints( connections ); + + for( size_t i = 0; i < connections.size(); i++ ) + addConnectedItemsToBlock( connections[i] ); + } + + pickedlist->SetPickerFlags( item->GetFlags(), ii ); + } + else if( item->IsConnectable() ) + { + std::vector< wxPoint > connections; + + item->GetConnectionPoints( connections ); + + for( size_t jj = 0; jj < connections.size(); jj++ ) + addConnectedItemsToBlock( connections[jj] ); + } + } + + m_BlockLocate.Inflate( -1 ); +} + + +void SCH_SCREEN::addConnectedItemsToBlock( const wxPoint& position ) +{ + SCH_ITEM* item; + ITEM_PICKER picker; + bool addinlist = true; + + for( item = m_drawList.begin(); item; item = item->Next() ) + { + picker.SetItem( item ); + + if( !item->IsConnectable() || !item->IsConnected( position ) + || (item->GetFlags() & SKIP_STRUCT) ) + continue; + + if( item->IsSelected() && item->Type() != SCH_LINE_T ) + continue; + + // A line having 2 ends, it can be tested twice: one time per end + if( item->Type() == SCH_LINE_T ) + { + if( ! item->IsSelected() ) // First time this line is tested + item->SetFlags( SELECTED | STARTPOINT | ENDPOINT ); + else // second time (or more) this line is tested + addinlist = false; + + SCH_LINE* line = (SCH_LINE*) item; + + if( line->GetStartPoint() == position ) + item->ClearFlags( STARTPOINT ); + else if( line->GetEndPoint() == position ) + item->ClearFlags( ENDPOINT ); + } + else + item->SetFlags( SELECTED ); + + if( addinlist ) + { + picker.SetFlags( item->GetFlags() ); + m_BlockLocate.GetItems().PushItem( picker ); + } + } +} + + +int SCH_SCREEN::UpdatePickList() +{ + ITEM_PICKER picker; + EDA_RECT area; + unsigned count; + + area.SetOrigin( m_BlockLocate.GetOrigin() ); + area.SetSize( m_BlockLocate.GetSize() ); + area.Normalize(); + + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + // An item is picked if its bounding box intersects the reference area. + if( item->HitTest( area ) ) + { + picker.SetItem( item ); + m_BlockLocate.PushItem( picker ); + } + } + + // if the block is composed of one item, + // select it as the current item + count = m_BlockLocate.GetCount(); + if( count == 1 ) + { + SetCurItem( (SCH_ITEM*) m_BlockLocate.GetItem( 0 ) ); + } + else + { + SetCurItem( NULL ); + } + + return count; +} + + +bool SCH_SCREEN::TestDanglingEnds( EDA_DRAW_PANEL* aCanvas, wxDC* aDC ) +{ + SCH_ITEM* item; + std::vector< DANGLING_END_ITEM > endPoints; + bool hasDanglingEnds = false; + + for( item = m_drawList.begin(); item; item = item->Next() ) + item->GetEndPoints( endPoints ); + + for( item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->IsDanglingStateChanged( endPoints ) && ( aCanvas ) && ( aDC ) ) + { + item->Draw( aCanvas, aDC, wxPoint( 0, 0 ), g_XorMode ); + item->Draw( aCanvas, aDC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE ); + } + + if( item->IsDangling() ) + hasDanglingEnds = true; + } + + return hasDanglingEnds; +} + + +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() ) + { + if( item->Type() == SCH_LINE_T && item->HitTest( aPosition ) + && (item->GetLayer() == LAYER_BUS || item->GetLayer() == LAYER_WIRE) ) + { + aList.push_back( item ); + } + else if( item->Type() == SCH_JUNCTION_T && item->HitTest( aPosition ) ) + { + aList.push_back( item ); + } + } + + return (int) aList.size(); +} + + +SCH_LINE* SCH_SCREEN::GetWireOrBus( const wxPoint& aPosition ) +{ + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( (item->Type() == SCH_LINE_T) && item->HitTest( aPosition ) + && (item->GetLayer() == LAYER_BUS || item->GetLayer() == LAYER_WIRE) ) + { + return (SCH_LINE*) item; + } + } + + return NULL; +} + + +SCH_LINE* SCH_SCREEN::GetLine( const wxPoint& aPosition, int aAccuracy, int aLayer, + SCH_LINE_TEST_T aSearchType ) +{ + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->Type() != SCH_LINE_T ) + continue; + + if( item->GetLayer() != aLayer ) + continue; + + if( !item->HitTest( aPosition, aAccuracy ) ) + continue; + + switch( aSearchType ) + { + case ENTIRE_LENGTH_T: + return (SCH_LINE*) item; + + case EXCLUDE_END_POINTS_T: + if( !( (SCH_LINE*) item )->IsEndPoint( aPosition ) ) + return (SCH_LINE*) item; + break; + + case END_POINTS_ONLY_T: + if( ( (SCH_LINE*) item )->IsEndPoint( aPosition ) ) + return (SCH_LINE*) item; + } + } + + return NULL; +} + + +SCH_TEXT* SCH_SCREEN::GetLabel( const wxPoint& aPosition, int aAccuracy ) +{ + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + switch( item->Type() ) + { + case SCH_LABEL_T: + case SCH_GLOBAL_LABEL_T: + case SCH_HIERARCHICAL_LABEL_T: + if( item->HitTest( aPosition, aAccuracy ) ) + return (SCH_TEXT*) item; + + default: + ; + } + } + + return NULL; +} + + +bool SCH_SCREEN::SetComponentFootprint( SCH_SHEET_PATH* aSheetPath, const wxString& aReference, + const wxString& aFootPrint, bool aSetVisible ) +{ + SCH_COMPONENT* component; + bool found = false; + + for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + if( item->Type() != SCH_COMPONENT_T ) + continue; + + component = (SCH_COMPONENT*) item; + + if( aReference.CmpNoCase( component->GetRef( aSheetPath ) ) == 0 ) + { + // Found: Init Footprint Field + + /* Give a reasonable value to the field position and + * orientation, if the text is empty at position 0, because + * it is probably not yet initialized + */ + SCH_FIELD * fpfield = component->GetField( FOOTPRINT ); + if( fpfield->GetText().IsEmpty() + && ( fpfield->GetTextPosition() == component->GetPosition() ) ) + { + fpfield->SetOrientation( component->GetField( VALUE )->GetOrientation() ); + fpfield->SetTextPosition( component->GetField( VALUE )->GetTextPosition() ); + fpfield->SetSize( component->GetField( VALUE )->GetSize() ); + + if( fpfield->GetOrientation() == 0 ) + fpfield->Offset( wxPoint( 0, 100 ) ); + else + fpfield->Offset( wxPoint( 100, 0 ) ); + } + + fpfield->SetText( aFootPrint ); + fpfield->SetVisible( aSetVisible ); + + found = true; + } + } + + return found; +} + + +int SCH_SCREEN::GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aList, + bool aFullConnection ) +{ + SCH_ITEM* item; + EDA_ITEM* tmp; + EDA_ITEMS list; + + // Clear flags member for all items. + ClearDrawingState(); + BreakSegmentsOnJunctions(); + + if( GetNode( aPosition, list ) == 0 ) + return 0; + + for( size_t i = 0; i < list.size(); i++ ) + { + item = (SCH_ITEM*) list[ i ]; + item->SetFlags( SELECTEDNODE | STRUCT_DELETED ); + + /* Put this structure in the picked list: */ + ITEM_PICKER picker( item, UR_DELETED ); + aList.PushItem( picker ); + } + + // Mark all wires, junctions, .. connected to the item(s) found. + if( aFullConnection ) + { + SCH_LINE* segment; + + for( item = m_drawList.begin(); item; item = item->Next() ) + { + if( !(item->GetFlags() & SELECTEDNODE) ) + continue; + + if( item->Type() != SCH_LINE_T ) + continue; + + MarkConnections( (SCH_LINE*) item ); + } + + // Search all attached wires (i.e wire with one new dangling end ) + for( item = m_drawList.begin(); item; item = item->Next() ) + { + bool noconnect = false; + + if( item->GetFlags() & STRUCT_DELETED ) + continue; // Already seen + + if( !(item->GetFlags() & CANDIDATE) ) + continue; // not a candidate + + if( item->Type() != SCH_LINE_T ) + continue; + + item->SetFlags( SKIP_STRUCT ); + + segment = (SCH_LINE*) item; + + /* If the wire start point is connected to a wire that was already found + * and now is not connected, add the wire to the list. */ + for( tmp = m_drawList.begin(); tmp; tmp = tmp->Next() ) + { + // Ensure tmp is a previously deleted segment: + if( ( tmp->GetFlags() & STRUCT_DELETED ) == 0 ) + continue; + + if( tmp->Type() != SCH_LINE_T ) + continue; + + SCH_LINE* testSegment = (SCH_LINE*) tmp; + + // Test for segment connected to the previously deleted segment: + if( testSegment->IsEndPoint( segment->GetStartPoint() ) ) + break; + } + + // when tmp != NULL, segment is a new candidate: + // put it in deleted list if + // the start point is not connected to an other item (like pin) + if( tmp && !CountConnectedItems( segment->GetStartPoint(), true ) ) + noconnect = true; + + /* If the wire end point is connected to a wire that has already been found + * and now is not connected, add the wire to the list. */ + for( tmp = m_drawList.begin(); tmp; tmp = tmp->Next() ) + { + // Ensure tmp is a previously deleted segment: + if( ( tmp->GetFlags() & STRUCT_DELETED ) == 0 ) + continue; + + if( tmp->Type() != SCH_LINE_T ) + continue; + + SCH_LINE* testSegment = (SCH_LINE*) tmp; + + // Test for segment connected to the previously deleted segment: + if( testSegment->IsEndPoint( segment->GetEndPoint() ) ) + break; + } + + // when tmp != NULL, segment is a new candidate: + // put it in deleted list if + // the end point is not connected to an other item (like pin) + if( tmp && !CountConnectedItems( segment->GetEndPoint(), true ) ) + noconnect = true; + + item->ClearFlags( SKIP_STRUCT ); + + if( noconnect ) + { + item->SetFlags( STRUCT_DELETED ); + + ITEM_PICKER picker( item, UR_DELETED ); + aList.PushItem( picker ); + + item = m_drawList.begin(); + } + } + + // 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 ) + continue; + + if( item->Type() != SCH_LABEL_T ) + continue; + + tmp = GetWireOrBus( ( (SCH_TEXT*) item )->GetPosition() ); + + if( tmp && tmp->GetFlags() & STRUCT_DELETED ) + { + item->SetFlags( STRUCT_DELETED ); + + ITEM_PICKER picker( item, UR_DELETED ); + aList.PushItem( picker ); + } + } + } + + ClearDrawingState(); + + return aList.GetCount(); +} + + +/******************************************************************/ +/* Class SCH_SCREENS to handle the list of screens in a hierarchy */ +/******************************************************************/ + +/** + * Function SortByTimeStamp + * sorts a list of schematic items by time stamp and type. + */ +static bool SortByTimeStamp( const EDA_ITEM* item1, const EDA_ITEM* item2 ) +{ + int ii = item1->GetTimeStamp() - item2->GetTimeStamp(); + + /* If the time stamps are the same, compare type in order to have component objects + * before sheet object. This is done because changing the sheet time stamp + * before the component time stamp could cause the current annotation to be lost. + */ + if( ( ii == 0 && ( item1->Type() != item2->Type() ) ) && ( item1->Type() == SCH_SHEET_T ) ) + ii = -1; + + return ii < 0; +} + + +SCH_SCREENS::SCH_SCREENS() +{ + m_index = 0; + BuildScreenList( g_RootSheet ); +} + + +SCH_SCREENS::~SCH_SCREENS() +{ +} + + +SCH_SCREEN* SCH_SCREENS::GetFirst() +{ + m_index = 0; + + if( m_screens.size() > 0 ) + return m_screens[0]; + + return NULL; +} + + +SCH_SCREEN* SCH_SCREENS::GetNext() +{ + if( m_index < m_screens.size() ) + m_index++; + + return GetScreen( m_index ); +} + + +SCH_SCREEN* SCH_SCREENS::GetScreen( unsigned int aIndex ) const +{ + if( aIndex < m_screens.size() ) + return m_screens[ aIndex ]; + + return NULL; +} + + +void SCH_SCREENS::AddScreenToList( SCH_SCREEN* aScreen ) +{ + if( aScreen == NULL ) + return; + + for( unsigned int i = 0; i < m_screens.size(); i++ ) + { + if( m_screens[i] == aScreen ) + return; + } + + m_screens.push_back( aScreen ); +} + + +void SCH_SCREENS::BuildScreenList( EDA_ITEM* aItem ) +{ + if( aItem && aItem->Type() == SCH_SHEET_T ) + { + SCH_SHEET* ds = (SCH_SHEET*) aItem; + aItem = ds->GetScreen(); + } + + if( aItem && aItem->Type() == SCH_SCREEN_T ) + { + SCH_SCREEN* screen = (SCH_SCREEN*) aItem; + + // Ensure each component has its pointer to its part lib LIB_PART + // up to date (the cost is low if this is the case) + // We do this update here, because most of time this function is called + // to create a netlist, or an ERC, which need this update + screen->CheckComponentsToPartsLinks(); + + AddScreenToList( screen ); + EDA_ITEM* strct = screen->GetDrawItems(); + + while( strct ) + { + if( strct->Type() == SCH_SHEET_T ) + { + BuildScreenList( strct ); + } + + strct = strct->Next(); + } + } +} + + +void SCH_SCREENS::ClearAnnotation() +{ + for( size_t i = 0; i < m_screens.size(); i++ ) + 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; + SCH_ITEM* item; + + for( size_t i = 0; i < m_screens.size(); i++ ) + m_screens[i]->GetHierarchicalItems( items ); + + if( items.size() < 2 ) + return 0; + + sort( items.begin(), items.end(), SortByTimeStamp ); + + int count = 0; + + for( size_t ii = 0; ii < items.size() - 1; ii++ ) + { + item = (SCH_ITEM*)items[ii]; + + SCH_ITEM* nextItem = (SCH_ITEM*)items[ii + 1]; + + if( item->GetTimeStamp() == nextItem->GetTimeStamp() ) + { + count++; + + // for a component, update its Time stamp and its paths + // (m_PathsAndReferences field) + if( item->Type() == SCH_COMPONENT_T ) + ( (SCH_COMPONENT*) item )->SetTimeStamp( GetNewTimeStamp() ); + + // for a sheet, update only its time stamp (annotation of its + // components will be lost) + // @todo: see how to change sheet paths for its cmp list (can + // be possible in most cases) + else + item->SetTimeStamp( GetNewTimeStamp() ); + } + } + + return count; +} + + +void SCH_SCREENS::DeleteAllMarkers( enum MARKER_BASE::TYPEMARKER aMarkerType ) +{ + SCH_ITEM* item; + SCH_ITEM* nextItem; + SCH_MARKER* marker; + SCH_SCREEN* screen; + + for( screen = GetFirst(); screen; screen = GetNext() ) + { + for( item = screen->GetDrawItems(); item; item = nextItem ) + { + nextItem = item->Next(); + + if( item->Type() != SCH_MARKER_T ) + continue; + + marker = (SCH_MARKER*) item; + + if( marker->GetMarkerType() != aMarkerType ) + continue; + + screen->DeleteItem( marker ); + } + } +} + + +int SCH_SCREENS::GetMarkerCount( enum MARKER_BASE::TYPEMARKER aMarkerType, + enum MARKER_BASE::MARKER_SEVERITY aSeverity ) +{ + int count = 0; + + for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) + { + for( SCH_ITEM* item = screen->GetDrawItems(); item; item = item->Next() ) + { + if( item->Type() != SCH_MARKER_T ) + continue; + + SCH_MARKER* marker = (SCH_MARKER*) item; + + if( ( aMarkerType != MARKER_BASE::MARKER_UNSPEC ) && + ( marker->GetMarkerType() != aMarkerType ) ) + continue; + + if( aSeverity == MARKER_BASE::MARKER_SEVERITY_UNSPEC || + aSeverity == marker->GetErrorLevel() ) + count++; + } + } + + return count; +} + +#if defined(DEBUG) +void SCH_SCREEN::Show( int nestLevel, std::ostream& os ) const +{ + // for now, make it look like XML, expand on this later. + NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() << ">\n"; + + for( EDA_ITEM* item = m_drawList.begin(); item; item = item->Next() ) + { + item->Show( nestLevel+1, os ); + } + + NestedSpace( nestLevel, os ) << "</" << GetClass().Lower().mb_str() << ">\n"; +} +#endif |