kicad/eeschema/sch_screen.cpp

1566 lines
42 KiB
C++
Raw Normal View History

/*
* 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>
2015-02-28 20:50:35 +00:00
* 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>
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
#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[] =
2009-02-06 11:45:35 +00:00
{
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
2009-02-06 11:45:35 +00:00
};
#define MM_TO_SCH_UNITS 1000.0 / 25.4 //schematic internal unites are mils
/* Default grid sizes for the schematic editor.
2015-07-20 14:11:34 +00:00
* 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[] = {
2015-07-20 14:11:34 +00:00
{ 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" ) )
{
2015-02-28 20:50:35 +00:00
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() );
}
2007-09-01 12:00:30 +00:00
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 );
}
2008-02-26 19:19:54 +00:00
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();
2008-02-26 19:19:54 +00:00
while( itemList )
2008-02-26 19:19:54 +00:00
{
if( itemList == aItem )
2008-02-26 19:19:54 +00:00
return true;
itemList = itemList->Next();
2008-02-26 19:19:54 +00:00
}
return false;
}
2008-02-26 19:19:54 +00:00
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() )
{
2011-07-08 19:55:41 +00:00
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 )
{
2012-02-05 13:02:46 +00:00
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 )
{
2011-12-12 08:37:05 +00:00
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()
2008-04-12 18:39:20 +00:00
{
m_index = 0;
2008-04-12 18:39:20 +00:00
BuildScreenList( g_RootSheet );
}
SCH_SCREENS::~SCH_SCREENS()
{
}
SCH_SCREEN* SCH_SCREENS::GetFirst()
{
m_index = 0;
if( m_screens.size() > 0 )
return m_screens[0];
2008-02-26 19:19:54 +00:00
return NULL;
}
2008-02-26 19:19:54 +00:00
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 ];
2008-02-26 19:19:54 +00:00
return NULL;
}
2008-02-26 19:19:54 +00:00
void SCH_SCREENS::AddScreenToList( SCH_SCREEN* aScreen )
{
if( aScreen == NULL )
2008-02-26 19:19:54 +00:00
return;
for( unsigned int i = 0; i < m_screens.size(); i++ )
2008-02-26 19:19:54 +00:00
{
if( m_screens[i] == aScreen )
2008-02-26 19:19:54 +00:00
return;
}
m_screens.push_back( aScreen );
}
2008-02-26 19:19:54 +00:00
void SCH_SCREENS::BuildScreenList( EDA_ITEM* aItem )
{
if( aItem && aItem->Type() == SCH_SHEET_T )
2008-02-26 19:19:54 +00:00
{
SCH_SHEET* ds = (SCH_SHEET*) aItem;
aItem = ds->GetScreen();
2008-02-26 19:19:54 +00:00
}
if( aItem && aItem->Type() == SCH_SCREEN_T )
2008-02-26 19:19:54 +00:00
{
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();
2008-02-26 19:19:54 +00:00
AddScreenToList( screen );
EDA_ITEM* strct = screen->GetDrawItems();
2008-02-26 19:19:54 +00:00
while( strct )
{
if( strct->Type() == SCH_SHEET_T )
2008-02-26 19:19:54 +00:00
{
BuildScreenList( strct );
}
strct = strct->Next();
2008-02-26 19:19:54 +00:00
}
}
}
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];
2011-12-12 08:37:05 +00:00
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
2011-12-12 08:37:05 +00:00
item->SetTimeStamp( GetNewTimeStamp() );
}
}
return count;
}
void SCH_SCREENS::DeleteAllMarkers( int 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( int aMarkerType )
{
SCH_ITEM* item;
SCH_ITEM* nextItem;
SCH_MARKER* marker;
SCH_SCREEN* screen;
int count = 0;
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( (aMarkerType != -1) && (marker->GetMarkerType() != aMarkerType) )
continue;
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