Use legacy schematic plugin for loading schematics in all code paths.

Use the legacy plugin schematic loader in the sheet edit and append
schematic code paths.

Check for fully qualified LIB_ID objects (must have library nickname)
when loading existing schematics when edit sheets.

Rewrite append schematic feature to handle import issues rather than
change the name and file name of all of the sheets in the imported
schematic.  This includes the following:
- Load the schematic into a temporary SCH_SHEET object.
- Make sure the imported schematic does not cause any hierarchy
  recursion issues.
- Verify the imported schematic uses fully qualified #LIB_ID objects
  (symbol library table).
- Check to see if any symbol libraries need to be added to the current
  project's symbol library table.  This includes:
- Check if the symbol library already exists in the project or global
  symbol library table.
- Convert symbol library URLS that use the ${KIPRJMOD} environment
  variable to absolute paths.  ${KIPRJMOD} will not be the same for
  this project.
- Check for duplicate symbol library nicknames and change the new symbol
  library nickname to prevent library name clashes.
- Update all schematic symbol LIB_ID object library nicknames when the
  library nickname was changed to prevent clashes.
- Check for duplicate sheet names which is illegal and automatically
  rename any duplicate sheets in the imported schematic.
- Clear all of the annotation in the imported schematic to prevent
  clashes.
- Append the objects from the temporary sheet to the current page.
- Replace any duplicate time stamps.
- Refresh the symbol library links.

Add support code to SCH_SCREEN object to assist with schematic import.

Doxygen comment cleaning.

Fixes lp:1731760

https://bugs.launchpad.net/kicad/+bug/1731760
This commit is contained in:
Wayne Stambaugh 2017-11-17 12:00:04 -05:00
parent b6884d3423
commit e97ce6ee4f
5 changed files with 681 additions and 483 deletions

View File

