Eagle Project Import: Code cleanup and documentation.
- Also makes the project and file import functions filetype dependent. - The change from IO_MGR::KICAD to IO_MGR::KICAD_SEXP removes a conflict from a compile definition for KICAD when compiling kicad/import_project.cpp
This commit is contained in:
parent
c3b0fca922
commit
3b0855d204
|
@ -42,6 +42,7 @@ Cheng Sheng <chengsheng[at]google-dot-com> Google Inc.
|
|||
Kristoffer Ödmark <kristoffer.odmark90[at]gmail-dot-com>
|
||||
Oliver Walters <oliver.henry.walters[at]gmail-dot-com>
|
||||
Jon Evans <jon[at]craftyjon-dot-com>
|
||||
Russell Oliver <roliver8143[at]gmail-dot-com>
|
||||
|
||||
See also CHANGELOG.txt for contributors.
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
Eagle Plugin Implementation Notes. 2017 Russell Oliver
|
||||
|
||||
Below is some notes on the correspondence between Eagle schematics and symbols libraries and the
|
||||
KiCad equivalent.
|
||||
|
||||
Eagle libraries are a many to many listing of symbols and footprints connected by a device set and
|
||||
device definitions. They are embedded in the schematic and board files if there are used, and
|
||||
therefore the schematic symbols and footprints can be recovered from either file.
|
||||
|
||||
An Eagle device set definition is the information needed to represent a physical part at the
|
||||
schematic level including the functional gates of the device. Each gate is lists the symbol to be
|
||||
displayed for that gate. This is equivalent to a KiCad symbol unit. Since the symbol is defined
|
||||
outside of the device set, multiple devices sets in the library can use the same symbol for a gate.
|
||||
Lower to a device set, is the device definition. This establishes the link between the schematic
|
||||
symbols and a physical part through the 'connect' elements. These map the symbol pins for each gate
|
||||
to the physical pins provided by the package (footprint) definition. An Eagle Symbol outlines the
|
||||
layout of graphical of items including pins. Pins for multi gate symbols are generally labelled
|
||||
per their function, i.e. input / output. An Eagle symbol pin is not numbered but merely labelled. A
|
||||
connect element gives the pad number for each pin found in that gate. Therefore the equivalent
|
||||
KiCad pin number is read from the connect element pad number. Since an Eagle gate is equivalent to
|
||||
a KiCad symbol unit, the graphical items for that unit will be copied from the Eagle symbol for
|
||||
that gate and will be unique for that unit. This will yield duplication of the graphical elements
|
||||
if the same symbol is used for multiple gates but the conversion will be complete.
|
||||
|
||||
An Eagle sheet contains a list of instances, which are equivalent to KiCad schematic component
|
||||
entries. An instance describes the part, the gate used and its location on the sheet. This is
|
||||
translated into the equivalent KiCad symbol with the given unit number.
|
||||
|
||||
Eagle 'plain' items describe graphical items with no electrical connection, such as note text,
|
||||
lines etc. Of importance is the use of wire elements to describe both electrical connections and
|
||||
graphical items. A wire element will act as an electrical connection when defined within a net and
|
||||
segment. Anywhere else it is a graphical line. The layer for the wire element will change the
|
||||
displayed colour for the wire. Connections between regular wires and busses occur when a wire ends
|
||||
on a bus segment. When translated to KiCad a bus connection symbol is created. Within an Eagle
|
||||
schematic there can be multiple sheets in a flat hierarchy. For each sheet, there is a list of
|
||||
electrically connected nets. Each net is broken up into graphically connected segments, defined by
|
||||
a list of wires and labels. Labels remain associate with wires of that net segment, even if they
|
||||
are not located on a wire element. This necessitates the movement of such a label to the nearest
|
||||
wire segment within KiCad.
|
|
@ -156,6 +156,26 @@ NODE_MAP MapChildren( wxXmlNode* currentNode )
|
|||
return nodesMap;
|
||||
}
|
||||
|
||||
int CountChildren( wxXmlNode* aCurrentNode, const std::string& aName )
|
||||
{
|
||||
// Map node_name -> node_pointer
|
||||
int count = 0;
|
||||
|
||||
// Loop through all children counting them if they match the given name
|
||||
aCurrentNode = aCurrentNode->GetChildren();
|
||||
|
||||
while( aCurrentNode )
|
||||
{
|
||||
if( aCurrentNode->GetName().ToStdString() == aName )
|
||||
count++;
|
||||
|
||||
// Get next child
|
||||
aCurrentNode = aCurrentNode->GetNext();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
string makeKey( const string& aFirst, const string& aSecond )
|
||||
{
|
||||
|
@ -211,32 +231,33 @@ wxPoint kicad_arc_center( const wxPoint& aStart, const wxPoint& aEnd, double aAn
|
|||
return center;
|
||||
}
|
||||
|
||||
int parseAlignment(wxString alignment)
|
||||
int parseAlignment( const wxString& alignment )
|
||||
{
|
||||
// (bottom-left | bottom-center | bottom-right | center-left |
|
||||
// center | center-right | top-left | top-center | top-right)
|
||||
// center | center-right | top-left | top-center | top-right)
|
||||
if( alignment == "center" )
|
||||
return ETEXT::CENTER;
|
||||
else if( alignment == "center-right" )
|
||||
return ETEXT::CENTER_RIGHT;
|
||||
return ETEXT::CENTER_RIGHT;
|
||||
else if( alignment == "top-left" )
|
||||
return ETEXT::TOP_LEFT;
|
||||
return ETEXT::TOP_LEFT;
|
||||
else if( alignment == "top-center" )
|
||||
return ETEXT::TOP_CENTER;
|
||||
return ETEXT::TOP_CENTER;
|
||||
else if( alignment == "top-right" )
|
||||
return ETEXT::TOP_RIGHT;
|
||||
return ETEXT::TOP_RIGHT;
|
||||
else if( alignment == "bottom-left" )
|
||||
return ETEXT::BOTTOM_LEFT;
|
||||
return ETEXT::BOTTOM_LEFT;
|
||||
else if( alignment == "bottom-center" )
|
||||
return ETEXT::BOTTOM_CENTER;
|
||||
return ETEXT::BOTTOM_CENTER;
|
||||
else if( alignment == "bottom-right" )
|
||||
return ETEXT::BOTTOM_RIGHT;
|
||||
return ETEXT::BOTTOM_RIGHT;
|
||||
else if( alignment == "center-left" )
|
||||
return ETEXT::CENTER_LEFT;
|
||||
return ETEXT::CENTER_LEFT;
|
||||
|
||||
return DEFAULT_ALIGNMENT;
|
||||
}
|
||||
|
||||
|
||||
// convert textsize method.
|
||||
wxSize convertTextSize(ETEXT& etext ) {
|
||||
|
||||
|
@ -909,7 +930,7 @@ EDEVICE::EDEVICE( wxXmlNode* aDevice )
|
|||
};
|
||||
|
||||
|
||||
EDEVICESET::EDEVICESET( wxXmlNode* aDeviceSet )
|
||||
EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet )
|
||||
{
|
||||
/*
|
||||
<!ELEMENT deviceset (description?, gates, devices)>
|
||||
|
|
|
@ -52,7 +52,7 @@ void FP_LIB_TABLE_ROW::SetType( const wxString& aType )
|
|||
type = IO_MGR::EnumFromStr( aType );
|
||||
|
||||
if( IO_MGR::PCB_FILE_T( -1 ) == type )
|
||||
type = IO_MGR::KICAD;
|
||||
type = IO_MGR::KICAD_SEXP;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
Eagle Plugin Implementation Notes.
|
||||
2017 Russell Oliver
|
||||
|
||||
|
||||
|
||||
|
||||
Kicad Eagle
|
||||
Footprint Package
|
||||
Symbol Device
|
||||
Unit Gate
|
||||
|
||||
Pin # Pad # from connect for gate
|
||||
|
||||
|
||||
An Eagle library is made up of a description, package, symbols and devicesets.
|
||||
|
||||
Symbols outline the graphical layout of items including pins.
|
||||
Pins for multi gate symbols are generally labled according to their function, i.e input / output. I/O
|
||||
In contrast to kicad, different gates can have different symbols.
|
||||
An Eagle gate is equivelent to a Kicad symbol Unit.
|
||||
|
||||
An Eagle symbol pin is not numbered, therefore the relationship is made by the Eagle connect element.
|
||||
A connect element gives the pad number for each pin found in that gate.
|
||||
Therefore the equivelent kicad pin number is read from the connect element pad number.
|
||||
|
||||
Since an Eagle gate is equivelent to a kicad symbol unit, the graphical items for that unit will be copied from the Eagle symbol and will be unique for that unit.
|
||||
This will yield duplication of the graphical elements if the same symbol is used for multiple gates but the conversion will be complete.
|
||||
A second pass may be used to remove duplicate items.
|
||||
A kicad pin is numbered using the pad number found in the connect element. The pin name will be retained.
|
||||
|
||||
An Eagle sheet contains a list of instances, which are equivelent to Kicad schematic component entries.
|
||||
An instance describes the part, the gate used and its location on the sheet.
|
||||
|
||||
<instance part="C1" gate="G$1" x="27.94" y="30.48"/>
|
||||
A part has a name, the library used, the deviceset, the device, and optionally the device's value.
|
||||
<part name="C1" library="rcl" deviceset="C-US" device="C0603" value="1u"/>
|
||||
|
||||
|
||||
|
||||
|
|
@ -623,59 +623,66 @@ bool SCH_EDIT_FRAME::doAutoSave()
|
|||
return autoSaveOk;
|
||||
}
|
||||
|
||||
bool SCH_EDIT_FRAME::ImportFile( const wxString aFileName)
|
||||
|
||||
bool SCH_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
||||
{
|
||||
wxString fullFileName( aFileName );
|
||||
|
||||
// We insist on caller sending us an absolute path, if it does not, we say it's a bug.
|
||||
wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(),
|
||||
wxT( "Import eagle schematic caller didn't send full filename" ) );
|
||||
|
||||
if( !LockFile( fullFileName ) )
|
||||
{
|
||||
wxString msg = wxString::Format( _(
|
||||
"Schematic file '%s' is already open." ),
|
||||
GetChars( fullFileName )
|
||||
);
|
||||
DisplayError( this, msg );
|
||||
return false;
|
||||
}
|
||||
wxString fullFileName( aFileName );
|
||||
|
||||
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi;
|
||||
|
||||
pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EAGLE ) );
|
||||
|
||||
|
||||
|
||||
|
||||
g_RootSheet = pi->Load( fullFileName, &Kiway() );
|
||||
|
||||
|
||||
wxString projectpath = Kiway().Prj().GetProjectPath();
|
||||
wxFileName newfilename = Prj().AbsolutePath( Prj().GetProjectName() );
|
||||
|
||||
newfilename.SetExt( SchematicFileExtension );
|
||||
|
||||
m_CurrentSheet->clear();
|
||||
m_CurrentSheet->push_back( g_RootSheet );
|
||||
SetScreen( m_CurrentSheet->LastScreen() );
|
||||
|
||||
g_RootSheet->SetFileName( newfilename.GetFullPath() );
|
||||
GetScreen()->SetFileName( newfilename.GetFullPath() );
|
||||
GetScreen()->SetModify();
|
||||
|
||||
wxString projectpath;
|
||||
wxFileName newfilename;
|
||||
SCH_SHEET_LIST sheetList( g_RootSheet );
|
||||
SCH_SCREENS schematic;
|
||||
|
||||
UpdateFileHistory( fullFileName );
|
||||
schematic.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
|
||||
GetScreen()->TestDanglingEnds(); // Only perform the dangling end test on root sheet.
|
||||
switch( (SCH_IO_MGR::SCH_FILE_T)aFileType )
|
||||
{
|
||||
case SCH_IO_MGR::SCH_EAGLE:
|
||||
// We insist on caller sending us an absolute path, if it does not, we say it's a bug.
|
||||
wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(),
|
||||
wxT( "Import eagle schematic caller didn't send full filename" ) );
|
||||
|
||||
GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
|
||||
Zoom_Automatique( false );
|
||||
SetSheetNumberAndCount();
|
||||
m_canvas->Refresh( true );
|
||||
UpdateTitle();
|
||||
if( !LockFile( fullFileName ) )
|
||||
{
|
||||
wxString msg = wxString::Format( _(
|
||||
"Schematic file '%s' is already open." ),
|
||||
GetChars( fullFileName )
|
||||
);
|
||||
DisplayError( this, msg );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_EAGLE ) );
|
||||
|
||||
g_RootSheet = pi->Load( fullFileName, &Kiway() );
|
||||
|
||||
projectpath = Kiway().Prj().GetProjectPath();
|
||||
newfilename = Prj().AbsolutePath( Prj().GetProjectName() );
|
||||
|
||||
newfilename.SetExt( SchematicFileExtension );
|
||||
|
||||
m_CurrentSheet->clear();
|
||||
m_CurrentSheet->push_back( g_RootSheet );
|
||||
SetScreen( m_CurrentSheet->LastScreen() );
|
||||
|
||||
g_RootSheet->SetFileName( newfilename.GetFullPath() );
|
||||
GetScreen()->SetFileName( newfilename.GetFullPath() );
|
||||
GetScreen()->SetModify();
|
||||
|
||||
UpdateFileHistory( fullFileName );
|
||||
schematic.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
|
||||
GetScreen()->TestDanglingEnds(); // Only perform the dangling end test on root sheet.
|
||||
|
||||
GetScreen()->SetGrid( ID_POPUP_GRID_LEVEL_1000 + m_LastGridSizeId );
|
||||
Zoom_Automatique( false );
|
||||
SetSheetNumberAndCount();
|
||||
m_canvas->Refresh( true );
|
||||
UpdateTitle();
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -112,12 +112,12 @@ void SCH_EDIT_FRAME::sendNetlist()
|
|||
bool SCH_EDIT_FRAME::CreateNetlist( int aFormat, const wxString& aFullFileName,
|
||||
unsigned aNetlistOptions, REPORTER* aReporter, bool silent )
|
||||
{
|
||||
if( !silent )
|
||||
if( !silent ) // checks for errors and invokes annotation dialog as neccessary
|
||||
{
|
||||
if( !prepareForNetlist() )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
else // performs similar function as prepareForNetlist but without a dialog.
|
||||
{
|
||||
SCH_SCREENS schematic;
|
||||
schematic.UpdateSymbolLinks();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,8 @@
|
|||
*
|
||||
* Copyright (C) 2017 CERN
|
||||
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
* @author Russell Oliver <roliver8143@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -69,9 +71,9 @@ class LIB_TEXT;
|
|||
typedef struct EAGLE_LIBRARY
|
||||
{
|
||||
std::string name;
|
||||
boost::ptr_map<std::string, LIB_PART> kicadsymbols;
|
||||
std::unordered_map<std::string, wxXmlNode*> symbolnodes;
|
||||
std::unordered_map<std::string, int> gate_unit;
|
||||
boost::ptr_map<std::string, LIB_PART> KiCadSymbols;
|
||||
std::unordered_map<std::string, wxXmlNode*> SymbolNodes;
|
||||
std::unordered_map<std::string, int> GateUnit;
|
||||
std::unordered_map<std::string, std::string> package;
|
||||
|
||||
} EAGLE_LIBRARY;
|
||||
|
@ -92,58 +94,62 @@ public:
|
|||
|
||||
int GetModifyHash() const override;
|
||||
|
||||
void SaveLibrary( const wxString& aFileName, const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
SCH_SHEET* Load( const wxString& aFileName, KIWAY* aKiway, SCH_SHEET* aAppendToMe = NULL,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
void Save( const wxString& aFileName, SCH_SCREEN* aSchematic, KIWAY* aKiway,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
size_t GetSymbolLibCount( const wxString& aLibraryPath,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
void EnumerateSymbolLib( wxArrayString& aAliasNameList, const wxString& aLibraryPath,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
LIB_ALIAS* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
void SaveSymbol( const wxString& aLibraryPath, const LIB_PART* aSymbol,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
void DeleteAlias( const wxString& aLibraryPath, const wxString& aAliasName,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
void DeleteSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
void CreateSymbolLib( const wxString& aLibraryPath,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
bool DeleteSymbolLib( const wxString& aLibraryPath,
|
||||
const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
bool IsSymbolLibWritable( const wxString& aLibraryPath ) override;
|
||||
|
||||
void SymbolLibOptions( PROPERTIES* aListToAppendTo ) const override;
|
||||
|
||||
bool CheckHeader( const wxString& aFileName ) override;
|
||||
|
||||
|
||||
// unimplemented functions. Will trigger a not_implemented IO error.
|
||||
//void SaveLibrary( const wxString& aFileName, const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//void Save( const wxString& aFileName, SCH_SCREEN* aSchematic, KIWAY* aKiway,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//size_t GetSymbolLibCount( const wxString& aLibraryPath,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//void EnumerateSymbolLib( wxArrayString& aAliasNameList, const wxString& aLibraryPath,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//LIB_ALIAS* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//void SaveSymbol( const wxString& aLibraryPath, const LIB_PART* aSymbol,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//void DeleteAlias( const wxString& aLibraryPath, const wxString& aAliasName,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//void DeleteSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//void CreateSymbolLib( const wxString& aLibraryPath,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
// bool DeleteSymbolLib( const wxString& aLibraryPath,
|
||||
// const PROPERTIES* aProperties = NULL ) override;
|
||||
|
||||
//bool IsSymbolLibWritable( const wxString& aLibraryPath ) override;
|
||||
|
||||
//void SymbolLibOptions( PROPERTIES* aListToAppendTo ) const override;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void loadDrawing( wxXmlNode* aDrawingNode );
|
||||
void loadLayerDefs( wxXmlNode* aLayers );
|
||||
void loadSchematic( wxXmlNode* aSchematicNode );
|
||||
void loadSheet( wxXmlNode* aSheetNode, int sheetcount );
|
||||
void loadInstance( wxXmlNode* aInstanceNode );
|
||||
void loadModuleinst( wxXmlNode* aModuleinstNode );
|
||||
//void loadModuleinst( wxXmlNode* aModuleinstNode ); // Eagle 8 feature that defines a replicatable schematic circuit and pcb layout.
|
||||
EAGLE_LIBRARY* loadLibrary( wxXmlNode* aLibraryNode, EAGLE_LIBRARY* elib);
|
||||
void countNets( wxXmlNode* aSchematicNode );
|
||||
void moveLabels( SCH_ITEM* wire, wxPoint newendpoint);
|
||||
void addBusEntries();
|
||||
static wxString fixNetName( const wxString& aNetName );
|
||||
|
||||
SCH_LAYER_ID kicadLayer( int aEagleLayer );
|
||||
SCH_LAYER_ID kiCadLayer( int aEagleLayer );
|
||||
wxPoint findNearestLinePoint(wxPoint aPoint, const DLIST< SCH_LINE >& lines);
|
||||
|
||||
void loadSegments( wxXmlNode* aSegmentsNode, const wxString& aNetName,
|
||||
|
@ -151,15 +157,15 @@ private:
|
|||
SCH_LINE* loadWire( wxXmlNode* aWireNode );
|
||||
SCH_TEXT* loadLabel( wxXmlNode* aLabelNode, const wxString& aNetName, const DLIST< SCH_LINE >& segmentWires);
|
||||
SCH_JUNCTION* loadJunction( wxXmlNode* aJunction );
|
||||
SCH_TEXT* loadplaintext( wxXmlNode* aSchText );
|
||||
SCH_TEXT* loadPlainText( wxXmlNode* aSchText );
|
||||
|
||||
bool loadSymbol(wxXmlNode *aSymbolNode, std::unique_ptr< LIB_PART >& aPart, EDEVICE* aDevice, int gateNumber, string gateName);
|
||||
LIB_CIRCLE* loadSymbolCircle( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aCircleNode, int gateNumber);
|
||||
LIB_RECTANGLE* loadSymbolRectangle( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aRectNode, int gateNumber );
|
||||
LIB_POLYLINE* loadSymbolPolyLine( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aPolygonNode, int gateNumber );
|
||||
LIB_ITEM* loadSymbolWire( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aWireNode, int gateNumber);
|
||||
LIB_PIN* loadPin( std::unique_ptr< LIB_PART >& aPart, wxXmlNode*, EPIN* epin, int gateNumber);
|
||||
LIB_TEXT* loadSymboltext( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aLibText, int gateNumber);
|
||||
bool loadSymbol(wxXmlNode *aSymbolNode, std::unique_ptr< LIB_PART >& aPart, EDEVICE* aDevice, int aGateNumber, string aGateName);
|
||||
LIB_CIRCLE* loadSymbolCircle( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aCircleNode, int aGateNumber);
|
||||
LIB_RECTANGLE* loadSymbolRectangle( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aRectNode, int aGateNumber );
|
||||
LIB_POLYLINE* loadSymbolPolyLine( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aPolygonNode, int aGateNumber );
|
||||
LIB_ITEM* loadSymbolWire( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aWireNode, int aGateNumber);
|
||||
LIB_PIN* loadPin( std::unique_ptr< LIB_PART >& aPart, wxXmlNode*, EPIN* epin, int aGateNumber);
|
||||
LIB_TEXT* loadSymbolText( std::unique_ptr< LIB_PART >& aPart, wxXmlNode* aLibText, int aGateNumber);
|
||||
|
||||
KIWAY* m_kiway; ///< For creating sub sheets.
|
||||
SCH_SHEET* m_rootSheet; ///< The root sheet of the schematic being loaded..
|
||||
|
@ -171,7 +177,6 @@ private:
|
|||
EPART_MAP m_partlist;
|
||||
std::map<std::string, EAGLE_LIBRARY> m_eaglelibraries;
|
||||
|
||||
EDA_RECT sheetBoundingBox;
|
||||
std::map<std::string, int > m_NetCounts;
|
||||
std::map<int, SCH_LAYER_ID> m_LayerMap;
|
||||
|
||||
|
|
|
@ -488,8 +488,6 @@ public:
|
|||
/**
|
||||
* Function CheckHeader
|
||||
* returns true if the first line in @a aFileName begins with the expected header
|
||||
* system libraries are read only because of where they are installed.)
|
||||
*
|
||||
* @param aFileName is the name of the file to use as input
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -785,7 +785,13 @@ public:
|
|||
|
||||
bool IsSearchCacheObsolete( const SCH_FIND_REPLACE_DATA& aSearchCriteria );
|
||||
|
||||
bool ImportFile( const wxString aFileName ) override ;
|
||||
/**
|
||||
* Function ImportFile
|
||||
* load the given filename but sets the path to the current project path.
|
||||
* @param full filepath of file to be imported.
|
||||
* @param aFileType SCH_FILE_T value for filetype
|
||||
*/
|
||||
bool ImportFile( const wxString& aFileName, int aFileType ) override ;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ public:
|
|||
* aData is empty, the attribute is understood as unavailable; otherwise, the
|
||||
* conversion to T is tried.
|
||||
*/
|
||||
OPTIONAL_XML_ATTRIBUTE( wxString aData )
|
||||
OPTIONAL_XML_ATTRIBUTE( const wxString& aData )
|
||||
{
|
||||
m_isAvailable = !aData.IsEmpty();
|
||||
|
||||
|
@ -243,7 +243,7 @@ public:
|
|||
* @param aData is a wxString that should be converted to T. If the string is empty, the
|
||||
* attribute is set to unavailable.
|
||||
*/
|
||||
OPTIONAL_XML_ATTRIBUTE<T>& operator =( wxString aData )
|
||||
OPTIONAL_XML_ATTRIBUTE<T>& operator =( const wxString& aData )
|
||||
{
|
||||
m_isAvailable = !aData.IsEmpty();
|
||||
|
||||
|
@ -282,7 +282,7 @@ public:
|
|||
* tries to convert a string to the base type.
|
||||
* @param aString is the string that will be converted to the base type.
|
||||
*/
|
||||
void Set( wxString aString )
|
||||
void Set( const wxString& aString )
|
||||
{
|
||||
m_data = Convert<T>( aString );
|
||||
}
|
||||
|
@ -360,6 +360,16 @@ public:
|
|||
*/
|
||||
NODE_MAP MapChildren( wxXmlNode* currentNode );
|
||||
|
||||
|
||||
/**
|
||||
* Function CountChildren
|
||||
* provides an easy access to the children of an XML node via their names.
|
||||
* @param aCurrentNode is a pointer to a wxXmlNode, whose children will be mapped.
|
||||
* @param aName the name of the specific child names to be counted.
|
||||
* @return number of children with the give node name.
|
||||
*/
|
||||
int CountChildren( wxXmlNode* aCurrentNode, const std::string& aName );
|
||||
|
||||
/// Assemble a two part key as a simple concatenation of aFirst and aSecond parts,
|
||||
/// using a separator.
|
||||
string makeKey( const string& aFirst, const string& aSecond );
|
||||
|
@ -948,7 +958,7 @@ typedef struct EDEVICE
|
|||
EDEVICE( wxXmlNode* aDevice );
|
||||
} EDEVICE;
|
||||
|
||||
struct EDEVICESET
|
||||
struct EDEVICE_SET
|
||||
{
|
||||
/*
|
||||
<!ELEMENT deviceset (description?, gates, devices)>
|
||||
|
@ -966,7 +976,7 @@ struct EDEVICESET
|
|||
//std::vector<EGATE> gates;
|
||||
|
||||
|
||||
EDEVICESET( wxXmlNode* aDeviceSet );
|
||||
EDEVICE_SET( wxXmlNode* aDeviceSet );
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
}
|
||||
|
||||
FP_LIB_TABLE_ROW() :
|
||||
type( IO_MGR::KICAD )
|
||||
type( IO_MGR::KICAD_SEXP )
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -180,10 +180,12 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Function OpenProjectFiles
|
||||
* Function ImportFile
|
||||
* load the given filename but sets the path to the current project path.
|
||||
* @param full filepath of file to be imported.
|
||||
* @param aFileType enum value for filetype
|
||||
*/
|
||||
VTBL_ENTRY bool ImportFile( const wxString aFileName)
|
||||
VTBL_ENTRY bool ImportFile( const wxString& aFileName, int aFileType )
|
||||
{
|
||||
// overload me for your wxFrame type.
|
||||
|
||||
|
@ -192,7 +194,7 @@ public:
|
|||
|
||||
/**
|
||||
* Function ReadPcbNetlist
|
||||
* provides access to PcbNew's function.
|
||||
* provides access to PcbNew's function ReadPcbNetlist.
|
||||
*/
|
||||
|
||||
VTBL_ENTRY void ReadPcbNetlist( const wxString& aNetlistFileName,
|
||||
|
@ -209,7 +211,7 @@ public:
|
|||
|
||||
/**
|
||||
* Function ReadPcbNetlist
|
||||
* provides access to Eeschema's function.
|
||||
* provides access to Eeschema's function CreateNetlist.
|
||||
*/
|
||||
VTBL_ENTRY bool CreateNetlist( int aFormat,
|
||||
const wxString& aFullFileName,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 2004-2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* @author Russell Oliver <roliver8143@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -23,7 +23,7 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @file eagle_project.cpp
|
||||
* @file import_project.cpp
|
||||
* @brief routines for importing an eagle project
|
||||
*/
|
||||
|
||||
|
@ -45,6 +45,8 @@
|
|||
#include <stdexcept>
|
||||
#include "pgm_kicad.h"
|
||||
|
||||
#include <io_mgr.h>
|
||||
#include <sch_io_mgr.h>
|
||||
#include <wxPcbStruct.h>
|
||||
#include <schframe.h>
|
||||
#include <netlist.h>
|
||||
|
@ -143,7 +145,7 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
|
|||
}
|
||||
}
|
||||
|
||||
schframe->ImportFile( sch_filename );
|
||||
schframe->ImportFile( sch_filename, SCH_IO_MGR::SCH_EAGLE );
|
||||
|
||||
if( !schframe->IsShown() ) // the frame exists, (created by the dialog field editor)
|
||||
// but no project loaded.
|
||||
|
@ -182,7 +184,7 @@ void KICAD_MANAGER_FRAME::OnImportEagleFiles( wxCommandEvent& event )
|
|||
// if the frame is not visible, the board is not yet loaded
|
||||
if( !pcbframe->IsVisible() )
|
||||
{
|
||||
pcbframe->ImportFile( pcb.GetFullPath() );
|
||||
pcbframe->ImportFile( pcb.GetFullPath(), IO_MGR::EAGLE );
|
||||
pcbframe->Show( true );
|
||||
}
|
||||
|
||||
|
|
|
@ -195,6 +195,9 @@ public:
|
|||
void ReCreateMenuBar() override;
|
||||
void RecreateBaseHToolbar();
|
||||
|
||||
/**
|
||||
* Open dialog to import Eagle schematic and board files.
|
||||
*/
|
||||
void OnImportEagleFiles( wxCommandEvent& event );
|
||||
|
||||
/**
|
||||
|
|
|
@ -185,7 +185,7 @@ public:
|
|||
|
||||
wxArrayString choices;
|
||||
|
||||
choices.Add( IO_MGR::ShowType( IO_MGR::KICAD ) );
|
||||
choices.Add( IO_MGR::ShowType( IO_MGR::KICAD_SEXP ) );
|
||||
choices.Add( IO_MGR::ShowType( IO_MGR::GITHUB ) );
|
||||
choices.Add( IO_MGR::ShowType( IO_MGR::LEGACY ) );
|
||||
choices.Add( IO_MGR::ShowType( IO_MGR::EAGLE ) );
|
||||
|
|
|
@ -65,7 +65,7 @@ static const struct
|
|||
IO_MGR::PCB_FILE_T m_Plugin;
|
||||
} fileFilters[FILTER_COUNT] =
|
||||
{
|
||||
{ "KiCad (folder with .kicad_mod files)", "kicad_mod", false, IO_MGR::KICAD },
|
||||
{ "KiCad (folder with .kicad_mod files)", "kicad_mod", false, IO_MGR::KICAD_SEXP },
|
||||
{ "Eagle 6.x (*.lbr)", "lbr", true, IO_MGR::EAGLE },
|
||||
{ "KiCad legacy (*.mod)", "mod", true, IO_MGR::LEGACY },
|
||||
{ "Geda (folder with *.fp files)", "fp", false, IO_MGR::GEDA_PCB },
|
||||
|
@ -206,7 +206,7 @@ wxString WIZARD_FPLIB_TABLE::LIBRARY::GetPluginName() const
|
|||
case IO_MGR::LEGACY:
|
||||
return wxT( "Legacy" );
|
||||
|
||||
case IO_MGR::KICAD:
|
||||
case IO_MGR::KICAD_SEXP:
|
||||
return wxT( "KiCad" );
|
||||
|
||||
case IO_MGR::EAGLE:
|
||||
|
@ -544,7 +544,7 @@ void WIZARD_FPLIB_TABLE::OnWizardFinished( wxWizardEvent& aEvent )
|
|||
wxString path = it->GetAbsolutePath();
|
||||
path.Replace( GetGithubURL(), getDownloadDir() );
|
||||
it->setPath( path );
|
||||
it->setPluginType( IO_MGR::KICAD );
|
||||
it->setPluginType( IO_MGR::KICAD_SEXP );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -654,7 +654,7 @@ bool WIZARD_FPLIB_TABLE::downloadGithubLibsFromList( wxArrayString& aUrlList,
|
|||
try
|
||||
{
|
||||
PLUGIN::RELEASER src( IO_MGR::PluginFind( IO_MGR::GITHUB ) );
|
||||
PLUGIN::RELEASER dst( IO_MGR::PluginFind( IO_MGR::KICAD ) );
|
||||
PLUGIN::RELEASER dst( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) );
|
||||
|
||||
wxArrayString footprints;
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName, bo
|
|||
IO_MGR::PCB_FILE_T pluginType;
|
||||
} loaders[] =
|
||||
{
|
||||
{ PcbFileWildcard, IO_MGR::KICAD }, // Current Kicad board files
|
||||
{ PcbFileWildcard, IO_MGR::KICAD_SEXP }, // Current Kicad board files
|
||||
{ LegacyPcbFileWildcard, IO_MGR::LEGACY }, // Old Kicad board files
|
||||
{ EaglePcbFileWildcard, IO_MGR::EAGLE }, // Import board files
|
||||
{ PCadPcbFileWildcard, IO_MGR::PCAD }, // Import board files
|
||||
|
@ -396,7 +396,7 @@ IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl )
|
|||
}
|
||||
else
|
||||
{
|
||||
pluginType = IO_MGR::KICAD;
|
||||
pluginType = IO_MGR::KICAD_SEXP;
|
||||
}
|
||||
|
||||
return pluginType;
|
||||
|
@ -469,7 +469,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
|
|||
|
||||
IO_MGR::PCB_FILE_T pluginType = plugin_type( fullFileName, aCtl );
|
||||
|
||||
bool converted = pluginType != IO_MGR::LEGACY && pluginType != IO_MGR::KICAD;
|
||||
bool converted = pluginType != IO_MGR::LEGACY && pluginType != IO_MGR::KICAD_SEXP;
|
||||
|
||||
if( !converted )
|
||||
{
|
||||
|
@ -711,7 +711,7 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
|
|||
|
||||
try
|
||||
{
|
||||
PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD ) );
|
||||
PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) );
|
||||
|
||||
wxASSERT( pcbFileName.IsAbsolute() );
|
||||
|
||||
|
@ -790,7 +790,7 @@ bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName )
|
|||
|
||||
try
|
||||
{
|
||||
PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD ) );
|
||||
PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) );
|
||||
|
||||
wxASSERT( pcbFileName.IsAbsolute() );
|
||||
|
||||
|
@ -864,23 +864,32 @@ bool PCB_EDIT_FRAME::doAutoSave()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool PCB_EDIT_FRAME::ImportFile( const wxString aFileName )
|
||||
|
||||
bool PCB_EDIT_FRAME::ImportFile( const wxString& aFileName, int aFileType )
|
||||
{
|
||||
if( OpenProjectFiles( std::vector<wxString>( 1, aFileName ),
|
||||
KICTL_EAGLE_BRD ) )
|
||||
switch( (IO_MGR::PCB_FILE_T) aFileType )
|
||||
{
|
||||
wxString projectpath = Kiway().Prj().GetProjectPath();
|
||||
wxFileName newfilename = Prj().AbsolutePath( Prj().GetProjectName() );
|
||||
case IO_MGR::EAGLE:
|
||||
if( OpenProjectFiles( std::vector<wxString>( 1, aFileName ),
|
||||
KICTL_EAGLE_BRD ) )
|
||||
{
|
||||
wxString projectpath = Kiway().Prj().GetProjectPath();
|
||||
wxFileName newfilename = Prj().AbsolutePath( Prj().GetProjectName() );
|
||||
|
||||
newfilename.SetExt( KiCadPcbFileExtension );
|
||||
newfilename.SetExt( KiCadPcbFileExtension );
|
||||
|
||||
GetBoard()->SetFileName( newfilename.GetFullPath() );
|
||||
UpdateTitle();
|
||||
|
||||
GetBoard()->SetFileName( newfilename.GetFullPath() );
|
||||
UpdateTitle();
|
||||
ArchiveModulesOnBoard( true, newfilename.GetName() );
|
||||
|
||||
ArchiveModulesOnBoard( true, newfilename.GetName() );
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -65,7 +65,7 @@ PLUGIN* IO_MGR::PluginFind( PCB_FILE_T aFileType )
|
|||
case LEGACY:
|
||||
return new LEGACY_PLUGIN();
|
||||
|
||||
case KICAD:
|
||||
case KICAD_SEXP:
|
||||
return new PCB_IO();
|
||||
|
||||
case EAGLE:
|
||||
|
@ -116,7 +116,7 @@ const wxString IO_MGR::ShowType( PCB_FILE_T aType )
|
|||
case LEGACY:
|
||||
return wxString( wxT( "Legacy" ) );
|
||||
|
||||
case KICAD:
|
||||
case KICAD_SEXP:
|
||||
return wxString( wxT( "KiCad" ) );
|
||||
|
||||
case EAGLE:
|
||||
|
@ -141,7 +141,7 @@ IO_MGR::PCB_FILE_T IO_MGR::EnumFromStr( const wxString& aType )
|
|||
// library tables, so don't do change, only additions are ok.
|
||||
|
||||
if( aType == wxT( "KiCad" ) )
|
||||
return KICAD;
|
||||
return KICAD_SEXP;
|
||||
|
||||
if( aType == wxT( "Legacy" ) )
|
||||
return LEGACY;
|
||||
|
@ -181,7 +181,7 @@ const wxString IO_MGR::GetFileExtension( PCB_FILE_T aFileType )
|
|||
|
||||
IO_MGR::PCB_FILE_T IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath )
|
||||
{
|
||||
PCB_FILE_T ret = KICAD; // default guess, unless detected otherwise.
|
||||
PCB_FILE_T ret = KICAD_SEXP; // default guess, unless detected otherwise.
|
||||
wxFileName fn( aLibPath );
|
||||
|
||||
if( fn.GetExt() == LegacyFootprintLibPathExtension )
|
||||
|
@ -209,7 +209,7 @@ IO_MGR::PCB_FILE_T IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath
|
|||
else if( fn.GetExt() == KiCadFootprintLibPathExtension &&
|
||||
!aLibPath.StartsWith( wxT( "http" ) ) )
|
||||
{
|
||||
ret = KICAD;
|
||||
ret = KICAD_SEXP;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -254,4 +254,3 @@ void IO_MGR::Save( PCB_FILE_T aFileType, const wxString& aFileName, BOARD* aBoar
|
|||
|
||||
THROW_IO_ERROR( wxString::Format( FMT_NOTFOUND, ShowType( aFileType ).GetData() ) );
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
enum PCB_FILE_T
|
||||
{
|
||||
LEGACY, ///< Legacy Pcbnew file formats prior to s-expression.
|
||||
KICAD, ///< S-expression Pcbnew file format.
|
||||
KICAD_SEXP, ///< S-expression Pcbnew file format.
|
||||
EAGLE,
|
||||
PCAD,
|
||||
GEDA_PCB, ///< Geda PCB file formats.
|
||||
|
|
|
@ -144,7 +144,7 @@ static IO_MGR::PCB_FILE_T detect_file_type( FILE* aFile, const wxFileName& aFile
|
|||
|
||||
if( !strncasecmp( line, "(module", strlen( "(module" ) ) )
|
||||
{
|
||||
file_type = IO_MGR::KICAD;
|
||||
file_type = IO_MGR::KICAD_SEXP;
|
||||
*aName = aFileName.GetName();
|
||||
}
|
||||
else if( !strncasecmp( line, FOOTPRINT_LIBRARY_HEADER, FOOTPRINT_LIBRARY_HEADER_CNT ) )
|
||||
|
@ -240,7 +240,7 @@ MODULE* try_load_footprint( const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFil
|
|||
module = parse_module_with_plugin( aFileName, aFileType, aName );
|
||||
break;
|
||||
|
||||
case IO_MGR::KICAD:
|
||||
case IO_MGR::KICAD_SEXP:
|
||||
module = parse_module_kicad( aFileName );
|
||||
break;
|
||||
|
||||
|
@ -479,8 +479,8 @@ wxString PCB_BASE_EDIT_FRAME::CreateNewLibrary(const wxString& aLibName )
|
|||
}
|
||||
|
||||
|
||||
// We can save fp libs only using IO_MGR::KICAD format (.pretty libraries)
|
||||
IO_MGR::PCB_FILE_T piType = IO_MGR::KICAD;
|
||||
// We can save fp libs only using IO_MGR::KICAD_SEXP format (.pretty libraries)
|
||||
IO_MGR::PCB_FILE_T piType = IO_MGR::KICAD_SEXP;
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -635,7 +635,7 @@ void PCB_EDIT_FRAME::ArchiveModulesOnBoard( bool aStoreInNewLib, const wxString&
|
|||
if( libPath.IsEmpty() ) // Aborted
|
||||
return;
|
||||
|
||||
IO_MGR::PCB_FILE_T piType = IO_MGR::KICAD;
|
||||
IO_MGR::PCB_FILE_T piType = IO_MGR::KICAD_SEXP;
|
||||
PLUGIN::RELEASER pi( IO_MGR::PluginFind( piType ) );
|
||||
|
||||
for( MODULE* curr_fp = GetBoard()->m_Modules; curr_fp; curr_fp = curr_fp->Next() )
|
||||
|
|
|
@ -60,7 +60,7 @@ void ScriptingSetPcbEditFrame( PCB_EDIT_FRAME* aPcbEditFrame )
|
|||
BOARD* LoadBoard( wxString& aFileName )
|
||||
{
|
||||
if( aFileName.EndsWith( wxT( ".kicad_pcb" ) ) )
|
||||
return LoadBoard( aFileName, IO_MGR::KICAD );
|
||||
return LoadBoard( aFileName, IO_MGR::KICAD_SEXP );
|
||||
|
||||
else if( aFileName.EndsWith( wxT( ".brd" ) ) )
|
||||
return LoadBoard( aFileName, IO_MGR::LEGACY );
|
||||
|
@ -95,7 +95,7 @@ bool SaveBoard( wxString& aFileName, BOARD* aBoard, IO_MGR::PCB_FILE_T aFormat )
|
|||
|
||||
bool SaveBoard( wxString& aFileName, BOARD* aBoard )
|
||||
{
|
||||
return SaveBoard( aFileName, aBoard, IO_MGR::KICAD );
|
||||
return SaveBoard( aFileName, aBoard, IO_MGR::KICAD_SEXP );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -866,9 +866,11 @@ public:
|
|||
|
||||
/**
|
||||
* Function ImportFile
|
||||
* import a board file and sets file path to current project path.
|
||||
* load the given filename but sets the path to the current project path.
|
||||
* @param full filepath of file to be imported.
|
||||
* @param aFileType PCB_FILE_T value for filetype
|
||||
*/
|
||||
bool ImportFile( const wxString aFileName) override;
|
||||
bool ImportFile( const wxString& aFileName, int aFileType ) override;
|
||||
|
||||
/**
|
||||
* Function SavePcbFile
|
||||
|
|
Loading…
Reference in New Issue