 Mark null project initial screen as zoom-initialized
The variable `m_Initialized` in `BASE_SCREEN` is used by
`SCH_EDIT_FRAME` to mark whether a screen had its zoom level initialized
by the "zoom to fit screen" action. When this variable is `false`, the
function `SCH_EDIT_FRAME::DisplayCurrentSheet()` performs "zoom to fit
screen", modifying the zoom level. This function is indirectly called in
the undo routines, so if `m_Initialized` is not set to `true`, a zoom
change will occur when the user undoes an operation, a behavior that is
undesired.
`m_Initialized` was not initialized to `true` for the null schematic
(the schematic that is loaded if no project is loaded), causing the
aforementioned undesired behavior.
To prevent this, I've changed the `SCH_EDIT_FRAME` constructor to set
`m_Initialized` to `true`, since it zooms to fit screen already. I've
moved `m_Initialized` from `BASE_SCREEN` to `SCH_SCREEN`, as it is used
only in Eeschema, and renamed it to `m_zoomInitialized`, a name I
believe that better describes what this variable does.
I've also introduced the function `SCH_EDIT_FRAME::initScreenZoom()` to
group the "zoom to fit screen" action with setting `m_Initialized` to
`true`, as they often should occur together.
I'd also like to say that I'm not confident whether
`SCH_EDIT_FRAME::DisplayCurrentSheet()` should perform the zoom level
initialization at this point, but I have decided to not change this
behavior for now, as the commit history suggests it's several years old.
Fixes https://gitlab.com/kicad/code/kicad/issues/7343
5 years ago Fix source comment/doc typos (follow-up)
Found via `codespell -q 3 -S *.po,./thirdparty -L aactual,acount,aline,alocation,alog,anormal,anumber,aother,apoints,aparent,aray,dout,einstance,modul,ot,overide,serie,te,,tesselate,tesselator,tht` 4 years ago  Modular KiCad Blueprint Milestone B), major portions:
*) When kicad.exe closes a project, close any open KIFACEs so that they cannot
get disassociated from their true PROJECT.
*) Allow loading eeschema library editor from kicad.exe
*) Allow loading pcbnew library editor from kicad.exe
*) Rename LIB_COMPONENT to LIB_PART.
*) Add class PART_LIBS, and PART_LIB.
*) Make PART_LIBS non-global, i.e. PROJECT specific.
*) Implement "data on demand" for PART_LIBS
*) Implement "data on demand" for schematic SEARCH_STACK.
*) Use RSTRINGs to retain eeschema editor's notion of last library and part being edited.
*) Get rid of library search on every SCH_COMPONENT::Draw() call, instead use
a weak pointer.
*) Remove all chdir() calls so projects don't need to be CWD.
*) Romove APPEND support from OpenProjectFiles().
*) Make OpenProjectFiles() robust, even for creating new projects.
*) Load EESCHEMA colors in the KIWAY::OnKiwayStart() rather in window open,
and save them in the .eeschema config file, not in the project file.
*) Fix bug with wxDir() while accessing protected dirs in kicad.exe
*) Consolidate template copying into PROJECT class, not in kicad.exe source.
*) Generally untangle eeschema, making its libraries not global but rather
held in the PROJECT.
11 years ago  Modular KiCad Blueprint Milestone B), major portions:
*) When kicad.exe closes a project, close any open KIFACEs so that they cannot
get disassociated from their true PROJECT.
*) Allow loading eeschema library editor from kicad.exe
*) Allow loading pcbnew library editor from kicad.exe
*) Rename LIB_COMPONENT to LIB_PART.
*) Add class PART_LIBS, and PART_LIB.
*) Make PART_LIBS non-global, i.e. PROJECT specific.
*) Implement "data on demand" for PART_LIBS
*) Implement "data on demand" for schematic SEARCH_STACK.
*) Use RSTRINGs to retain eeschema editor's notion of last library and part being edited.
*) Get rid of library search on every SCH_COMPONENT::Draw() call, instead use
a weak pointer.
*) Remove all chdir() calls so projects don't need to be CWD.
*) Romove APPEND support from OpenProjectFiles().
*) Make OpenProjectFiles() robust, even for creating new projects.
*) Load EESCHEMA colors in the KIWAY::OnKiwayStart() rather in window open,
and save them in the .eeschema config file, not in the project file.
*) Fix bug with wxDir() while accessing protected dirs in kicad.exe
*) Consolidate template copying into PROJECT class, not in kicad.exe source.
*) Generally untangle eeschema, making its libraries not global but rather
held in the PROJECT.
11 years ago  Modular KiCad Blueprint Milestone B), major portions:
*) When kicad.exe closes a project, close any open KIFACEs so that they cannot
get disassociated from their true PROJECT.
*) Allow loading eeschema library editor from kicad.exe
*) Allow loading pcbnew library editor from kicad.exe
*) Rename LIB_COMPONENT to LIB_PART.
*) Add class PART_LIBS, and PART_LIB.
*) Make PART_LIBS non-global, i.e. PROJECT specific.
*) Implement "data on demand" for PART_LIBS
*) Implement "data on demand" for schematic SEARCH_STACK.
*) Use RSTRINGs to retain eeschema editor's notion of last library and part being edited.
*) Get rid of library search on every SCH_COMPONENT::Draw() call, instead use
a weak pointer.
*) Remove all chdir() calls so projects don't need to be CWD.
*) Romove APPEND support from OpenProjectFiles().
*) Make OpenProjectFiles() robust, even for creating new projects.
*) Load EESCHEMA colors in the KIWAY::OnKiwayStart() rather in window open,
and save them in the .eeschema config file, not in the project file.
*) Fix bug with wxDir() while accessing protected dirs in kicad.exe
*) Consolidate template copying into PROJECT class, not in kicad.exe source.
*) Generally untangle eeschema, making its libraries not global but rather
held in the PROJECT.
11 years ago  Modular KiCad Blueprint Milestone B), major portions:
*) When kicad.exe closes a project, close any open KIFACEs so that they cannot
get disassociated from their true PROJECT.
*) Allow loading eeschema library editor from kicad.exe
*) Allow loading pcbnew library editor from kicad.exe
*) Rename LIB_COMPONENT to LIB_PART.
*) Add class PART_LIBS, and PART_LIB.
*) Make PART_LIBS non-global, i.e. PROJECT specific.
*) Implement "data on demand" for PART_LIBS
*) Implement "data on demand" for schematic SEARCH_STACK.
*) Use RSTRINGs to retain eeschema editor's notion of last library and part being edited.
*) Get rid of library search on every SCH_COMPONENT::Draw() call, instead use
a weak pointer.
*) Remove all chdir() calls so projects don't need to be CWD.
*) Romove APPEND support from OpenProjectFiles().
*) Make OpenProjectFiles() robust, even for creating new projects.
*) Load EESCHEMA colors in the KIWAY::OnKiwayStart() rather in window open,
and save them in the .eeschema config file, not in the project file.
*) Fix bug with wxDir() while accessing protected dirs in kicad.exe
*) Consolidate template copying into PROJECT class, not in kicad.exe source.
*) Generally untangle eeschema, making its libraries not global but rather
held in the PROJECT.
11 years ago  Modular KiCad Blueprint Milestone B), major portions:
*) When kicad.exe closes a project, close any open KIFACEs so that they cannot
get disassociated from their true PROJECT.
*) Allow loading eeschema library editor from kicad.exe
*) Allow loading pcbnew library editor from kicad.exe
*) Rename LIB_COMPONENT to LIB_PART.
*) Add class PART_LIBS, and PART_LIB.
*) Make PART_LIBS non-global, i.e. PROJECT specific.
*) Implement "data on demand" for PART_LIBS
*) Implement "data on demand" for schematic SEARCH_STACK.
*) Use RSTRINGs to retain eeschema editor's notion of last library and part being edited.
*) Get rid of library search on every SCH_COMPONENT::Draw() call, instead use
a weak pointer.
*) Remove all chdir() calls so projects don't need to be CWD.
*) Romove APPEND support from OpenProjectFiles().
*) Make OpenProjectFiles() robust, even for creating new projects.
*) Load EESCHEMA colors in the KIWAY::OnKiwayStart() rather in window open,
and save them in the .eeschema config file, not in the project file.
*) Fix bug with wxDir() while accessing protected dirs in kicad.exe
*) Consolidate template copying into PROJECT class, not in kicad.exe source.
*) Generally untangle eeschema, making its libraries not global but rather
held in the PROJECT.
11 years ago  Modular KiCad Blueprint Milestone B), major portions:
*) When kicad.exe closes a project, close any open KIFACEs so that they cannot
get disassociated from their true PROJECT.
*) Allow loading eeschema library editor from kicad.exe
*) Allow loading pcbnew library editor from kicad.exe
*) Rename LIB_COMPONENT to LIB_PART.
*) Add class PART_LIBS, and PART_LIB.
*) Make PART_LIBS non-global, i.e. PROJECT specific.
*) Implement "data on demand" for PART_LIBS
*) Implement "data on demand" for schematic SEARCH_STACK.
*) Use RSTRINGs to retain eeschema editor's notion of last library and part being edited.
*) Get rid of library search on every SCH_COMPONENT::Draw() call, instead use
a weak pointer.
*) Remove all chdir() calls so projects don't need to be CWD.
*) Romove APPEND support from OpenProjectFiles().
*) Make OpenProjectFiles() robust, even for creating new projects.
*) Load EESCHEMA colors in the KIWAY::OnKiwayStart() rather in window open,
and save them in the .eeschema config file, not in the project file.
*) Fix bug with wxDir() while accessing protected dirs in kicad.exe
*) Consolidate template copying into PROJECT class, not in kicad.exe source.
*) Generally untangle eeschema, making its libraries not global but rather
held in the PROJECT.
11 years ago  Modular KiCad Blueprint Milestone B), major portions:
*) When kicad.exe closes a project, close any open KIFACEs so that they cannot
get disassociated from their true PROJECT.
*) Allow loading eeschema library editor from kicad.exe
*) Allow loading pcbnew library editor from kicad.exe
*) Rename LIB_COMPONENT to LIB_PART.
*) Add class PART_LIBS, and PART_LIB.
*) Make PART_LIBS non-global, i.e. PROJECT specific.
*) Implement "data on demand" for PART_LIBS
*) Implement "data on demand" for schematic SEARCH_STACK.
*) Use RSTRINGs to retain eeschema editor's notion of last library and part being edited.
*) Get rid of library search on every SCH_COMPONENT::Draw() call, instead use
a weak pointer.
*) Remove all chdir() calls so projects don't need to be CWD.
*) Romove APPEND support from OpenProjectFiles().
*) Make OpenProjectFiles() robust, even for creating new projects.
*) Load EESCHEMA colors in the KIWAY::OnKiwayStart() rather in window open,
and save them in the .eeschema config file, not in the project file.
*) Fix bug with wxDir() while accessing protected dirs in kicad.exe
*) Consolidate template copying into PROJECT class, not in kicad.exe source.
*) Generally untangle eeschema, making its libraries not global but rather
held in the PROJECT.
11 years ago |
|
/*
* 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 Wayne Stambaugh <stambaughw@gmail.com> * Copyright (C) 1992-2022 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 */
#include <wx/filefn.h>
#include <eda_item.h>
#include <id.h>
#include <string_utils.h>
#include <kiway.h>
#include <plotters/plotter.h>
#include <project.h>
#include <reporter.h>
#include <sch_draw_panel.h>
#include <sch_edit_frame.h>
#include <sch_item.h>
#include <symbol_library.h>
#include <connection_graph.h>
#include <lib_item.h>
#include <lib_pin.h>
#include <lib_shape.h>
#include <sch_symbol.h>
#include <sch_junction.h>
#include <sch_line.h>
#include <sch_marker.h>
#include <sch_sheet.h>
#include <sch_sheet_pin.h>
#include <sch_text.h>
#include <schematic.h>
#include <symbol_lib_table.h>
#include <tool/common_tools.h>
#include <sim/sim_model.h> // For V6 to V7 simulation model migration.
#include <sim/sim_value.h> //
#include <locale_io.h>
#include <algorithm>
// TODO(JE) Debugging only
#include <profile.h>
#include "sch_bus_entry.h"
#include "sim/sim_model_ideal.h"
SCH_SCREEN::SCH_SCREEN( EDA_ITEM* aParent ) : BASE_SCREEN( aParent, SCH_SCREEN_T ), m_fileFormatVersionAtLoad( 0 ), m_paper( wxT( "A4" ) ), m_isReadOnly( false ), m_fileExists( false ){ m_modification_sync = 0; m_refCount = 0; m_zoomInitialized = false; m_LastZoomLevel = 1.0;
// Suitable for schematic only. For symbol_editor and viewlib, must be set to true
m_Center = false;
InitDataPoints( m_paper.GetSizeIU( schIUScale.IU_PER_MILS ) );}
SCH_SCREEN::~SCH_SCREEN(){ clearLibSymbols(); FreeDrawList();}
SCHEMATIC* SCH_SCREEN::Schematic() const{ wxCHECK_MSG( GetParent() && GetParent()->Type() == SCHEMATIC_T, nullptr, wxT( "SCH_SCREEN must have a SCHEMATIC parent!" ) );
return static_cast<SCHEMATIC*>( GetParent() );}
void SCH_SCREEN::clearLibSymbols(){ for( const std::pair<const wxString, LIB_SYMBOL*>& libSymbol : m_libSymbols ) delete libSymbol.second;
m_libSymbols.clear();}
void SCH_SCREEN::SetFileName( const wxString& aFileName ){ wxASSERT( aFileName.IsEmpty() || wxIsAbsolutePath( aFileName ) );
m_fileName = aFileName;}
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--;}
bool SCH_SCREEN::HasItems( KICAD_T aItemType ) const{ EE_RTREE::EE_TYPE sheets = m_rtree.OfType( aItemType );
return sheets.begin() != sheets.end();}
bool SCH_SCREEN::ClassOf( const EDA_ITEM* aItem ){ return aItem && SCH_SCREEN_T == aItem->Type();}
void SCH_SCREEN::Append( SCH_ITEM* aItem ){ if( aItem->Type() != SCH_SHEET_PIN_T && aItem->Type() != SCH_FIELD_T ) { // Ensure the item can reach the SCHEMATIC through this screen
aItem->SetParent( this );
if( aItem->Type() == SCH_SYMBOL_T ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( aItem );
if( symbol->GetLibSymbolRef() ) { symbol->GetLibSymbolRef()->GetDrawItems().sort();
auto it = m_libSymbols.find( symbol->GetSchSymbolLibraryName() );
if( it == m_libSymbols.end() || !it->second ) { m_libSymbols[symbol->GetSchSymbolLibraryName()] = new LIB_SYMBOL( *symbol->GetLibSymbolRef() ); } else { // The original library symbol may have changed since the last time
// it was added to the schematic. If it has changed, then a new name
// must be created for the library symbol list to prevent all of the
// other schematic symbols referencing that library symbol from changing.
LIB_SYMBOL* foundSymbol = it->second;
foundSymbol->GetDrawItems().sort();
if( *foundSymbol != *symbol->GetLibSymbolRef() ) { int cnt = 1; wxString newName;
newName.Printf( "%s_%d", symbol->GetLibId().GetUniStringLibItemName(), cnt );
while( m_libSymbols.find( newName ) != m_libSymbols.end() ) { cnt += 1; newName.Printf( "%s_%d", symbol->GetLibId().GetUniStringLibItemName(), cnt ); }
// Update the schematic symbol library link as this symbol only exists
// in the schematic.
symbol->SetSchSymbolLibraryName( newName );
LIB_SYMBOL* newLibSymbol = new LIB_SYMBOL( *symbol->GetLibSymbolRef() ); LIB_ID newLibId( wxEmptyString, newName );
newLibSymbol->SetLibId( newLibId ); newLibSymbol->SetName( newName ); symbol->SetLibSymbol( newLibSymbol->Flatten().release() ); m_libSymbols[newName] = newLibSymbol; } } } }
m_rtree.insert( aItem ); --m_modification_sync; }}
void SCH_SCREEN::Append( SCH_SCREEN* aScreen ){ wxCHECK_RET( aScreen, "Invalid screen object." );
// No need to descend the hierarchy. Once the top level screen is copied, all of its
// children are copied as well.
for( SCH_ITEM* aItem : aScreen->m_rtree ) Append( aItem );
aScreen->Clear( false );}
void SCH_SCREEN::Clear( bool aFree ){ if( aFree ) { FreeDrawList(); clearLibSymbols(); } else { m_rtree.clear(); }
// Clear the project settings
m_virtualPageNumber = m_pageCount = 1;
m_titles.Clear();}
void SCH_SCREEN::FreeDrawList(){ // We don't know which order we will encounter dependent items (e.g. pins or fields), so
// we store the items to be deleted until we've fully cleared the tree before deleting
std::vector<SCH_ITEM*> delete_list;
std::copy_if( m_rtree.begin(), m_rtree.end(), std::back_inserter( delete_list ), []( SCH_ITEM* aItem ) { return ( aItem->Type() != SCH_SHEET_PIN_T && aItem->Type() != SCH_FIELD_T ); } );
m_rtree.clear();
for( SCH_ITEM* item : delete_list ) delete item;}
void SCH_SCREEN::Update( SCH_ITEM* aItem ){ if( Remove( aItem ) ) Append( aItem );}
bool SCH_SCREEN::Remove( SCH_ITEM* aItem ){ bool retv = m_rtree.remove( aItem );
// Check if the library symbol for the removed schematic symbol is still required.
if( retv && aItem->Type() == SCH_SYMBOL_T ) { SCH_SYMBOL* removedSymbol = static_cast<SCH_SYMBOL*>( aItem );
bool removeUnusedLibSymbol = true;
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
if( removedSymbol->GetSchSymbolLibraryName() == symbol->GetSchSymbolLibraryName() ) { removeUnusedLibSymbol = false; break; } }
if( removeUnusedLibSymbol ) { auto it = m_libSymbols.find( removedSymbol->GetSchSymbolLibraryName() );
if( it != m_libSymbols.end() ) { delete it->second; m_libSymbols.erase( it ); } } }
return retv;}
void SCH_SCREEN::DeleteItem( SCH_ITEM* aItem ){ wxCHECK_RET( aItem, wxT( "Cannot delete invalid item from screen." ) );
// Markers are not saved in the file, no need to flag as modified.
// TODO: Maybe we should have a listing somewhere of items that aren't saved?
if( aItem->Type() != SCH_MARKER_T ) SetContentModified();
Remove( aItem );
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 pin parent not properly set, bad programmer!" ) ); sheet->RemovePin( sheetPin ); return; }
delete aItem;}
bool SCH_SCREEN::CheckIfOnDrawList( const SCH_ITEM* aItem ) const{ return m_rtree.contains( aItem, true );}
SCH_ITEM* SCH_SCREEN::GetItem( const VECTOR2I& aPosition, int aAccuracy, KICAD_T aType ) const{ BOX2I bbox; bbox.SetOrigin( aPosition ); bbox.Inflate( aAccuracy );
for( SCH_ITEM* item : Items().Overlapping( aType, bbox ) ) { if( item->HitTest( aPosition, aAccuracy ) ) return item; }
return nullptr;}
std::set<SCH_ITEM*> SCH_SCREEN::MarkConnections( SCH_LINE* aSegment, bool aSecondPass ){#define PROCESSED CANDIDATE // Don't use SKIP_STRUCT; IsConnected() returns false if it's set.
std::set<SCH_ITEM*> retval; std::stack<SCH_LINE*> to_search;
wxCHECK_MSG( aSegment && aSegment->Type() == SCH_LINE_T, retval, wxT( "Invalid pointer." ) );
to_search.push( aSegment );
while( !to_search.empty() ) { SCH_ITEM* item = to_search.top(); to_search.pop();
if( item->HasFlag( PROCESSED ) ) continue;
item->SetFlags( PROCESSED );
for( SCH_ITEM* candidate : Items().Overlapping( SCH_LINE_T, item->GetBoundingBox() ) ) { SCH_LINE* line = static_cast<SCH_LINE*>( candidate );
if( line->HasFlag( PROCESSED ) ) continue;
// Skip connecting lines on different layers (e.g. buses)
if( item->GetLayer() != line->GetLayer() ) continue;
for( VECTOR2I pt : { line->GetStartPoint(), line->GetEndPoint() } ) { if( item->IsConnected( pt ) ) { SCH_ITEM* junction = GetItem( pt, 0, SCH_JUNCTION_T ); SCH_ITEM* pin = GetItem( pt, 0, SCH_PIN_T );
if( item->IsSelected() && aSecondPass ) { if( junction ) retval.insert( junction );
retval.insert( line ); to_search.push( line ); } else if( !junction && !pin ) { retval.insert( line ); to_search.push( line ); }
break; } } } }
for( SCH_ITEM* item : Items() ) item->ClearTempFlags();
return retval;}
bool SCH_SCREEN::IsJunction( const VECTOR2I& aPosition ) const{ bool hasExplicitJunction; bool hasBusEntry; bool isJunction = doIsJunction( aPosition, false, &hasExplicitJunction, &hasBusEntry );
return isJunction;}
bool SCH_SCREEN::IsExplicitJunction( const VECTOR2I& aPosition ) const{ bool hasExplicitJunction; bool hasBusEntry; bool isJunction = doIsJunction( aPosition, false, &hasExplicitJunction, &hasBusEntry );
return isJunction && !hasBusEntry;}
bool SCH_SCREEN::IsExplicitJunctionNeeded( const VECTOR2I& aPosition ) const{ bool hasExplicitJunction; bool hasBusEntry; bool isJunction = doIsJunction( aPosition, false, &hasExplicitJunction, &hasBusEntry );
return isJunction && !hasBusEntry && !hasExplicitJunction;}
TEXT_SPIN_STYLE SCH_SCREEN::GetLabelOrientationForPoint( const VECTOR2I& aPosition, TEXT_SPIN_STYLE aDefaultOrientation, const SCH_SHEET_PATH* aSheet ) const{ auto ret = aDefaultOrientation; for( SCH_ITEM* item : Items().Overlapping( aPosition ) ) { if( item->GetEditFlags() & STRUCT_DELETED ) continue;
switch( item->Type() ) { case SCH_BUS_WIRE_ENTRY_T: { auto busEntry = static_cast<const SCH_BUS_WIRE_ENTRY*>( item ); if( busEntry->m_connected_bus_item ) { // bus connected, take the bus direction into consideration ony if it is
// vertical or horizontal
auto bus = static_cast<const SCH_LINE*>( busEntry->m_connected_bus_item ); if( bus->Angle().AsDegrees() == 90.0 ) { // bus is vertical -> label shall be horizontal and
// shall be placed to the side where the bus entry is
if( aPosition.x < bus->GetPosition().x ) ret = TEXT_SPIN_STYLE::LEFT; else if( aPosition.x > bus->GetPosition().x ) ret = TEXT_SPIN_STYLE::RIGHT; } else if( bus->Angle().AsDegrees() == 0.0 ) { // bus is horizontal -> label shall be vertical and
// shall be placed to the side where the bus entry is
if( aPosition.y < bus->GetPosition().y ) ret = TEXT_SPIN_STYLE::UP; else if( aPosition.y > bus->GetPosition().y ) ret = TEXT_SPIN_STYLE::BOTTOM; } } } break;
case SCH_LINE_T: { auto line = static_cast<const SCH_LINE*>( item ); // line angles goes between -90 and 90 degrees, but normalize
auto angle = line->Angle().Normalize90().AsDegrees();
if( -45 < angle && angle <= 45 ) { if( line->GetStartPoint().x <= line->GetEndPoint().x ) { ret = line->GetEndPoint() == aPosition ? TEXT_SPIN_STYLE::RIGHT : TEXT_SPIN_STYLE::LEFT; } else { ret = line->GetEndPoint() == aPosition ? TEXT_SPIN_STYLE::LEFT : TEXT_SPIN_STYLE::RIGHT; } } else { if( line->GetStartPoint().y <= line->GetEndPoint().y ) { ret = line->GetEndPoint() == aPosition ? TEXT_SPIN_STYLE::BOTTOM : TEXT_SPIN_STYLE::UP; } else { ret = line->GetEndPoint() == aPosition ? TEXT_SPIN_STYLE::UP : TEXT_SPIN_STYLE::BOTTOM; } } } break;
case SCH_SYMBOL_T: { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
for( SCH_PIN* pin : symbol->GetPins( aSheet ) ) { if( pin->GetPosition() == aPosition ) { if( pin->GetOrientation() == PIN_RIGHT ) { ret = TEXT_SPIN_STYLE::LEFT; } else if( pin->GetOrientation() == PIN_LEFT ) { ret = TEXT_SPIN_STYLE::RIGHT; } else if( pin->GetOrientation() == PIN_UP ) { ret = TEXT_SPIN_STYLE::BOTTOM; } else if( pin->GetOrientation() == PIN_DOWN ) { ret = TEXT_SPIN_STYLE::UP; }
switch( static_cast<SYMBOL_ORIENTATION_T>( symbol->GetOrientation() & ( ~( SYM_MIRROR_X | SYM_MIRROR_Y ) ) ) ) { case SYM_ROTATE_CLOCKWISE: case SYM_ORIENT_90: if( ret == TEXT_SPIN_STYLE::UP ) ret = TEXT_SPIN_STYLE::LEFT; else if( ret == TEXT_SPIN_STYLE::BOTTOM ) ret = TEXT_SPIN_STYLE::RIGHT; else if( ret == TEXT_SPIN_STYLE::LEFT ) ret = TEXT_SPIN_STYLE::BOTTOM; else if( ret == TEXT_SPIN_STYLE::RIGHT ) ret = TEXT_SPIN_STYLE::UP;
if( symbol->GetOrientation() & SYM_MIRROR_X ) { if( ret == TEXT_SPIN_STYLE::UP ) ret = TEXT_SPIN_STYLE::BOTTOM; else if( ret == TEXT_SPIN_STYLE::BOTTOM ) ret = TEXT_SPIN_STYLE::UP; }
if( symbol->GetOrientation() & SYM_MIRROR_Y ) { if( ret == TEXT_SPIN_STYLE::LEFT ) ret = TEXT_SPIN_STYLE::RIGHT; else if( ret == TEXT_SPIN_STYLE::RIGHT ) ret = TEXT_SPIN_STYLE::LEFT; } break; case SYM_ROTATE_COUNTERCLOCKWISE: case SYM_ORIENT_270: if( ret == TEXT_SPIN_STYLE::UP ) ret = TEXT_SPIN_STYLE::RIGHT; else if( ret == TEXT_SPIN_STYLE::BOTTOM ) ret = TEXT_SPIN_STYLE::LEFT; else if( ret == TEXT_SPIN_STYLE::LEFT ) ret = TEXT_SPIN_STYLE::UP; else if( ret == TEXT_SPIN_STYLE::RIGHT ) ret = TEXT_SPIN_STYLE::BOTTOM;
if( symbol->GetOrientation() & SYM_MIRROR_X ) { if( ret == TEXT_SPIN_STYLE::UP ) ret = TEXT_SPIN_STYLE::BOTTOM; else if( ret == TEXT_SPIN_STYLE::BOTTOM ) ret = TEXT_SPIN_STYLE::UP; }
if( symbol->GetOrientation() & SYM_MIRROR_Y ) { if( ret == TEXT_SPIN_STYLE::LEFT ) ret = TEXT_SPIN_STYLE::RIGHT; else if( ret == TEXT_SPIN_STYLE::RIGHT ) ret = TEXT_SPIN_STYLE::LEFT; } break; case SYM_ORIENT_180: if( ret == TEXT_SPIN_STYLE::UP ) ret = TEXT_SPIN_STYLE::BOTTOM; else if( ret == TEXT_SPIN_STYLE::BOTTOM ) ret = TEXT_SPIN_STYLE::UP; else if( ret == TEXT_SPIN_STYLE::LEFT ) ret = TEXT_SPIN_STYLE::RIGHT; else if( ret == TEXT_SPIN_STYLE::RIGHT ) ret = TEXT_SPIN_STYLE::LEFT;
if( symbol->GetOrientation() & SYM_MIRROR_X ) { if( ret == TEXT_SPIN_STYLE::UP ) ret = TEXT_SPIN_STYLE::BOTTOM; else if( ret == TEXT_SPIN_STYLE::BOTTOM ) ret = TEXT_SPIN_STYLE::UP; }
if( symbol->GetOrientation() & SYM_MIRROR_Y ) { if( ret == TEXT_SPIN_STYLE::LEFT ) ret = TEXT_SPIN_STYLE::RIGHT; else if( ret == TEXT_SPIN_STYLE::RIGHT ) ret = TEXT_SPIN_STYLE::LEFT; } break; case SYM_ORIENT_0: case SYM_NORMAL: default: if( symbol->GetOrientation() & SYM_MIRROR_X ) { if( ret == TEXT_SPIN_STYLE::UP ) ret = TEXT_SPIN_STYLE::BOTTOM; else if( ret == TEXT_SPIN_STYLE::BOTTOM ) ret = TEXT_SPIN_STYLE::UP; }
if( symbol->GetOrientation() & SYM_MIRROR_Y ) { if( ret == TEXT_SPIN_STYLE::LEFT ) ret = TEXT_SPIN_STYLE::RIGHT; else if( ret == TEXT_SPIN_STYLE::RIGHT ) ret = TEXT_SPIN_STYLE::LEFT; } break; }
break; } } } break; default: break; } } return ret;}
bool SCH_SCREEN::IsExplicitJunctionAllowed( const VECTOR2I& aPosition ) const{ bool hasExplicitJunction; bool hasBusEntry; bool isJunction = doIsJunction( aPosition, true, &hasExplicitJunction, &hasBusEntry );
return isJunction && !hasBusEntry;}
bool SCH_SCREEN::doIsJunction( const VECTOR2I& aPosition, bool aBreakCrossings, bool* aHasExplicitJunctionDot, bool* aHasBusEntry ) const{ enum layers { WIRES = 0, BUSES };
*aHasExplicitJunctionDot = false; *aHasBusEntry = false;
bool breakLines[ 2 ] = { false }; std::unordered_set<int> exitAngles[ 2 ]; std::vector<const SCH_LINE*> midPointLines[ 2 ];
// A pin at 90° still shouldn't match a line at 90° so just give pins unique numbers
int uniqueAngle = 10000;
for( const SCH_ITEM* item : Items().Overlapping( aPosition ) ) { if( item->GetEditFlags() & STRUCT_DELETED ) continue;
switch( item->Type() ) { case SCH_JUNCTION_T: if( item->HitTest( aPosition, -1 ) ) *aHasExplicitJunctionDot = true;
break;
case SCH_LINE_T: { const SCH_LINE* line = static_cast<const SCH_LINE*>( item ); int layer;
if( line->GetStartPoint() == line->GetEndPoint() ) break; else if( line->GetLayer() == LAYER_WIRE ) layer = WIRES; else if( line->GetLayer() == LAYER_BUS ) layer = BUSES; else break;
if( line->IsConnected( aPosition ) ) { breakLines[ layer ] = true; exitAngles[ layer ].insert( line->GetAngleFrom( aPosition ) ); } else if( line->HitTest( aPosition, -1 ) ) { if( aBreakCrossings ) breakLines[ layer ] = true;
// Defer any line midpoints until we know whether or not we're breaking them
midPointLines[ layer ].push_back( line ); } } break;
case SCH_BUS_WIRE_ENTRY_T: if( item->IsConnected( aPosition ) ) { breakLines[ BUSES ] = true; exitAngles[ BUSES ].insert( uniqueAngle++ ); breakLines[ WIRES ] = true; exitAngles[ WIRES ].insert( uniqueAngle++ ); *aHasBusEntry = true; }
break;
case SCH_SYMBOL_T: case SCH_SHEET_T: if( item->IsConnected( aPosition ) ) { breakLines[ WIRES ] = true; exitAngles[ WIRES ].insert( uniqueAngle++ ); }
break;
default: break; } }
for( int layer : { WIRES, BUSES } ) { if( breakLines[ layer ] ) { for( const SCH_LINE* line : midPointLines[ layer ] ) { exitAngles[ layer ].insert( line->GetAngleFrom( aPosition ) ); exitAngles[ layer ].insert( line->GetReverseAngleFrom( aPosition ) ); } } }
return exitAngles[ WIRES ].size() >= 3 || exitAngles[ BUSES ].size() >= 3;}
bool SCH_SCREEN::IsTerminalPoint( const VECTOR2I& aPosition, int aLayer ) const{ wxCHECK_MSG( aLayer == LAYER_NOTES || aLayer == LAYER_BUS || aLayer == LAYER_WIRE, false, wxT( "Invalid layer type passed to SCH_SCREEN::IsTerminalPoint()." ) );
SCH_SHEET_PIN* sheetPin; SCH_LABEL_BASE* label;
switch( aLayer ) { case LAYER_BUS: if( GetBus( aPosition ) ) return true;
sheetPin = GetSheetPin( aPosition );
if( sheetPin && sheetPin->IsConnected( aPosition ) ) return true;
label = GetLabel( aPosition );
if( label && label->IsConnected( aPosition ) ) return true;
break;
case LAYER_NOTES: if( GetLine( aPosition ) ) return true;
break;
case LAYER_WIRE: if( GetItem( aPosition, 1, SCH_BUS_WIRE_ENTRY_T) ) return true;
if( GetItem( aPosition, 1, SCH_JUNCTION_T ) ) return true;
if( GetPin( aPosition, nullptr, true ) ) return true;
if( GetWire( aPosition ) ) return true;
label = GetLabel( aPosition, 1 );
if( label && label->IsConnected( aPosition ) ) return true;
sheetPin = GetSheetPin( aPosition );
if( sheetPin && sheetPin->IsConnected( aPosition ) ) return true;
break;
default: break; }
return false;}
void SCH_SCREEN::UpdateSymbolLinks( REPORTER* aReporter ){ wxCHECK_RET( Schematic(), "Cannot call SCH_SCREEN::UpdateSymbolLinks with no SCHEMATIC" );
wxString msg; std::unique_ptr< LIB_SYMBOL > libSymbol; std::vector<SCH_SYMBOL*> symbols; SYMBOL_LIB_TABLE* libs = Schematic()->Prj().SchSymbolLibTable();
// This will be a nullptr if an s-expression schematic is loaded.
SYMBOL_LIBS* legacyLibs = Schematic()->Prj().SchLibs();
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) ) symbols.push_back( static_cast<SCH_SYMBOL*>( item ) );
// Remove them from the R tree. There bounding box size may change.
for( SCH_SYMBOL* symbol : symbols ) Remove( symbol );
// Clear all existing symbol links.
clearLibSymbols();
for( SCH_SYMBOL* symbol : symbols ) { LIB_SYMBOL* tmp = nullptr; libSymbol.reset();
// If the symbol is already in the internal library, map the symbol to it.
auto it = m_libSymbols.find( symbol->GetSchSymbolLibraryName() );
if( ( it != m_libSymbols.end() ) ) { if( aReporter ) { msg.Printf( _( "Setting schematic symbol '%s %s' library identifier to '%s'." ), symbol->GetField( REFERENCE_FIELD )->GetText(), symbol->GetField( VALUE_FIELD )->GetText(), UnescapeString( symbol->GetLibId().Format() ) ); aReporter->ReportTail( msg, RPT_SEVERITY_INFO ); }
// Internal library symbols are already flattened so just make a copy.
symbol->SetLibSymbol( new LIB_SYMBOL( *it->second ) ); continue; }
if( !symbol->GetLibId().IsValid() ) { if( aReporter ) { msg.Printf( _( "Schematic symbol reference '%s' library identifier is not valid. " "Unable to link library symbol." ), UnescapeString( symbol->GetLibId().Format() ) ); aReporter->ReportTail( msg, RPT_SEVERITY_WARNING ); }
continue; }
// LIB_TABLE_BASE::LoadSymbol() throws an IO_ERROR if the library nickname
// is not found in the table so check if the library still exists in the table
// before attempting to load the symbol.
if( !libs->HasLibrary( symbol->GetLibId().GetLibNickname() ) && !legacyLibs ) { if( aReporter ) { msg.Printf( _( "Symbol library '%s' not found and no fallback cache library " "available. Unable to link library symbol." ), symbol->GetLibId().GetLibNickname().wx_str() ); aReporter->ReportTail( msg, RPT_SEVERITY_WARNING ); }
continue; }
if( libs->HasLibrary( symbol->GetLibId().GetLibNickname() ) ) { try { tmp = libs->LoadSymbol( symbol->GetLibId() ); } catch( const IO_ERROR& ioe ) { if( aReporter ) { msg.Printf( _( "I/O error %s resolving library symbol %s" ), ioe.What(), UnescapeString( symbol->GetLibId().Format() ) ); aReporter->ReportTail( msg, RPT_SEVERITY_ERROR ); } } }
if( !tmp && legacyLibs && legacyLibs->GetLibraryCount() ) { SYMBOL_LIB& legacyCacheLib = legacyLibs->back();
// It better be the cache library.
wxCHECK2( legacyCacheLib.IsCache(), continue );
wxString id = symbol->GetLibId().Format();
id.Replace( ':', '_' );
if( aReporter ) { msg.Printf( _( "Falling back to cache to set symbol '%s:%s' link '%s'." ), symbol->GetField( REFERENCE_FIELD )->GetText(), symbol->GetField( VALUE_FIELD )->GetText(), UnescapeString( id ) ); aReporter->ReportTail( msg, RPT_SEVERITY_WARNING ); }
tmp = legacyCacheLib.FindSymbol( id ); }
if( tmp ) { // We want a full symbol not just the top level child symbol.
libSymbol = tmp->Flatten(); libSymbol->SetParent();
m_libSymbols.insert( { symbol->GetSchSymbolLibraryName(), new LIB_SYMBOL( *libSymbol.get() ) } );
if( aReporter ) { msg.Printf( _( "Setting schematic symbol '%s %s' library identifier to '%s'." ), symbol->GetField( REFERENCE_FIELD )->GetText(), symbol->GetField( VALUE_FIELD )->GetText(), UnescapeString( symbol->GetLibId().Format() ) ); aReporter->ReportTail( msg, RPT_SEVERITY_INFO ); } } else { if( aReporter ) { msg.Printf( _( "No library symbol found for schematic symbol '%s %s'." ), symbol->GetField( REFERENCE_FIELD )->GetText(), symbol->GetField( VALUE_FIELD )->GetText() ); aReporter->ReportTail( msg, RPT_SEVERITY_ERROR ); } }
symbol->SetLibSymbol( libSymbol.release() ); }
// Changing the symbol may adjust the bbox of the symbol. This re-inserts the
// item with the new bbox
for( SCH_SYMBOL* symbol : symbols ) Append( symbol );}
void SCH_SCREEN::UpdateLocalLibSymbolLinks(){ std::vector<SCH_SYMBOL*> symbols;
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) ) symbols.push_back( static_cast<SCH_SYMBOL*>( item ) );
for( SCH_SYMBOL* symbol : symbols ) { // Changing the symbol may adjust the bbox of the symbol; remove and reinsert it afterwards.
m_rtree.remove( symbol );
auto it = m_libSymbols.find( symbol->GetSchSymbolLibraryName() );
LIB_SYMBOL* libSymbol = nullptr;
if( it != m_libSymbols.end() ) libSymbol = new LIB_SYMBOL( *it->second );
symbol->SetLibSymbol( libSymbol );
m_rtree.insert( symbol ); }}
void SCH_SCREEN::SetConnectivityDirty(){ for( SCH_ITEM* item : Items() ) item->SetConnectivityDirty( true );}
void SCH_SCREEN::Print( const RENDER_SETTINGS* aSettings ){ // Ensure links are up to date, even if a library was reloaded for some reason:
std::vector<SCH_ITEM*> junctions; std::vector<SCH_ITEM*> bitmaps; std::vector<SCH_ITEM*> other;
for( SCH_ITEM* item : Items() ) { if( item->IsMoving() || item->IsResized() ) continue;
if( item->Type() == SCH_JUNCTION_T ) junctions.push_back( item ); else if( item->Type() == SCH_BITMAP_T ) bitmaps.push_back( item ); else other.push_back( item ); }
/// Sort to ensure plot-order consistency with screen drawing
std::stable_sort( other.begin(), other.end(), []( const SCH_ITEM* a, const SCH_ITEM* b ) { if( a->Type() == b->Type() ) return a->GetLayer() > b->GetLayer();
return a->Type() < b->Type(); } );
for( SCH_ITEM* item : bitmaps ) item->Print( aSettings, VECTOR2I( 0, 0 ) );
for( SCH_ITEM* item : other ) item->PrintBackground( aSettings, VECTOR2I( 0, 0 ) );
for( SCH_ITEM* item : other ) item->Print( aSettings, VECTOR2I( 0, 0 ) );
for( SCH_ITEM* item : junctions ) item->Print( aSettings, VECTOR2I( 0, 0 ) );}
void SCH_SCREEN::Plot( PLOTTER* aPlotter ) const{ // Ensure links are up to date, even if a library was reloaded for some reason:
std::vector<SCH_ITEM*> junctions; std::vector<SCH_ITEM*> bitmaps; std::vector<SCH_SYMBOL*> symbols; std::vector<SCH_ITEM*> other;
for( SCH_ITEM* item : Items() ) { if( item->IsMoving() || item->IsResized() ) continue;
if( item->Type() == SCH_JUNCTION_T ) junctions.push_back( item ); else if( item->Type() == SCH_BITMAP_T ) bitmaps.push_back( item ); else other.push_back( item );
// Where the symbols overlap each other, we need to plot the text items a second
// time to get them on top of the overlapping element. This collection is in addition
// to the symbols already collected in `other`
if( item->Type() == SCH_SYMBOL_T ) { for( SCH_ITEM* sym : m_rtree.Overlapping( SCH_SYMBOL_T, item->GetBoundingBox() ) ) { if( sym != item ) { symbols.push_back( static_cast<SCH_SYMBOL*>( item ) ); break; } } } }
/// Sort to ensure plot-order consistency with screen drawing
std::sort( other.begin(), other.end(), []( const SCH_ITEM* a, const SCH_ITEM* b ) { if( a->Type() == b->Type() ) return a->GetLayer() > b->GetLayer();
return a->Type() > b->Type(); } );
int defaultPenWidth = aPlotter->RenderSettings()->GetDefaultPenWidth(); constexpr bool background = true;
// Bitmaps are drawn first to ensure they are in the background
// This is particularly important for the wxPostscriptDC (used in *nix printers) as
// the bitmap PS command clears the screen
for( const SCH_ITEM* item : bitmaps ) { aPlotter->SetCurrentLineWidth( std::max( item->GetPenWidth(), defaultPenWidth ) ); item->Plot( aPlotter, background ); }
for( const SCH_ITEM* item : other ) { aPlotter->SetCurrentLineWidth( std::max( item->GetPenWidth(), defaultPenWidth ) ); item->Plot( aPlotter, background ); }
for( const SCH_ITEM* item : other ) { aPlotter->SetCurrentLineWidth( std::max( item->GetPenWidth(), defaultPenWidth ) ); item->Plot( aPlotter, !background ); }
// After plotting the symbols as a group above (in `other`), we need to overplot the pins
// and symbols to ensure that they are always visible
for( const SCH_SYMBOL* sym :symbols ) { aPlotter->SetCurrentLineWidth( std::max( sym->GetPenWidth(), defaultPenWidth ) );
for( SCH_FIELD field : sym->GetFields() ) field.Plot( aPlotter, false );
sym->PlotPins( aPlotter ); }
for( const SCH_ITEM* item : junctions ) { aPlotter->SetCurrentLineWidth( std::max( item->GetPenWidth(), defaultPenWidth ) ); item->Plot( aPlotter, !background ); }}
void SCH_SCREEN::ClearDrawingState(){ for( SCH_ITEM* item : Items() ) item->ClearTempFlags();}
LIB_PIN* SCH_SCREEN::GetPin( const VECTOR2I& aPosition, SCH_SYMBOL** aSymbol, bool aEndPointOnly ) const{ SCH_SYMBOL* candidate = nullptr; LIB_PIN* pin = nullptr;
for( SCH_ITEM* item : Items().Overlapping( SCH_SYMBOL_T, aPosition ) ) { candidate = static_cast<SCH_SYMBOL*>( item );
if( aEndPointOnly ) { pin = nullptr;
if( !candidate->GetLibSymbolRef() ) continue;
for( pin = candidate->GetLibSymbolRef()->GetNextPin(); pin; pin = candidate->GetLibSymbolRef()->GetNextPin( pin ) ) { // Skip items not used for this part.
if( candidate->GetUnit() && pin->GetUnit() && ( pin->GetUnit() != candidate->GetUnit() ) ) continue;
if( candidate->GetConvert() && pin->GetConvert() && ( pin->GetConvert() != candidate->GetConvert() ) ) continue;
if( candidate->GetPinPhysicalPosition( pin ) == aPosition ) break; }
if( pin ) break; } else { pin = (LIB_PIN*) candidate->GetDrawItem( aPosition, LIB_PIN_T );
if( pin ) break; } }
if( pin && aSymbol ) *aSymbol = candidate;
return pin;}
SCH_SHEET_PIN* SCH_SCREEN::GetSheetPin( const VECTOR2I& aPosition ) const{ SCH_SHEET_PIN* sheetPin = nullptr;
for( SCH_ITEM* item : Items().Overlapping( SCH_SHEET_T, aPosition ) ) { SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
sheetPin = sheet->GetPin( aPosition );
if( sheetPin ) break; }
return sheetPin;}
size_t SCH_SCREEN::CountConnectedItems( const VECTOR2I& aPos, bool aTestJunctions ) const{ size_t count = 0;
for( const SCH_ITEM* item : Items().Overlapping( aPos ) ) { if( ( item->Type() != SCH_JUNCTION_T || aTestJunctions ) && item->IsConnected( aPos ) ) count++; }
return count;}
void SCH_SCREEN::ClearAnnotation( SCH_SHEET_PATH* aSheetPath, bool aResetPrefix ){
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
symbol->ClearAnnotation( aSheetPath, aResetPrefix ); }}
void SCH_SCREEN::EnsureAlternateReferencesExist(){ if( GetClientSheetPaths().size() <= 1 ) // No need for alternate reference
return;
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
// Add (when not existing) all sheet path entries
for( const SCH_SHEET_PATH& sheet : GetClientSheetPaths() ) symbol->AddSheetPathReferenceEntryIfMissing( sheet.Path() ); }}
void SCH_SCREEN::GetHierarchicalItems( std::vector<SCH_ITEM*>* aItems ) const{ for( SCH_ITEM* item : Items() ) { if( item->IsType( { SCH_SYMBOL_T, SCH_SHEET_T, SCH_LABEL_LOCATE_ANY_T } ) ) aItems->push_back( item ); }}
void SCH_SCREEN::GetSheets( std::vector<SCH_ITEM*>* aItems ) const{ for( SCH_ITEM* item : Items().OfType( SCH_SHEET_T ) ) aItems->push_back( item );
std::sort( aItems->begin(), aItems->end(), []( EDA_ITEM* a, EDA_ITEM* b ) -> bool { if( a->GetPosition().x == b->GetPosition().x ) { // Ensure deterministic sort
if( a->GetPosition().y == b->GetPosition().y ) return a->m_Uuid < b->m_Uuid;
return a->GetPosition().y < b->GetPosition().y; } else { return a->GetPosition().x < b->GetPosition().x; } } );}
void SCH_SCREEN::TestDanglingEnds( const SCH_SHEET_PATH* aPath, std::function<void( SCH_ITEM* )>* aChangedHandler ) const{ std::vector<DANGLING_END_ITEM> endPoints;
for( SCH_ITEM* item : Items() ) { if( item->IsConnectable() ) { endPoints.clear();
for( SCH_ITEM* overlapping : Items().Overlapping( item->GetBoundingBox() ) ) overlapping->GetEndPoints( endPoints );
if( item->UpdateDanglingState( endPoints, aPath ) ) { if( aChangedHandler ) (*aChangedHandler)( item ); } } }}
SCH_LINE* SCH_SCREEN::GetLine( const VECTOR2I& aPosition, int aAccuracy, int aLayer, SCH_LINE_TEST_T aSearchType ) const{ // an accuracy of 0 had problems with rounding errors; use at least 1
aAccuracy = std::max( aAccuracy, 1 );
for( SCH_ITEM* item : Items().Overlapping( aPosition, aAccuracy ) ) { 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 nullptr;}
SCH_LABEL_BASE* SCH_SCREEN::GetLabel( const VECTOR2I& aPosition, int aAccuracy ) const{ for( SCH_ITEM* item : Items().Overlapping( aPosition, aAccuracy ) ) { switch( item->Type() ) { case SCH_LABEL_T: case SCH_GLOBAL_LABEL_T: case SCH_HIER_LABEL_T: case SCH_DIRECTIVE_LABEL_T: if( item->HitTest( aPosition, aAccuracy ) ) return static_cast<SCH_LABEL_BASE*>( item );
break;
default: ; } }
return nullptr;}
void SCH_SCREEN::AddLibSymbol( LIB_SYMBOL* aLibSymbol ){ wxCHECK( aLibSymbol, /* void */ );
wxString libSymbolName = aLibSymbol->GetLibId().Format().wx_str();
auto it = m_libSymbols.find( libSymbolName );
if( it != m_libSymbols.end() ) { delete it->second; m_libSymbols.erase( it ); }
m_libSymbols[libSymbolName] = aLibSymbol;}
void SCH_SCREEN::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias ){ m_aliases.insert( aAlias );}
void SCH_SCREEN::SetLegacySymbolInstanceData(){ for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
// Add missing value and footprint instance data for legacy schematics.
for( const SYMBOL_INSTANCE_REFERENCE& instance : symbol->GetInstanceReferences() ) { symbol->AddHierarchicalReference( instance.m_Path, instance.m_Reference, instance.m_Unit ); } }}
#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( const SCH_ITEM* item : Items() ) item->Show( nestLevel + 1, os );
NestedSpace( nestLevel, os ) << "</" << GetClass().Lower().mb_str() << ">\n";}#endif
SCH_SCREENS::SCH_SCREENS( SCH_SHEET* aSheet ){ m_index = 0; buildScreenList( aSheet );}
SCH_SCREENS::~SCH_SCREENS(){}
SCH_SCREEN* SCH_SCREENS::GetFirst(){ m_index = 0;
if( m_screens.size() > 0 ) return m_screens[0];
return nullptr;}
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 nullptr;}
SCH_SHEET* SCH_SCREENS::GetSheet( unsigned int aIndex ) const{ if( aIndex < m_sheets.size() ) return m_sheets[ aIndex ];
return nullptr;}
void SCH_SCREENS::addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet ){ if( aScreen == nullptr ) return;
for( const SCH_SCREEN* screen : m_screens ) { if( screen == aScreen ) return; }
m_screens.push_back( aScreen ); m_sheets.push_back( aSheet );}
void SCH_SCREENS::buildScreenList( SCH_SHEET* aSheet ){ if( aSheet && aSheet->Type() == SCH_SHEET_T ) { SCH_SCREEN* screen = aSheet->GetScreen();
wxCHECK_RET( screen, "No screen for aSheet" );
addScreenToList( screen, aSheet );
for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) ) buildScreenList( static_cast<SCH_SHEET*>( item ) ); }}
void SCH_SCREENS::ClearAnnotationOfNewSheetPaths( SCH_SHEET_LIST& aInitialSheetPathList ){ SCH_SCREEN* first = GetFirst();
if( !first ) return;
SCHEMATIC* sch = first->Schematic();
wxCHECK_RET( sch, "Null schematic in SCH_SCREENS::ClearAnnotationOfNewSheetPaths" );
// Clear the annotation for symbols inside new sheetpaths not already in aInitialSheetList
SCH_SCREENS screensList( sch->Root() ); // The list of screens, shared by sheet paths
screensList.BuildClientSheetPathList(); // build the shared by sheet paths, by screen
// Search for new sheet paths, not existing in aInitialSheetPathList
// and existing in sheetpathList
for( SCH_SHEET_PATH& sheetpath : sch->GetSheets() ) { bool path_exists = false;
for( const SCH_SHEET_PATH& existing_sheetpath: aInitialSheetPathList ) { if( existing_sheetpath.Path() == sheetpath.Path() ) { path_exists = true; break; } }
if( !path_exists ) { // A new sheet path is found: clear the annotation corresponding to this new path:
SCH_SCREEN* curr_screen = sheetpath.LastScreen();
// Clear annotation and create the AR for this path, if not exists,
// when the screen is shared by sheet paths.
// Otherwise ClearAnnotation do nothing, because the F1 field is used as
// reference default value and takes the latest displayed value
curr_screen->EnsureAlternateReferencesExist(); curr_screen->ClearAnnotation( &sheetpath, false ); } }}
int SCH_SCREENS::ReplaceDuplicateTimeStamps(){ std::vector<SCH_ITEM*> items; int count = 0;
auto timestamp_cmp = []( const EDA_ITEM* a, const EDA_ITEM* b ) -> bool { return a->m_Uuid < b->m_Uuid; };
std::set<EDA_ITEM*, decltype( timestamp_cmp )> unique_stamps( timestamp_cmp );
for( SCH_SCREEN* screen : m_screens ) screen->GetHierarchicalItems( &items );
if( items.size() < 2 ) return 0;
for( EDA_ITEM* item : items ) { if( !unique_stamps.insert( item ).second ) { // Reset to fully random UUID. This may lose reference, but better to be
// deterministic about it rather than to have duplicate UUIDs with random
// side-effects.
const_cast<KIID&>( item->m_Uuid ) = KIID(); count++;
// @todo If the item is a sheet, we need to decend the heirarchy from the sheet
// and repace all instances of the changed UUID in sheet paths. Otherwise,
// all instance paths with the sheet's UUID will get clobbered.
} }
return count;}
void SCH_SCREENS::ClearEditFlags(){ for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) { for( SCH_ITEM* item : screen->Items() ) item->ClearEditFlags(); }}
void SCH_SCREENS::DeleteMarker( SCH_MARKER* aMarker ){ for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) { for( SCH_ITEM* item : screen->Items().OfType( SCH_MARKER_T ) ) { if( item == aMarker ) { screen->DeleteItem( item ); return; } } }}
void SCH_SCREENS::DeleteMarkers( enum MARKER_BASE::TYPEMARKER aMarkerType, int aErrorCode, bool aIncludeExclusions ){ for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) { std::vector<SCH_ITEM*> markers;
for( SCH_ITEM* item : screen->Items().OfType( SCH_MARKER_T ) ) { SCH_MARKER* marker = static_cast<SCH_MARKER*>( item ); std::shared_ptr<RC_ITEM>rcItem = marker->GetRCItem();
if( marker->GetMarkerType() == aMarkerType && ( aErrorCode == ERCE_UNSPECIFIED || rcItem->GetErrorCode() == aErrorCode ) && ( !marker->IsExcluded() || aIncludeExclusions ) ) { markers.push_back( item ); } }
for( SCH_ITEM* marker : markers ) screen->DeleteItem( marker ); }}
void SCH_SCREENS::DeleteAllMarkers( enum MARKER_BASE::TYPEMARKER aMarkerType, bool aIncludeExclusions ){ DeleteMarkers( aMarkerType, ERCE_UNSPECIFIED, aIncludeExclusions );}
void SCH_SCREENS::UpdateSymbolLinks( REPORTER* aReporter ){ for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) screen->UpdateSymbolLinks( aReporter );
SCH_SCREEN* first = GetFirst();
if( !first ) return;
SCHEMATIC* sch = first->Schematic();
wxCHECK_RET( sch, "Null schematic in SCH_SCREENS::UpdateSymbolLinks" );
SCH_SHEET_LIST sheets = sch->GetSheets();
// All of the library symbols have been replaced with copies so the connection graph
// pointers are stale.
if( sch->ConnectionGraph() ) sch->ConnectionGraph()->Recalculate( sheets, true );}
bool SCH_SCREENS::HasNoFullyDefinedLibIds(){ bool has_symbols = false;
for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) { for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item ); has_symbols = true;
if( !symbol->GetLibId().GetLibNickname().empty() ) return false; } }
// return true (i.e. has no fully defined symbol) only if at least one symbol is found
return has_symbols ? true : false;}
size_t SCH_SCREENS::GetLibNicknames( wxArrayString& aLibNicknames ){ for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) { for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item ); const UTF8& nickname = symbol->GetLibId().GetLibNickname();
if( !nickname.empty() && ( aLibNicknames.Index( nickname ) == wxNOT_FOUND ) ) aLibNicknames.Add( nickname ); } }
return aLibNicknames.GetCount();}
int SCH_SCREENS::ChangeSymbolLibNickname( const wxString& aFrom, const wxString& aTo ){ SCH_SCREEN* screen; int cnt = 0;
for( screen = GetFirst(); screen; screen = GetNext() ) { for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
if( symbol->GetLibId().GetLibNickname() != aFrom ) continue;
LIB_ID id = symbol->GetLibId(); id.SetLibNickname( aTo ); symbol->SetLibId( id ); cnt++; } }
return cnt;}
bool SCH_SCREENS::HasSchematic( const wxString& aSchematicFileName ){ for( const SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) { if( screen->GetFileName() == aSchematicFileName ) return true; }
return false;}
bool SCH_SCREENS::CanCauseCaseSensitivityIssue( const wxString& aSchematicFileName ) const{ wxString lhsLower; wxString rhsLower; wxFileName lhs; wxFileName rhs = aSchematicFileName;
wxCHECK( rhs.IsAbsolute(), false );
for( const SCH_SCREEN* screen : m_screens ) { lhs = screen->GetFileName();
if( lhs.GetPath() != rhs.GetPath() ) continue;
lhsLower = lhs.GetFullName().Lower(); rhsLower = rhs.GetFullName().Lower();
if( lhsLower == rhsLower && lhs.GetFullName() != rhs.GetFullName() ) return true; }
return false;}
void SCH_SCREENS::BuildClientSheetPathList(){ SCH_SCREEN* first = GetFirst();
if( !first ) return;
SCHEMATIC* sch = first->Schematic();
wxCHECK_RET( sch, "Null schematic in SCH_SCREENS::BuildClientSheetPathList" );
for( SCH_SCREEN* curr_screen = GetFirst(); curr_screen; curr_screen = GetNext() ) curr_screen->GetClientSheetPaths().clear();
for( SCH_SHEET_PATH& sheetpath : sch->GetSheets() ) { SCH_SCREEN* used_screen = sheetpath.LastScreen();
// Search for the used_screen in list and add this unique sheet path:
for( SCH_SCREEN* curr_screen = GetFirst(); curr_screen; curr_screen = GetNext() ) { if( used_screen == curr_screen ) { curr_screen->GetClientSheetPaths().push_back( sheetpath ); break; } } }}
void SCH_SCREENS::SetLegacySymbolInstanceData(){ for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) screen->SetLegacySymbolInstanceData();}
void SCH_SCREEN::MigrateSimModels(){ LOCALE_IO toggle;
// V6 schematics may specify model names in Value fields, which we don't do in V7.
// Migrate by adding an equivalent model for these symbols.
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item ); migrateSimModel( *symbol ); }}
void SCH_SCREEN::migrateSimModel( SCH_SYMBOL& aSymbol ){ if( aSymbol.FindField( SIM_MODEL::DEVICE_TYPE_FIELD ) || aSymbol.FindField( SIM_MODEL::TYPE_FIELD ) || aSymbol.FindField( SIM_MODEL::PINS_FIELD ) || aSymbol.FindField( SIM_MODEL::PARAMS_FIELD ) ) { // Has a V7 model field -- skip.
return; }
wxString prefix = aSymbol.GetPrefix(); wxString value = aSymbol.GetValueFieldText( true );
wxString spiceType; wxString spiceModel; wxString spiceLib; wxString pinMap;
if( aSymbol.FindField( wxT( "Spice_Primitive" ) ) || aSymbol.FindField( wxT( "Spice_Node_Sequence" ) ) || aSymbol.FindField( wxT( "Spice_Model" ) ) || aSymbol.FindField( wxT( "Spice_Netlist_Enabled" ) ) || aSymbol.FindField( wxT( "Spice_Lib_File" ) ) ) { if( SCH_FIELD* primitiveField = aSymbol.FindField( wxT( "Spice_Primitive" ) ) ) { spiceType = primitiveField->GetText(); aSymbol.RemoveField( wxT( "Spice_Primitive" ) ); }
if( SCH_FIELD* nodeSequenceField = aSymbol.FindField( wxT( "Spice_Node_Sequence" ) ) ) { const wxString delimiters( "{:,; }" ); const wxString& nodeSequence = nodeSequenceField->GetText();
if( nodeSequence != "" ) { wxStringTokenizer tkz( nodeSequence, delimiters );
for( long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber ) { long symbolPinNumber = 1; tkz.GetNextToken().ToLong( &symbolPinNumber );
if( modelPinNumber != 1 ) pinMap.Append( " " );
pinMap.Append( wxString::Format( "%ld=%ld", symbolPinNumber, modelPinNumber ) ); } }
aSymbol.RemoveField( wxT( "Spice_Node_Sequence" ) ); }
if( SCH_FIELD* modelField = aSymbol.FindField( wxT( "Spice_Model" ) ) ) { spiceModel = modelField->GetText(); aSymbol.RemoveField( wxT( "Spice_Model" ) ); } else { spiceModel = aSymbol.FindField( wxT( "Value" ) )->GetText(); }
if( SCH_FIELD* netlistEnabledField = aSymbol.FindField( wxT( "Spice_Netlist_Enabled" ) ) ) { wxString netlistEnabled = netlistEnabledField->GetText().Lower();
if( netlistEnabled.StartsWith( wxT( "0" ) ) || netlistEnabled.StartsWith( wxT( "n" ) ) || netlistEnabled.StartsWith( wxT( "f" ) ) ) { SCH_FIELD enableField( VECTOR2I( 0, 0 ), aSymbol.GetFieldCount(), &aSymbol, SIM_MODEL::ENABLE_FIELD ); } }
if( SCH_FIELD* libFileField = aSymbol.FindField( wxT( "Spice_Lib_File" ) ) ) { spiceLib = libFileField->GetText(); aSymbol.RemoveField( wxT( "Spice_Lib_File" ) ); } } else if( prefix == wxT( "V" ) || prefix == wxT( "I" ) ) { spiceModel = value; } else { // Auto convert some legacy fields used in the middle of 7.0 development...
if( SCH_FIELD* legacyDevice = aSymbol.FindField( wxT( "Sim_Type" ) ) ) { legacyDevice->SetName( SIM_MODEL::TYPE_FIELD ); }
if( SCH_FIELD* legacyDevice = aSymbol.FindField( wxT( "Sim_Device" ) ) ) { legacyDevice->SetName( SIM_MODEL::DEVICE_TYPE_FIELD ); }
if( SCH_FIELD* legacyPins = aSymbol.FindField( wxT( "Sim_Pins" ) ) ) { // Migrate pins from array of indexes to name-value-pairs
wxArrayString pinIndexes; wxString pins;
wxStringSplit( legacyPins->GetText(), pinIndexes, ' ' );
if( SIM_MODEL_IDEAL::InferSimParams( prefix, value ).length() ) { if( pinIndexes[0] == wxT( "2" ) ) pins = "1=- 2=+"; else pins = "1=+ 2=-"; } else { for( unsigned ii = 0; ii < pinIndexes.size(); ++ii ) { if( ii > 0 ) pins.Append( wxS( " " ) );
pins.Append( wxString::Format( wxT( "%u=%s" ), ii + 1, pinIndexes[ ii ] ) ); } }
legacyPins->SetName( SIM_MODEL::PINS_FIELD ); legacyPins->SetText( pins ); }
if( SCH_FIELD* legacyPins = aSymbol.FindField( wxT( "Sim_Params" ) ) ) { legacyPins->SetName( SIM_MODEL::PARAMS_FIELD ); }
return; }
// Insert a plaintext model as a substitute.
SCH_FIELD deviceTypeField( VECTOR2I( 0, 0 ), aSymbol.GetFieldCount(), &aSymbol, SIM_MODEL::DEVICE_TYPE_FIELD ); deviceTypeField.SetText( SIM_MODEL::DeviceInfo( SIM_MODEL::DEVICE_T::SPICE ).fieldValue ); aSymbol.AddField( deviceTypeField );
SCH_FIELD paramsField( VECTOR2I( 0, 0 ), aSymbol.GetFieldCount(), &aSymbol, SIM_MODEL::PARAMS_FIELD ); paramsField.SetText( wxString::Format( "type=\"%s\" model=\"%s\" lib=\"%s\"", spiceType, spiceModel, spiceLib ) ); aSymbol.AddField( paramsField );
// Legacy models by default get linear pin mapping.
if( pinMap != "" ) { SCH_FIELD pinsField( VECTOR2I( 0, 0 ), aSymbol.GetFieldCount(), &aSymbol, SIM_MODEL::PINS_FIELD );
pinsField.SetText( pinMap ); aSymbol.AddField( pinsField ); } else { wxString pins;
for( unsigned ii = 0; ii < aSymbol.GetLibPins().size(); ++ii ) { if( ii > 0 ) pins.Append( wxS( " " ) );
pins.Append( wxString::Format( wxT( "%u=%u" ), ii + 1, ii + 1 ) ); }
SCH_FIELD pinsField( VECTOR2I( 0, 0 ), aSymbol.GetFieldCount(), &aSymbol, SIM_MODEL::PINS_FIELD ); pinsField.SetText( pins ); aSymbol.AddField( pinsField ); }}
|