@ -77,7 +77,7 @@ private:
TITLE_BLOCK m_titles;
/// Origin of the auxilliary axis, which is used in exports mostly, but not yet in EESCHEMA
/// Origin of the auxiliary axis, which is used in exports mostly, but not yet in EESCHEMA
wxPoint m_aux_origin;
DLIST< SCH_ITEM > m_drawList; ///< Object list for the screen.
@ -86,10 +86,9 @@ private:
///< will trigger ResolveAll().
/**
* Function addConnectedItemsToBlock
* add items connected at \a aPosition to the block pick list.
* Add items connected at \a aPosition to the block pick list.
* <p>
* This method tests all connectible unselected items in the screen that are connected to
* This method tests all connectable unselected items in the screen that are connected to
* \a aPosition and adds them to the block selection pick list. This is used when a block
* drag is being performed to ensure connections to items in the block are not lost.
*</p>
@ -132,8 +131,7 @@ public:
int GetRefCount() const { return m_refCount; }
/**
* Function GetDrawItems().
* @return - A pointer to the first item in the linked list of draw items.
* @return A pointer to the first item in the linked list of draw items.
*/
SCH_ITEM* GetDrawItems() const { return m_drawList.begin(); }
@ -144,8 +142,19 @@ public:
}
/**
* Function Append
* adds \a aList of SCH_ITEM objects to the list for draw items for the sheet.
* Copy the contents of \a aScreen into this #SCH_SCREEN object.
*
* @warning The contents of \a Screen cannot contain any duplicate sheet names or any
* hierarchy recursion issues or bad things will happen.
*
* @param aScreen is the screen to append to this one.
* @return false if there are any duplicate sheet names or any hierarchy recursion issues the
* calling this method or KiCad will crash.
*/
void Append( SCH_SCREEN* aScreen );
/**
* Add \a aList of SCH_ITEM objects to the list for draw items for the sheet.
*
* @param aList A reference to a #DLIST containing the #SCH_ITEM to add to the sheet.
*/
@ -156,22 +165,21 @@ public:
}
/**
* Function GetCurItem
* returns the currently selected SCH_ITEM, overriding BASE_SCREEN::GetCurItem().
* Return the currently selected SCH_ITEM, overriding BASE_SCREEN::GetCurItem().
*
* @return SCH_ITEM* - the one selected, or NULL.
*/
SCH_ITEM* GetCurItem() const { return (SCH_ITEM*) BASE_SCREEN::GetCurItem(); }
/**
* Function SetCurItem
* sets the currently selected object, m_CurrentItem.
* Sets the currently selected object, m_CurrentItem.
*
* @param aItem Any object derived from SCH_ITEM
*/
void SetCurItem( SCH_ITEM* aItem ) { BASE_SCREEN::SetCurItem( (EDA_ITEM*) aItem ); }
/**
* Function Clear
* deletes all draw items and clears the project settings.
* Delete all draw items and clears the project settings.
*/
void Clear();
@ -183,8 +191,8 @@ public:
void FreeDrawList();
/**
* Function GetItem
* checks \a aPosition within a distance of \a aAccuracy for items of type \a aFilter.
* Check \a aPosition within a distance of \a aAccuracy for items of type \a aFilter.
*
* @param aPosition Position in drawing units.
* @param aAccuracy The maximum distance within \a Position to check for an item.
* @param aType The type of item to find or #NOT_USED to find any item type.
@ -210,11 +218,11 @@ public:
void UpdateSymbolLinks( bool aForce = false );
/**
* Function Draw
* draws all the items in the screen to \a aCanvas.
* note: this function is useful only for schematic.
* library editor and library viewer do not use a draw list, and therefore
* draws nothing
* Draw all the items in the screen to \a aCanvas.
*
* @note This function is useful only for schematic. The library editor and library viewer
* do not use a draw list and therefore draws nothing.
*
* @param aCanvas The canvas item to draw on.
* @param aDC The device context to draw on.
* @param aDrawMode The drawing mode.
@ -224,19 +232,17 @@ public:
COLOR4D aColor = COLOR4D::UNSPECIFIED );
/**
* Function Plot
* plots all the schematic objects to \a aPlotter.
* note: this function is useful only for schematic.
* library editor and library viewer do not use a draw list, and therefore
* plots nothing
* Plot all the schematic objects to \a aPlotter.
*
* @note This function is useful only for schematic. The library editor and library viewer
* do not use a draw list and therefore plots nothing.
*
* @param aPlotter The plotter object to plot to.
*/
void Plot( PLOTTER* aPlotter );
/**
* Function Remove
* removes \a aItem from the schematic associated with this screen.
* Remove \a aItem from the schematic associated with this screen.
*
* @note The removed item is not deleted. It is only unlinked from the item list.
* @param aItem Item to be removed from schematic.
@ -244,10 +250,11 @@ public:
void Remove( SCH_ITEM* aItem );
/**
* Function DeleteItem
* removes \a aItem from the linked list and deletes the object. If \a aItem is
* is a schematic sheet label, it is removed from the screen associated with the
* sheet that contains the label to be deleted.
* Removes \a aItem from the linked list and deletes the object.
*
* If \a aItem is a schematic sheet label, it is removed from the screen associated with
* the sheet that contains the label to be deleted.
*
* @param aItem The schematic object to be deleted from the screen.
*/
void DeleteItem( SCH_ITEM* aItem );
@ -255,8 +262,7 @@ public:
bool CheckIfOnDrawList( SCH_ITEM* st );
/**
* Function SchematicCleanUp
* performs routine schematic cleaning including breaking wire and buses and
* Perform routine schematic cleaning including breaking wire and buses and
* deleting identical objects superimposed on top of each other.
*
* @return True if any schematic clean up was performed.
@ -264,46 +270,44 @@ public:
bool SchematicCleanUp();
/**
* Function TestDanglingEnds
* tests all of the connectible objects in the schematic for unused connection points.
* Test all of the connectable objects in the schematic for unused connection points.
*
* @return True if any connection state changes were made.
*/
bool TestDanglingEnds();
/**
* Function ExtractWires
* extracts the old wires, junctions and buses. If \a aCreateCopy is true, replace
* extracted items with a copy of the original. Old items are to be put in undo list,
* and the new ones can be modified by clean up safely. If an abort draw segmat command
* is made, the old wires must be put back into #m_drawList, and the copies must be
* deleted. This is because previously stored undo commands can handle pointers on wires
* or buses, and we do not delete wires or buses, we must put them in undo list.
* Extracts the old wires, junctions and buses.
*
* Because cleanup deletes and/or modify bus and wires, it is easier is to put
* If \a aCreateCopy is true, replace extracted items with a copy of the original. Old
* items are to be put in undo list and the new ones can be modified by clean up safely.
* If an abort draw segment command is made, the old wires must be put back into #m_drawList,
* and the copies must be deleted. This is because previously stored undo commands can
* handle pointers on wires or buses, and we do not delete wires or buses, we must put them
* in undo list. Because cleanup deletes and/or modify bus and wires, it is easier is to put
* all the existing wires in undo list and use a new copy of wires for cleanup.
*/
void ExtractWires( DLIST< SCH_ITEM >& aList, bool aCreateCopy );
/**
* Function ReplaceWires
* replaces all of the wires, buses, and junctions in the screen with \a aWireList.
* Replace all of the wires, buses, and junctions in the screen with \a aWireList.
*
* @param aWireList List of wires to replace the existing wires with.
*/
void ReplaceWires( DLIST< SCH_ITEM >& aWireList );
/**
* Function MarkConnections
* add all wires and junctions connected to \a aSegment which are not connected any
* Add all wires and junctions connected to \a aSegment which are not connected any
* component pin to \a aItemList.
*
* @param aSegment The segment to test for connections.
*/
void MarkConnections( SCH_LINE* aSegment );
/**
* Functions GetConnection
* adds all of the wires and junctions to \a aList that make up a connection to the
* Adds all of the wires and junctions to \a aList that make up a connection to the
* object at \a aPosition.
*
* @param aPosition The position of the first connection object in drawing units.
* @param aList The pick list to add the connect item to.
* @param aFullConnection If true all the objects that make up this connection are
@ -314,18 +318,18 @@ public:
int GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aList, bool aFullConnection );
/**
* Function BreakSegment
* checks every wire and bus for a intersection at \a aPoint and break into two segments
* Checks every wire and bus for a intersection at \a aPoint and break into two segments
* at \a aPoint if an intersection is found.
*
* @param aPoint Test this point for an intersection.
* @return True if any wires or buses were broken.
*/
bool BreakSegment( const wxPoint& aPoint );
/**
* Function BreakSegmentsOnJunctions
* tests all junctions and bus entries in the schematic for intersections with wires and
* Tests all junctions and bus entries in the schematic for intersections with wires and
* buses and breaks any intersections into multiple segments.
*
* @return True if any wires or buses were broken.
*/
bool BreakSegmentsOnJunctions();
@ -335,11 +339,12 @@ public:
// use BASE_SCREEN::PushCommandToRedoList( PICKED_ITEMS_LIST* aItem )
/**
* Function ClearUndoORRedoList
* free the undo or redo list from List element
* Wrappers are deleted.
* data pointed by wrappers are deleted if not in use in schematic
* i.e. when they are copy of a schematic item or they are no more in use (DELETED)
* Free the undo or redo list from \a aList element.
*
* - Wrappers are deleted.
* - data pointed by wrappers are deleted if not in use in schematic
* i.e. when they are copy of a schematic item or they are no more in use (DELETED)
*
* @param aList = the UNDO_REDO_CONTAINER to clear
* @param aItemCount = the count of items to remove. < 0 for all items
* items are removed from the beginning of the list.
@ -348,8 +353,7 @@ public:
virtual void ClearUndoORRedoList( UNDO_REDO_CONTAINER& aList, int aItemCount = -1 ) override;
/**
* Function Save
* writes the data structures for this object out to \a aFile in "*.sch" format.
* Writes the data structures for this object out to \a aFile in "*.sch" format.
*
* @param aFile The FILE to write to.
* @return bool - true if success writing else false.
@ -364,8 +368,7 @@ public:
int CountConnectedItems( const wxPoint& aPos, bool aTestJunctions ) const;
/**
* Function IsJunctionNeeded
* tests if a junction is required for the items at \a aPosition on the screen.
* Test if a junction is required for the items at \a aPosition on the screen.
* <p>
* A junction is required at \a aPosition if the following criteria are satisfied:
* <ul>
@ -382,8 +385,7 @@ public:
bool IsJunctionNeeded( const wxPoint& aPosition );
/**
* Function IsTerminalPoint
* tests if \a aPosition is a connection point on \a aLayer.
* Test if \a aPosition is a connection point on \a aLayer.
*
* @param aPosition Position to test.
* @param aLayer The layer type to test against. Valid layer types are #LAYER_NOTES,
@ -393,8 +395,8 @@ public:
bool IsTerminalPoint( const wxPoint& aPosition, int aLayer );
/**
* Function GetPin
* test the screen for a component pin item at \a aPosition.
* Test the screen for a component pin item at \a aPosition.
*
* @param aPosition Position to test.
* @param aComponent The component if a pin was found, otherwise NULL.
* @param aEndPointOnly Set to true to test if \a aPosition is the connection
@ -405,41 +407,39 @@ public:
bool aEndPointOnly = false ) const;
/**
* Function GetSheet
* returns a sheet object pointer that is named \a aName.
* Returns a sheet object pointer that is named \a aName.
*
* @note The screen hierarchy is not descened.
* @note The screen hierarchy is not descend.
* @param aName is the case insensitive name of the sheet.
* @return A pointer to the SCH_SHEET object found or NULL.
*/
SCH_SHEET* GetSheet( const wxString& aName );
/**
* Function GetSheetLabel
* test the screen if \a aPosition is a sheet label object.
* Test the screen if \a aPosition is a sheet label object.
*
* @param aPosition The position to test.
* @return The sheet label object if found otherwise NULL.
*/
SCH_SHEET_PIN* GetSheetLabel( const wxPoint& aPosition );
/**
* Function ClearAnnotation
* clears the annotation for the components in \a aSheetPath on the screen.
* Clear the annotation for the components in \a aSheetPath on the screen.
*
* @param aSheetPath The sheet path of the component annotation to clear. If NULL then
* the entire hierarchy is cleared.
*/
void ClearAnnotation( SCH_SHEET_PATH* aSheetPath );
/**
* Function GetHierarchicalItems
* adds all schematic sheet and component object in the screen to \a aItems.
* Add all schematic sheet and component objects in the screen to \a aItems.
*
* @param aItems Hierarchical item list to fill.
*/
void GetHierarchicalItems( EDA_ITEMS& aItems );
/**
* Function GetNode
* returns all the items at \a aPosition that form a node.
* Return all the items at \a aPosition that form a node.
*
* @param aPosition The wxPoint to test for node items.
* @param aList A #EDA_ITEMS container to place the items found.
@ -448,8 +448,7 @@ public:
int GetNode( const wxPoint& aPosition, EDA_ITEMS& aList );
/**
* Function GetWireOrBus
* returns a wire or bus item located at \a aPosition.
* Return a wire or bus item located at \a aPosition.
*
* @param aPosition The wxPoint to test for node items.
* @return The SCH_LINE* of the wire or bus item found at \a aPosition or NULL if item not
@ -458,8 +457,7 @@ public:
SCH_LINE* GetWireOrBus( const wxPoint& aPosition );
/**
* Function GetLine
* returns a line item located at \a aPosition.
* Return a line item located at \a aPosition.
*
* @param aPosition The wxPoint to test for a line item.
* @param aAccuracy Amount to inflate the item hit test bounding box.
@ -484,8 +482,7 @@ public:
}
/**
* Function GetLabel
* returns a label item located at \a aPosition.
* Return a label item located at \a aPosition.
*
* @param aPosition The wxPoint to test for label items.
* @param aAccuracy Amount to inflate the item hit test bounding box.
@ -495,8 +492,7 @@ public:
SCH_TEXT* GetLabel( const wxPoint& aPosition, int aAccuracy = 0 );
/**
* Function SetFootprintField
* searches screen for a component with \a aReference and set the footprint field to
* Search this screen for a symbol with \a aReference and set the footprint field to
* \a aFootPrint if found.
*
* @param aSheetPath The sheet path used to look up the reference designator.
@ -509,16 +505,16 @@ public:
const wxString& aFootPrint, bool aSetVisible );
/**
* Function SelectBlockItems
* creates a list of items found when a block command is initiated. The items selected
* depend on the block command. If the drag block command is issued, than any items
* connected to items in the block are also selected.
* Create a list of items found when a block command is initiated.
*
* The items selected depend on the block command. If the drag block command is issued,
* then any items connected to items in the block are also selected.
*/
void SelectBlockItems();
/**
* Function UpdatePickList
* adds all the items in the screen within the block selection rectangle to the pick list.
* Add all the items in the screen within the block selection rectangle to the pick list.
*
* @return The number of items in the pick list.
*/
int UpdatePickList();
@ -541,7 +537,7 @@ private:
unsigned int m_index;
public:
SCH_SCREENS();
SCH_SCREENS( SCH_SHEET* aSheet = NULL );
~SCH_SCREENS();
int GetCount() const { return m_screens.size(); }
SCH_SCREEN* GetFirst();
@ -612,9 +608,26 @@ public:
*/
bool HasNoFullyDefinedLibIds();
/**
* Fetch all of the symbol library nickames into \a aLibNicknames.
*
* @param aLibNicknames is the array to populate with all of the unique library nicknames.
* @return the number of symbol library nicknames found.
*/
size_t GetLibNicknames( wxArrayString& aLibNicknames );
/**
* Change all of the symbol library nicknames.
*
* @param aFrom the current symbol library name to change.
* @param aTo the new symbol library name.
* @return the number of symbol library nicknames that were changed.
*/
int ChangeSymbolLibNickname( const wxString& aFrom, const wxString& aTo );
private:
void AddScreenToList( SCH_SCREEN* aScreen );
void BuildScreenList( EDA_ITEM* aItem );
void addScreenToList( SCH_SCREEN* aScreen );
void buildScreenList( SCH_SHEET* aSheet);
};
#endif /* CLASS_SCREEN_H */

View File

@ -193,7 +193,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// This is for python:
if( aFileSet.size() != 1 )
{
UTF8 msg = StrPrintf( "Eeschema:%s() takes only a single filename", __func__ );
UTF8 msg = StrPrintf( "Eeschema:%s() takes only a single filename.", __func__ );
DisplayError( this, msg );
return false;
}
@ -372,8 +372,9 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
bool SCH_EDIT_FRAME::AppendOneEEProject()
bool SCH_EDIT_FRAME::AppendSchematic()
{
wxString msg;
wxString fullFileName;
SCH_SCREEN* screen = GetScreen();
@ -423,101 +424,216 @@ bool SCH_EDIT_FRAME::AppendOneEEProject()
wxLogDebug( wxT( "Importing schematic " ) + fullFileName );
// Keep trace of the last item in list.
// New items will be loaded after this one.
SCH_ITEM* bs = screen->GetDrawItems();
// Load the schematic into a temporary sheet.
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET );
if( bs )
newSheet->SetFileName( fullFileName );
try
{
while( bs->Next() )
bs = bs->Next();
pi->Load( fullFileName, &Kiway(), newSheet.get() );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error occurred loading schematic file '%s'." ), fullFileName );
DisplayErrorMessage( this, msg, ioe.What() );
msg.Printf( _( "Failed to load schematic '%s'" ), fullFileName );
AppendMsgPanel( wxEmptyString, msg, CYAN );
return false;
}
// load the project
bool success = LoadOneEEFile( screen, fullFileName, true );
// Make sure any new sheet changes do not cause any recursion issues.
SCH_SHEET_LIST hierarchy( g_RootSheet ); // This is the schematic sheet hierarchy.
SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the import.
if( success )
wxFileName destFile = screen->GetFileName();
if( destFile.IsRelative() )
destFile.MakeAbsolute( Prj().GetProjectPath() );
if( hierarchy.TestForRecursion( sheetHierarchy, destFile.GetFullPath( wxPATH_UNIX ) ) )
{
// the new loaded items need cleaning to avoid duplicate parameters
// which should be unique (ref and time stamp).
// Clear ref and set a new time stamp for new items
if( bs == NULL )
bs = screen->GetDrawItems();
else
bs = bs->Next();
msg.Printf( _( "The sheet changes cannot be made because the destination sheet already "
"has the sheet <%s> or one of it's subsheets as a parent somewhere in "
"the schematic hierarchy." ),
destFile.GetFullPath() );
DisplayError( this, msg );
return false;
}
while( bs )
wxArrayString names;
// Make sure the imported schematic has been remapped to the symbol library table.
SCH_SCREENS newScreens( newSheet.get() ); // All screens associated with the import.
if( newScreens.HasNoFullyDefinedLibIds() )
{
if( !IsOK( this,
"This schematic has not been remapped to the symbol library table. "
"Therefore, all of the library symbol links will be broken. Do you "
"want to continue?" ) )
return false;
}
else
{
// If there are symbol libraries in the imported schematic that are not in the
// symbol library table of this project, there could be a lot of broken symbol
// library links. Attempt to add the missing libraries to the project symbol
// library table.
newScreens.GetLibNicknames( names );
wxArrayString newLibNames;
for( auto name : names )
{
SCH_ITEM* nextbs = bs->Next();
if( !Prj().SchSymbolLibTable()->HasLibrary( name ) )
newLibNames.Add( name );
}
// To avoid issues with the current hieratchy,
// do not load included sheets files and give new filenames
// and new sheet names.
// There are many tricky cases (loops, creation of complex hierarchies
// with duplicate file names, duplicate sheet names...)
// So the included sheets names are renamed if existing,
// and filenames are just renamed to avoid loops and
// creation of complex hierarchies.
// If someone want to change it for a better append function, remember
// these cases need work to avoid issues.
if( bs->Type() == SCH_SHEET_T )
wxFileName symLibTableFn( fn.GetPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
if( !newLibNames.IsEmpty() && symLibTableFn.Exists() && symLibTableFn.IsFileReadable() )
{
SYMBOL_LIB_TABLE table;
try
{
SCH_SHEET * sheet = (SCH_SHEET *) bs;
time_t newtimestamp = GetNewTimeStamp();
sheet->SetTimeStamp( newtimestamp );
// Check for existing subsheet name in the current sheet
wxString tmp = sheet->GetName();
sheet->SetName( wxEmptyString );
const SCH_SHEET* subsheet = GetScreen()->GetSheet( tmp );
if( subsheet )
sheet->SetName( wxString::Format( wxT( "Sheet%8.8lX" ), (long) newtimestamp ) );
else
sheet->SetName( tmp );
sheet->SetFileName( wxString::Format( wxT( "file%8.8lX.sch" ),
(long) newtimestamp ) );
SCH_SCREEN* new_screen = new SCH_SCREEN( &Kiway() );
new_screen->SetMaxUndoItems( m_UndoRedoCountMax );
sheet->SetScreen( new_screen );
sheet->GetScreen()->SetFileName( sheet->GetFileName() );
table.Load( symLibTableFn.GetFullPath() );
}
// clear annotation and init new time stamp for the new components
else if( bs->Type() == SCH_COMPONENT_T )
catch( const IO_ERROR& ioe )
{
( (SCH_COMPONENT*) bs )->SetTimeStamp( GetNewTimeStamp() );
( (SCH_COMPONENT*) bs )->ClearAnnotation( NULL );
// Clear flags, which are set by these previous modifications:
bs->ClearFlags();
msg.Printf( _( "An error occurred loading the symbol library table '%s'." ),
symLibTableFn.GetFullPath() );
DisplayErrorMessage( NULL, msg, ioe.What() );
}
bs = nextbs;
if( !table.IsEmpty() )
{
for( auto libName : newLibNames )
{
if( !table.HasLibrary( libName ) )
continue;
// Don't expand environment variable because KIPRJMOD will not be correct
// for a different project.
wxString uri = table.GetFullURI( libName, false );
wxFileName newLib;
if( uri.Contains( "${KIPRJMOD}" ) )
{
newLib.SetPath( fn.GetPath() );
newLib.SetFullName( uri.AfterLast( '}' ) );
uri = newLib.GetFullPath();
}
else
{
uri = table.GetFullURI( libName );
}
// Add the library from the imported project to the current project
// symbol library table.
const SYMBOL_LIB_TABLE_ROW* row = table.FindRow( libName );
wxCHECK2_MSG( row, continue, "Library '" + libName +
"' missing from symbol library table '" +
symLibTableFn.GetFullPath() + "'." );
wxString newLibName = libName;
int libNameCnt = 1;
// Rename the imported symbol library if it already exists.
while( Prj().SchSymbolLibTable()->HasLibrary( newLibName ) )
{
newLibName = wxString::Format( "%s%d", libName, libNameCnt );
}
SYMBOL_LIB_TABLE_ROW* newRow = new SYMBOL_LIB_TABLE_ROW( newLibName,
uri,
row->GetType(),
row->GetOptions(),
row->GetDescr() );
Prj().SchSymbolLibTable()->InsertRow( newRow );
if( libName != newLibName )
newScreens.ChangeSymbolLibNickname( libName, newLibName );
}
}
}
}
// Check for duplicate sheet names in the current page.
wxArrayString duplicateSheetNames;
SCH_TYPE_COLLECTOR sheets;
sheets.Collect( screen->GetDrawItems(), SCH_COLLECTOR::SheetsOnly );
for( int i = 0; i < sheets.GetCount(); ++i )
{
if( newSheet->GetScreen()->GetSheet( ( ( SCH_SHEET* ) sheets[i] )->GetName() ) )
duplicateSheetNames.Add( ( ( SCH_SHEET* ) sheets[i] )->GetName() );
}
if( !duplicateSheetNames.IsEmpty() )
{
msg.Printf( "Duplicate sheet names exist on the current page. Do you want to "
"automatically rename the duplicate sheet names?" );
if( !IsOK( this, msg ) )
return false;
}
SCH_SCREEN* newScreen = newSheet->GetScreen();
wxCHECK_MSG( newScreen, false, "No screen defined for imported sheet." );
for( auto duplicateName : duplicateSheetNames )
{
SCH_SHEET* renamedSheet = newScreen->GetSheet( duplicateName );
wxCHECK2_MSG( renamedSheet, continue,
"Sheet " + duplicateName + " not found in imported schematic." );
time_t newtimestamp = GetNewTimeStamp();
renamedSheet->SetTimeStamp( newtimestamp );
renamedSheet->SetName( wxString::Format( "Sheet%8.8lX", (long) newtimestamp ) );
}
// Clear all annotation in the imported schematic to prevent clashes with existing annotation.
newScreens.ClearAnnotation();
// It is finally save to add the imported schematic.
screen->Append( newScreen );
SCH_SCREENS allScreens;
allScreens.ReplaceDuplicateTimeStamps();
OnModify();
// redraw base screen (ROOT) if necessary
SCH_SCREENS screens( GetCurrentSheet().Last() );
screens.UpdateSymbolLinks( true );
GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
Zoom_Automatique( false );
SetSheetNumberAndCount();
m_canvas->Refresh( true );
return success;
return true;
}
void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
{
wxString msg = _( "This operation cannot be undone. "
"Besides, take into account that hierarchical sheets will not be appended.\n\n"
"Do you want to save the current document before proceeding?" );
if( GetScreen() && GetScreen()->IsModified() )
{
wxString msg = _( "This operation cannot be undone.\n\n"
"Do you want to save the current document before proceeding?" );
if( IsOK( this, msg ) )
OnSaveProject( event );
if( IsOK( this, msg ) )
OnSaveProject( event );
}
AppendOneEEProject();
AppendSchematic();
}
@ -552,10 +668,7 @@ void SCH_EDIT_FRAME::OnSaveProject( wxCommandEvent& aEvent )
if( !fn.IsDirWritable() )
{
wxString msg = wxString::Format( _(
"Directory '%s' is not writable" ),
GetChars( fn.GetPath() )
);
wxString msg = wxString::Format( _( "Directory '%s' is not writable." ), fn.GetPath() );
DisplayError( this, msg );
return;
@ -676,10 +789,10 @@ bool SCH_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
wxString msg;
msg.Printf( _( "Error loading schematic file '%s'.\n%s" ),
GetChars( fullFileName ), GetChars( ioe.What() ) );
fullFileName, ioe.What() );
DisplayError( this, msg );
msg.Printf( _( "Failed to load '%s'" ), GetChars( fullFileName ) );
msg.Printf( _( "Failed to load '%s'" ), fullFileName );
AppendMsgPanel( wxEmptyString, msg, CYAN );
return false;

View File

@ -136,6 +136,20 @@ void SCH_SCREEN::DecRefCount()
}
void SCH_SCREEN::Append( SCH_SCREEN* aScreen )
{
wxCHECK_RET( aScreen, "Invalid screen object." );
// No need to decend the hierarchy. Once the top level screen is copied, all of it's
// children are copied as well.
m_drawList.Append( aScreen->m_drawList );
// This screen owns the objects now. This prevents the object from being delete when
// aSheet is deleted.
aScreen->m_drawList.SetOwnership( false );
}
void SCH_SCREEN::Clear()
{
FreeDrawList();
@ -1291,6 +1305,22 @@ int SCH_SCREEN::GetConnection( const wxPoint& aPosition, PICKED_ITEMS_LIST& aLis
}
#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
/******************************************************************/
/* Class SCH_SCREENS to handle the list of screens in a hierarchy */
/******************************************************************/
@ -1314,10 +1344,10 @@ static bool SortByTimeStamp( const EDA_ITEM* item1, const EDA_ITEM* item2 )
}
SCH_SCREENS::SCH_SCREENS()
SCH_SCREENS::SCH_SCREENS( SCH_SHEET* aSheet )
{
m_index = 0;
BuildScreenList( g_RootSheet );
buildScreenList( ( !aSheet ) ? g_RootSheet : aSheet );
}
@ -1355,7 +1385,7 @@ SCH_SCREEN* SCH_SCREENS::GetScreen( unsigned int aIndex ) const
}
void SCH_SCREENS::AddScreenToList( SCH_SCREEN* aScreen )
void SCH_SCREENS::addScreenToList( SCH_SCREEN* aScreen )
{
if( aScreen == NULL )
return;
@ -1370,26 +1400,21 @@ void SCH_SCREENS::AddScreenToList( SCH_SCREEN* aScreen )
}
void SCH_SCREENS::BuildScreenList( EDA_ITEM* aItem )
void SCH_SCREENS::buildScreenList( SCH_SHEET* aSheet )
{
if( aItem && aItem->Type() == SCH_SHEET_T )
if( aSheet && aSheet->Type() == SCH_SHEET_T )
{
SCH_SHEET* ds = (SCH_SHEET*) aItem;
aItem = ds->GetScreen();
}
SCH_SCREEN* screen = aSheet->GetScreen();
if( aItem && aItem->Type() == SCH_SCREEN_T )
{
SCH_SCREEN* screen = (SCH_SCREEN*) aItem;
addScreenToList( screen );
AddScreenToList( screen );
EDA_ITEM* strct = screen->GetDrawItems();
while( strct )
{
if( strct->Type() == SCH_SHEET_T )
{
BuildScreenList( strct );
buildScreenList( ( SCH_SHEET* )strct );
}
strct = strct->Next();
@ -1561,17 +1586,65 @@ bool SCH_SCREENS::HasNoFullyDefinedLibIds()
}
#if defined(DEBUG)
void SCH_SCREEN::Show( int nestLevel, std::ostream& os ) const
size_t SCH_SCREENS::GetLibNicknames( wxArrayString& aLibNicknames )
{
// for now, make it look like XML, expand on this later.
NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() << ">\n";
SCH_COMPONENT* symbol;
SCH_ITEM* item;
SCH_ITEM* nextItem;
SCH_SCREEN* screen;
wxString nickname;
for( EDA_ITEM* item = m_drawList.begin(); item; item = item->Next() )
for( screen = GetFirst(); screen; screen = GetNext() )
{
item->Show( nestLevel+1, os );
for( item = screen->GetDrawItems(); item; item = nextItem )
{
nextItem = item->Next();
if( item->Type() != SCH_COMPONENT_T )
continue;
symbol = dynamic_cast< SCH_COMPONENT* >( item );
nickname = symbol->GetLibId().GetLibNickname();
if( !nickname.empty() && ( aLibNicknames.Index( nickname ) == wxNOT_FOUND ) )
aLibNicknames.Add( nickname );;
}
}
NestedSpace( nestLevel, os ) << "</" << GetClass().Lower().mb_str() << ">\n";
return aLibNicknames.GetCount();
}
#endif
int SCH_SCREENS::ChangeSymbolLibNickname( const wxString& aFrom, const wxString& aTo )
{
SCH_COMPONENT* symbol;
SCH_ITEM* item;
SCH_ITEM* nextItem;
SCH_SCREEN* screen;
int cnt = 0;
for( screen = GetFirst(); screen; screen = GetNext() )
{
for( item = screen->GetDrawItems(); item; item = nextItem )
{
nextItem = item->Next();
if( item->Type() != SCH_COMPONENT_T )
continue;
symbol = dynamic_cast< SCH_COMPONENT* >( item );
if( symbol->GetLibId().GetLibNickname() != aFrom )
continue;
LIB_ID id = symbol->GetLibId();
id.SetLibNickname( aTo );
symbol->SetLibId( id );
cnt++;
}
}
return cnt;
}

File diff suppressed because it is too large Load Diff

View File

@ -29,16 +29,17 @@
#include <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <schframe.h>
#include <base_units.h>
#include <kiface_i.h>
#include <project.h>
#include <wildcards_and_files_ext.h>
#include <schframe.h>
#include <sch_legacy_plugin.h>
#include <sch_sheet.h>
#include <sch_sheet_path.h>
#include <dialogs/dialog_sch_sheet_props.h>
#include <wildcards_and_files_ext.h>
#include <project.h>
bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
@ -58,7 +59,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
dlg.SetSheetName( aSheet->GetName() );
dlg.SetSheetNameTextSize( StringFromValue( g_UserUnit, aSheet->GetSheetNameSize() ) );
dlg.SetSheetNameTextSizeUnits( units );
dlg.SetSheetTimeStamp( wxString::Format( wxT("%8.8lX"),
dlg.SetSheetTimeStamp( wxString::Format( wxT( "%8.8lX" ),
(unsigned long) aSheet->GetTimeStamp() ) );
/* This ugly hack fixes a bug in wxWidgets 2.8.7 and likely earlier
@ -66,7 +67,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
* column from being sized correctly. It doesn't cause any problems
* on win32 so it doesn't need to wrapped in ugly #ifdef __WXGTK__
* #endif.
* Still presen in wxWidgets 3.0.2
* Still present in wxWidgets 3.0.2
*/
dlg.Layout();
dlg.Fit();
@ -91,7 +92,7 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
if( sheet && (sheet != aSheet) )
{
DisplayError( this, wxString::Format( _( "A sheet named \"%s\" already exists." ),
GetChars( dlg.GetSheetName() ) ) );
dlg.GetSheetName() ) );
return false;
}
@ -103,19 +104,21 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
// Search for a schematic file having the same filename
// already in use in the hierarchy or on disk, in order to reuse it.
if( !g_RootSheet->SearchHierarchy( newFilename, &useScreen ) )
if( !g_RootSheet->SearchHierarchy( fileName.GetFullName(), &useScreen ) )
{
// if user entered a relative path, allow that to stay, but do the
// file existence test with an absolute (full) path. This transformation
// is local to this scope, but is the same one used at load time later.
wxString absolute = Prj().AbsolutePath( newFilename );
newFilename = Prj().AbsolutePath( newFilename );
loadFromFile = wxFileExists( absolute );
loadFromFile = wxFileExists( newFilename );
}
// Inside Eeschema, filenames are stored using unix notation
newFilename.Replace( wxT( "\\" ), wxT( "/" ) );
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
if( aSheet->GetScreen() == NULL ) // New sheet.
{
if( useScreen || loadFromFile ) // Load from existing file.
@ -123,11 +126,11 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
if( useScreen != NULL )
{
msg.Printf( _( "A file named '%s' already exists in the current schematic "
"hierarchy." ), GetChars( newFilename ) );
"hierarchy." ), newFilename );
}
else
{
msg.Printf( _( "A file named '%s' already exists." ), GetChars( newFilename ) );
msg.Printf( _( "A file named '%s' already exists." ), newFilename );
}
msg += _( "\n\nDo you want to create a sheet with the contents of this file?" );
@ -166,12 +169,11 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
if( useScreen != NULL )
{
tmp.Printf( _( "A file named <%s> already exists in the current schematic "
"hierarchy." ), GetChars( newFilename ) );
"hierarchy." ), newFilename );
}
else
{
tmp.Printf( _( "A file named <%s> already exists." ),
GetChars( newFilename ) );
tmp.Printf( _( "A file named <%s> already exists." ), newFilename );
}
msg += tmp;
@ -206,7 +208,21 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
if( renameFile )
{
aSheet->GetScreen()->SetFileName( newFilename );
SaveEEFile( aSheet->GetScreen() );
try
{
pi->Save( newFilename, aSheet->GetScreen(), &Kiway() );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error occurred saving schematic file '%s'." ), newFilename );
DisplayErrorMessage( this, msg, ioe.What() );
msg.Printf( _( "Failed to save schematic '%s'" ), newFilename );
AppendMsgPanel( wxEmptyString, msg, CYAN );
return false;
}
// If the the associated screen is shared by more than one sheet, remove the
// screen and reload the file to a new screen. Failure to do this will trash
@ -219,12 +235,29 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
}
}
aSheet->SetFileName( newFilename );
aSheet->SetFileName( fileName.GetFullPath( wxPATH_UNIX ) );
if( useScreen )
{
aSheet->SetScreen( useScreen );
}
else if( loadFromFile )
aSheet->Load( this );
{
try
{
aSheet = pi->Load( newFilename, &Kiway(), aSheet );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error occurred loading schematic file '%s'." ), newFilename );
DisplayErrorMessage( this, msg, ioe.What() );
msg.Printf( _( "Failed to load schematic '%s'" ), newFilename );
AppendMsgPanel( wxEmptyString, msg, CYAN );
return false;
}
}
aSheet->SetFileNameSize( ValueFromString( g_UserUnit, dlg.GetFileNameTextSize() ) );
aSheet->SetName( dlg.GetSheetName() );
@ -248,11 +281,24 @@ bool SCH_EDIT_FRAME::EditSheet( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHierarchy )
msg.Printf( _( "The sheet changes cannot be made because the destination sheet already "
"has the sheet <%s> or one of it's subsheets as a parent somewhere in "
"the schematic hierarchy." ),
GetChars( newFilename ) );
newFilename );
DisplayError( this, msg );
return false;
}
// Check to make sure the symbols have been remapped to the symbol library table.
SCH_SCREENS newScreens( aSheet );
if( newScreens.HasNoFullyDefinedLibIds() )
{
msg.Printf(_( "The schematic '%s' has not been remapped to the symbol library table. "
"Most if not all of the symbol library links will be broken. Do you "
"want to continue?" ), fileName.GetFullName() );
if( !IsOK( this, msg ) )
return false;
}
m_canvas->MoveCursorToCrossHair();
m_canvas->SetIgnoreMouseEvents( false );
OnModify();