Commit 945f5f1e authored by charras's avatar charras

eeschema: code cleaning.

parent 0750a215
...@@ -5,6 +5,12 @@ Started 2007-June-11 ...@@ -5,6 +5,12 @@ Started 2007-June-11
Please add newer entries at the top, list the date and your name with Please add newer entries at the top, list the date and your name with
email address. email address.
2008-Apr-09 UPDATE Jean-Pierre Charras <jean-pierre.charras@inpg.fr>
================================================================================
+eeschema
code cleaning.
2008-Apr-09 UPDATE Jean-Pierre Charras <jean-pierre.charras@inpg.fr> 2008-Apr-09 UPDATE Jean-Pierre Charras <jean-pierre.charras@inpg.fr>
================================================================================ ================================================================================
+eeschema +eeschema
......
...@@ -133,7 +133,14 @@ CmpListStruct* AllocateCmpListStrct( int numcomponents ) ...@@ -133,7 +133,14 @@ CmpListStruct* AllocateCmpListStrct( int numcomponents )
} }
/* qsort function to annotate items by their position. */ /* qsort function to annotate items by their position.
* Components are sorted
* by reference
* if same reference: by sheet
* if same sheet, by X pos
* if same X pos, by Y pos
* if same Y pos, by time stamp
*/
int AnnotateByPosition( const void* o1, const void* o2 ) int AnnotateByPosition( const void* o1, const void* o2 )
{ {
CmpListStruct* item1 = (CmpListStruct*) o1; CmpListStruct* item1 = (CmpListStruct*) o1;
...@@ -214,6 +221,10 @@ void DeleteAnnotation( WinEDA_SchematicFrame* parent, bool annotateSchematic ) ...@@ -214,6 +221,10 @@ void DeleteAnnotation( WinEDA_SchematicFrame* parent, bool annotateSchematic )
* Compute the annotation of the components for the whole project, or the * Compute the annotation of the components for the whole project, or the
* current sheet only. All the components or the new ones only will be * current sheet only. All the components or the new ones only will be
* annotated. * annotated.
* @param parent = Schematic frame
* @param annotateSchematic : true = entire schematic annotation, false = current scheet only
* @param sortByPosition : true = annotate by sorting X position, false = annotate by sorting value
* @param resetAnnotation : true = remove previous annotation false = anotate new components only
*****************************************************************************/ *****************************************************************************/
void AnnotateComponents( WinEDA_SchematicFrame* parent, void AnnotateComponents( WinEDA_SchematicFrame* parent,
bool annotateSchematic, bool annotateSchematic,
...@@ -277,10 +288,10 @@ void AnnotateComponents( WinEDA_SchematicFrame* parent, ...@@ -277,10 +288,10 @@ void AnnotateComponents( WinEDA_SchematicFrame* parent,
if( sortByPosition ) if( sortByPosition )
qsort( BaseListeCmp, NbOfCmp, sizeof(CmpListStruct), qsort( BaseListeCmp, NbOfCmp, sizeof(CmpListStruct),
( int( * ) ( const void*, const void* ) )AnnotateByValue ); ( int( * ) ( const void*, const void* ) )AnnotateByPosition );
else else
qsort( BaseListeCmp, NbOfCmp, sizeof(CmpListStruct), qsort( BaseListeCmp, NbOfCmp, sizeof(CmpListStruct),
( int( * ) ( const void*, const void* ) )AnnotateByPosition ); ( int( * ) ( const void*, const void* ) ) AnnotateByValue);
/* Recalculate reference numbers */ /* Recalculate reference numbers */
ComputeReferenceNumber( BaseListeCmp, NbOfCmp ); ComputeReferenceNumber( BaseListeCmp, NbOfCmp );
......
...@@ -331,6 +331,9 @@ bool WinEDA_AnnotateFrame::GetResetItems( void ) ...@@ -331,6 +331,9 @@ bool WinEDA_AnnotateFrame::GetResetItems( void )
} }
bool WinEDA_AnnotateFrame::GetSortOrder( void ) bool WinEDA_AnnotateFrame::GetSortOrder( void )
/**
* @return true if annotation by position, false if annotation by value
*/
{ {
wxASSERT_MSG( (m_rbSortByPosition != NULL) && wxASSERT_MSG( (m_rbSortByPosition != NULL) &&
m_rbSortByPosition->IsKindOf( CLASSINFO( wxRadioButton ) ), m_rbSortByPosition->IsKindOf( CLASSINFO( wxRadioButton ) ),
......
...@@ -77,6 +77,82 @@ DrawSheetStruct::~DrawSheetStruct() ...@@ -77,6 +77,82 @@ DrawSheetStruct::~DrawSheetStruct()
} }
/**********************************************/
bool DrawSheetStruct::Save( FILE *f )
/***********************************************/
/* Routine utilisee dans la routine precedente.
Assure la sauvegarde de la structure LibItemStruct
*/
{
int ii;
bool Failed = FALSE;
DrawSheetLabelStruct * SheetLabel;
fprintf(f, "$Sheet\n");
if (fprintf(f, "S %-4d %-4d %-4d %-4d\n",
m_Pos.x,m_Pos.y,
m_Size.x,m_Size.y) == EOF){
Failed = TRUE; return(Failed);
}
//save the unique timestamp, like other shematic parts.
if( fprintf(f, "U %8.8lX\n", m_TimeStamp) == EOF ){
Failed = TRUE; return(Failed);
}
/* Generation de la liste des 2 textes (sheetname et filename) */
if ( ! m_SheetName.IsEmpty())
{
if(fprintf(f,"F0 \"%s\" %d\n", CONV_TO_UTF8(m_SheetName), m_SheetNameSize) == EOF)
{
Failed = TRUE; return(Failed);
}
}
if( ! GetFileName().IsEmpty())
{
if(fprintf(f,"F1 \"%s\" %d\n", CONV_TO_UTF8(GetFileName()), m_FileNameSize) == EOF)
{
Failed = TRUE; return(Failed);
}
}
/* Generation de la liste des labels (entrees) de la sous feuille */
ii = 2;
SheetLabel = m_Label;
while( SheetLabel != NULL )
{
int type = 'U', side = 'L';
if( SheetLabel->m_Text.IsEmpty() ) continue;
if( SheetLabel->m_Edge ) side = 'R';
switch(SheetLabel->m_Shape)
{
case NET_INPUT: type = 'I'; break;
case NET_OUTPUT: type = 'O'; break;
case NET_BIDI: type = 'B'; break;
case NET_TRISTATE: type = 'T'; break;
case NET_UNSPECIFIED: type = 'U'; break;
}
if(fprintf(f,"F%d \"%s\" %c %c %-3d %-3d %-3d\n", ii,
CONV_TO_UTF8(SheetLabel->m_Text), type, side,
SheetLabel->m_Pos.x, SheetLabel->m_Pos.y,
SheetLabel->m_Size.x) == EOF)
{
Failed = TRUE; break;
}
ii++;
SheetLabel = (DrawSheetLabelStruct*)SheetLabel->Pnext;
}
fprintf(f, "$EndSheet\n");
return(Failed);
}
/***********************************************/ /***********************************************/
DrawSheetStruct* DrawSheetStruct::GenCopy() DrawSheetStruct* DrawSheetStruct::GenCopy()
/***********************************************/ /***********************************************/
......
/********************************************/
/* Definitions for the EESchema program: */
/********************************************/
#ifndef CLASS_DRAWSHEET_H
#define CLASS_DRAWSHEET_H
#ifndef eda_global
#define eda_global extern
#endif
#include "base_struct.h"
extern DrawSheetStruct* g_RootSheet;
class DrawSheetLabelStruct : public EDA_BaseStruct,
public EDA_TextStruct
{
public:
int m_Layer;
int m_Edge, m_Shape;
bool m_IsDangling; // TRUE non connected
public:
DrawSheetLabelStruct( DrawSheetStruct* parent,
const wxPoint& pos = wxPoint( 0, 0 ),
const wxString& text = wxEmptyString );
~DrawSheetLabelStruct() { }
virtual wxString GetClass() const
{
return wxT( "DrawSheetLabelStruct" );
}
DrawSheetLabelStruct* GenCopy();
DrawSheetLabelStruct* Next()
{ return (DrawSheetLabelStruct*) Pnext; }
void Place( WinEDA_DrawFrame* frame, wxDC* DC );
virtual void Draw( WinEDA_DrawPanel* panel, wxDC* DC, const wxPoint& offset,
int draw_mode, int Color = -1 );
};
/* class DrawSheetStruct
* This class is the sheet symbol placed in a schematic, and is the entry point for a sub schematic
*/
WX_DEFINE_ARRAY( DrawSheetStruct *, SheetGrowArray );
class DrawSheetStruct : public EDA_BaseStruct /*public SCH_SCREEN*/ /* Gestion de la hierarchie */
{
public:
wxString m_SheetName; //this is equivalent to C101 for components:
// it is stored in F0 ... of the file.
private:
wxString m_FileName; //also in SCH_SCREEN (redundant),
//but need it here for loading after
//reading the sheet description from file.
public:
int m_SheetNameSize; // Size (height) of the text, used to draw the name
int m_FileNameSize; // Size (height) of the text, used to draw the name
wxPoint m_Pos;
wxSize m_Size; /* Position and Size of sheet symbol */
int m_Layer;
DrawSheetLabelStruct* m_Label; /* Points de connection, linked list.*/
int m_NbLabel; /* Nombre de points de connexion */
SCH_SCREEN* m_AssociatedScreen; /* Associated Screen which handle the physical data
* In complex hierarchies we can have many DrawSheetStruct using the same data
*/
int m_SheetNumber; // sheet number (used for info)
int m_NumberOfSheets; // Sheets count in the whole schematic (used for info)
public:
DrawSheetStruct( const wxPoint& pos = wxPoint( 0, 0 ) );
~DrawSheetStruct();
virtual wxString GetClass() const
{
return wxT( "DrawSheetStruct" );
}
/** Function Save
* Write on file a DrawSheetStruct description
* @param f = output file
* return an error: false if ok, true if error
*/
bool Save( FILE *f );
void Place( WinEDA_DrawFrame* frame, wxDC* DC );
DrawSheetStruct* GenCopy();
void Display_Infos( WinEDA_DrawFrame* frame );
void CleanupSheet( WinEDA_SchematicFrame* frame, wxDC* DC );
virtual void Draw( WinEDA_DrawPanel* panel, wxDC* DC, const wxPoint& offset,
int draw_mode, int Color = -1 );
EDA_Rect GetBoundingBox();
void SwapData( DrawSheetStruct* copyitem );
void DeleteAnnotation( bool recurse );
int ComponentCount();
bool Load( WinEDA_SchematicFrame* frame );
bool SearchHierarchy( wxString filename, SCH_SCREEN** screen );
bool LocatePathOfScreen( SCH_SCREEN* screen, DrawSheetPath* list );
int CountSheets();
wxString GetFileName(void);
void SetFileName(const wxString & aFilename); // Set a new filename without changing anything else
bool ChangeFileName(WinEDA_SchematicFrame * aFrame, const wxString & aFileName); // Set a new filename and manage data and associated screen
//void RemoveSheet(DrawSheetStruct* sheet);
//to remove a sheet, just delete it
//-- the destructor should take care of everything else.
};
/**********************************************/
/* class to handle a series of sheets *********/
/* a 'path' so to speak.. *********************/
/**********************************************/
#define DSLSZ 32
class DrawSheetPath
{
public:
int m_numSheets;
DrawSheetStruct* m_sheets[DSLSZ];
DrawSheetPath();
~DrawSheetPath() { };
void Clear() { m_numSheets = 0; }
int Cmp( DrawSheetPath& d );
DrawSheetStruct* Last();
SCH_SCREEN* LastScreen();
EDA_BaseStruct* LastDrawList();
void Push( DrawSheetStruct* sheet );
DrawSheetStruct* Pop();
wxString Path();
wxString PathHumanReadable();
void UpdateAllScreenReferences();
bool operator =( const DrawSheetPath& d1 );
bool operator ==( const DrawSheetPath& d1 );
bool operator !=( const DrawSheetPath& d1 );
};
/*******************************************************/
/* Class to handle the list of *Sheets* in a hierarchy */
/*******************************************************/
// sheets are not unique - can have many sheets with the same
// filename and the same SCH_SCREEN reference.
class EDA_SheetList
{
private:
DrawSheetPath* m_List;
int m_count; /* Number of sheets included in hierarchy,
* starting at the given sheet in constructor . the given sheet is counted
*/
int m_index;
DrawSheetPath m_currList;
public:
EDA_SheetList( DrawSheetStruct* sheet )
{
m_index = 0;
m_count = 0;
m_List = NULL;
if( sheet == NULL )
sheet = g_RootSheet;
BuildSheetList( sheet );
}
~EDA_SheetList()
{
if( m_List )
{
free( m_List );
}
m_List = NULL;
}
int GetCount() { return m_count; }
DrawSheetPath* GetFirst();
DrawSheetPath* GetNext();
DrawSheetPath* GetSheet( int index );
private:
void BuildSheetList( DrawSheetStruct* sheet );
};
#endif /* CLASS_DRAWSHEET_H */
...@@ -182,6 +182,14 @@ void SCH_SCREEN::AddToDrawList( EDA_BaseStruct* st ) ...@@ -182,6 +182,14 @@ void SCH_SCREEN::AddToDrawList( EDA_BaseStruct* st )
/* Class EDA_ScreenList to handle the list of screens in a hierarchy */ /* Class EDA_ScreenList to handle the list of screens in a hierarchy */
/*********************************************************************/ /*********************************************************************/
/********************************/
EDA_ScreenList::EDA_ScreenList()
/********************************/
{
m_Index = 0;
BuildScreenList( g_RootSheet );
}
/*****************************************/ /*****************************************/
SCH_SCREEN* EDA_ScreenList::GetFirst() SCH_SCREEN* EDA_ScreenList::GetFirst()
/*****************************************/ /*****************************************/
......
...@@ -16,11 +16,6 @@ ...@@ -16,11 +16,6 @@
#define NB_MAX_SHEET 500 #define NB_MAX_SHEET 500
/* Forward declarations */
class DrawSheetStruct;
extern DrawSheetStruct* g_RootSheet;
class SCH_SCREEN : public BASE_SCREEN class SCH_SCREEN : public BASE_SCREEN
{ {
public: public:
...@@ -55,178 +50,6 @@ public: ...@@ -55,178 +50,6 @@ public:
}; };
class DrawSheetLabelStruct : public EDA_BaseStruct,
public EDA_TextStruct
{
public:
int m_Layer;
int m_Edge, m_Shape;
bool m_IsDangling; // TRUE non connected
public:
DrawSheetLabelStruct( DrawSheetStruct* parent,
const wxPoint& pos = wxPoint( 0, 0 ),
const wxString& text = wxEmptyString );
~DrawSheetLabelStruct() { }
virtual wxString GetClass() const
{
return wxT( "DrawSheetLabelStruct" );
}
DrawSheetLabelStruct* GenCopy();
DrawSheetLabelStruct* Next()
{ return (DrawSheetLabelStruct*) Pnext; }
void Place( WinEDA_DrawFrame* frame, wxDC* DC );
virtual void Draw( WinEDA_DrawPanel* panel, wxDC* DC, const wxPoint& offset,
int draw_mode, int Color = -1 );
};
/* class DrawSheetStruct
* This class is the sheet symbol placed in a schematic, and is the entry point for a sub schematic
*/
WX_DEFINE_ARRAY( DrawSheetStruct *, SheetGrowArray );
class DrawSheetStruct : public EDA_BaseStruct /*public SCH_SCREEN*/ /* Gestion de la hierarchie */
{
public:
wxString m_SheetName; //this is equivalent to C101 for components:
// it is stored in F0 ... of the file.
private:
wxString m_FileName; //also in SCH_SCREEN (redundant),
//but need it here for loading after
//reading the sheet description from file.
public:
int m_SheetNameSize; // Size (height) of the text, used to draw the name
int m_FileNameSize; // Size (height) of the text, used to draw the name
wxPoint m_Pos;
wxSize m_Size; /* Position and Size of sheet symbol */
int m_Layer;
DrawSheetLabelStruct* m_Label; /* Points de connection, linked list.*/
int m_NbLabel; /* Nombre de points de connexion */
SCH_SCREEN* m_AssociatedScreen; /* Associated Screen which handle the physical data
* In complex hierarchies we can have many DrawSheetStruct using the same data
*/
int m_SheetNumber; // sheet number (used for info)
int m_NumberOfSheets; // Sheets count in the whole schematic (used for info)
public:
DrawSheetStruct( const wxPoint& pos = wxPoint( 0, 0 ) );
~DrawSheetStruct();
virtual wxString GetClass() const
{
return wxT( "DrawSheetStruct" );
}
void Place( WinEDA_DrawFrame* frame, wxDC* DC );
DrawSheetStruct* GenCopy();
void Display_Infos( WinEDA_DrawFrame* frame );
void CleanupSheet( WinEDA_SchematicFrame* frame, wxDC* DC );
virtual void Draw( WinEDA_DrawPanel* panel, wxDC* DC, const wxPoint& offset,
int draw_mode, int Color = -1 );
EDA_Rect GetBoundingBox();
void SwapData( DrawSheetStruct* copyitem );
void DeleteAnnotation( bool recurse );
int ComponentCount();
bool Load( WinEDA_SchematicFrame* frame );
bool SearchHierarchy( wxString filename, SCH_SCREEN** screen );
bool LocatePathOfScreen( SCH_SCREEN* screen, DrawSheetPath* list );
int CountSheets();
wxString GetFileName(void);
void SetFileName(const wxString & aFilename); // Set a new filename without changing anything else
bool ChangeFileName(WinEDA_SchematicFrame * aFrame, const wxString & aFileName); // Set a new filename and manage data and associated screen
//void RemoveSheet(DrawSheetStruct* sheet);
//to remove a sheet, just delete it
//-- the destructor should take care of everything else.
};
/**********************************************/
/* class to handle a series of sheets *********/
/* a 'path' so to speak.. *********************/
/**********************************************/
#define DSLSZ 32
class DrawSheetPath
{
public:
int m_numSheets;
DrawSheetStruct* m_sheets[DSLSZ];
DrawSheetPath();
~DrawSheetPath() { };
void Clear() { m_numSheets = 0; }
int Cmp( DrawSheetPath& d );
DrawSheetStruct* Last();
SCH_SCREEN* LastScreen();
EDA_BaseStruct* LastDrawList();
void Push( DrawSheetStruct* sheet );
DrawSheetStruct* Pop();
wxString Path();
wxString PathHumanReadable();
void UpdateAllScreenReferences();
bool operator =( const DrawSheetPath& d1 );
bool operator ==( const DrawSheetPath& d1 );
bool operator !=( const DrawSheetPath& d1 );
};
/*******************************************************/
/* Class to handle the list of *Sheets* in a hierarchy */
/*******************************************************/
// sheets are not unique - can have many sheets with the same
// filename and the same SCH_SCREEN reference.
class EDA_SheetList
{
private:
DrawSheetPath* m_List;
int m_count; /* Number of sheets included in hierarchy,
* starting at the given sheet in constructor . the given sheet is counted
*/
int m_index;
DrawSheetPath m_currList;
public:
EDA_SheetList( DrawSheetStruct* sheet )
{
m_index = 0;
m_count = 0;
m_List = NULL;
if( sheet == NULL )
sheet = g_RootSheet;
BuildSheetList( sheet );
}
~EDA_SheetList()
{
if( m_List )
{
free( m_List );
}
m_List = NULL;
}
int GetCount() { return m_count; }
DrawSheetPath* GetFirst();
DrawSheetPath* GetNext();
DrawSheetPath* GetSheet( int index );
private:
void BuildSheetList( DrawSheetStruct* sheet );
};
/********************************************************/ /********************************************************/
/* Class to handle the list of *screens* in a hierarchy */ /* Class to handle the list of *screens* in a hierarchy */
/********************************************************/ /********************************************************/
...@@ -240,13 +63,7 @@ private: ...@@ -240,13 +63,7 @@ private:
unsigned int m_Index; unsigned int m_Index;
public: public:
EDA_ScreenList() EDA_ScreenList();
{
m_Index = 0;
BuildScreenList( g_RootSheet );
}
~EDA_ScreenList() { } ~EDA_ScreenList() { }
int GetCount() { return m_List.GetCount(); } int GetCount() { return m_List.GetCount(); }
SCH_SCREEN* GetFirst(); SCH_SCREEN* GetFirst();
......
...@@ -25,6 +25,18 @@ WX_DEFINE_OBJARRAY( ArrayOfSheetLists ); ...@@ -25,6 +25,18 @@ WX_DEFINE_OBJARRAY( ArrayOfSheetLists );
/* class SCH_COMPONENT */ /* class SCH_COMPONENT */
/***************************/ /***************************/
/** Function AddHierarchicalReference
* Add a full hierachical reference (path + local reference)
* @param path = hierarchical path (/<sheet timestamp>/component timestamp> like /05678E50/A23EF560)
* @param ref = local reference like C45, R56
*/
void SCH_COMPONENT::AddHierarchicalReference( const wxString& path, const wxString& ref )
{
m_Paths.Add( path );
m_References.Add( ref );
}
/****************************************************************/ /****************************************************************/
const wxString& ReturnDefaultFieldName( int aFieldNdx ) const wxString& ReturnDefaultFieldName( int aFieldNdx )
/****************************************************************/ /****************************************************************/
...@@ -118,15 +130,16 @@ const wxString SCH_COMPONENT::GetRef( DrawSheetPath* sheet ) ...@@ -118,15 +130,16 @@ const wxString SCH_COMPONENT::GetRef( DrawSheetPath* sheet )
/***********************************************************************/ /***********************************************************************/
void SCH_COMPONENT::SetRef( DrawSheetPath* sheet, wxString ref ) void SCH_COMPONENT::SetRef( DrawSheetPath* sheet, const wxString& ref )
/***********************************************************************/ /***********************************************************************/
{ {
//check to see if it is already there before inserting it //check to see if it is already there before inserting it
wxString path = GetPath( sheet ); wxString path = GetPath( sheet );
// printf( "SetRef path: %s ref: %s\n", CONV_TO_UTF8( path ), CONV_TO_UTF8( ref ) ); // Debug // printf( "SetRef path: %s ref: %s\n", CONV_TO_UTF8( path ), CONV_TO_UTF8( ref ) ); // Debug
unsigned int i; unsigned int i;
bool notInArray = true; bool notInArray = true;
for( i = 0; i<m_Paths.GetCount(); i++ ) for( i = 0; i<m_Paths.GetCount(); i++ )
{ {
if( m_Paths[i].Cmp( path ) == 0 ) if( m_Paths[i].Cmp( path ) == 0 )
...@@ -162,6 +175,7 @@ void SCH_COMPONENT::ClearRefs() ...@@ -162,6 +175,7 @@ void SCH_COMPONENT::ClearRefs()
{ {
m_Paths.Empty(); m_Paths.Empty();
m_References.Empty(); m_References.Empty();
m_PartPerPackageSelections.Empty();
} }
...@@ -179,7 +193,7 @@ const wxString& SCH_COMPONENT::GetFieldValue( int aFieldNdx ) const ...@@ -179,7 +193,7 @@ const wxString& SCH_COMPONENT::GetFieldValue( int aFieldNdx ) const
/*******************************************************************/ /*******************************************************************/
SCH_COMPONENT::SCH_COMPONENT( const wxPoint& aPos ) : SCH_COMPONENT::SCH_COMPONENT( const wxPoint& aPos ) :
SCH_ITEM( NULL, TYPE_SCH_COMPONENT ) SCH_ITEM( NULL, TYPE_SCH_COMPONENT )
/*******************************************************************/ /*******************************************************************/
{ {
int ii; int ii;
...@@ -210,8 +224,6 @@ SCH_COMPONENT::SCH_COMPONENT( const wxPoint& aPos ) : ...@@ -210,8 +224,6 @@ SCH_COMPONENT::SCH_COMPONENT( const wxPoint& aPos ) :
m_Field[VALUE].m_Layer = LAYER_VALUEPART; m_Field[VALUE].m_Layer = LAYER_VALUEPART;
m_Field[REFERENCE].m_Layer = LAYER_REFERENCEPART; m_Field[REFERENCE].m_Layer = LAYER_REFERENCEPART;
m_PinIsDangling = NULL;
m_PrefixString = wxString( _( "U" ) ); m_PrefixString = wxString( _( "U" ) );
} }
...@@ -364,7 +376,14 @@ void SCH_COMPONENT::ClearAnnotation() ...@@ -364,7 +376,14 @@ void SCH_COMPONENT::ClearAnnotation()
Entry = FindLibPart( m_ChipName.GetData(), wxEmptyString, FIND_ROOT ); Entry = FindLibPart( m_ChipName.GetData(), wxEmptyString, FIND_ROOT );
if( !Entry || !Entry->m_UnitSelectionLocked ) if( !Entry || !Entry->m_UnitSelectionLocked )
{
m_Multi = 1; m_Multi = 1;
m_PartPerPackageSelections.Empty();
for( i = 0; i< m_Paths.GetCount(); i++ )
{
m_PartPerPackageSelections.Add( wxT( "1" ) );
}
}
} }
...@@ -374,10 +393,10 @@ SCH_COMPONENT* SCH_COMPONENT::GenCopy() ...@@ -374,10 +393,10 @@ SCH_COMPONENT* SCH_COMPONENT::GenCopy()
{ {
SCH_COMPONENT* new_item = new SCH_COMPONENT( m_Pos ); SCH_COMPONENT* new_item = new SCH_COMPONENT( m_Pos );
int ii; int ii;
new_item->m_Multi = m_Multi; new_item->m_Multi = m_Multi;
new_item->m_ChipName = m_ChipName; new_item->m_ChipName = m_ChipName;
new_item->m_PrefixString = m_PrefixString; new_item->m_PrefixString = m_PrefixString;
//new_item->m_FlagControlMulti = m_FlagControlMulti; //new_item->m_FlagControlMulti = m_FlagControlMulti;
...@@ -512,15 +531,16 @@ void SCH_COMPONENT::SetRotationMiroir( int type_rotate ) ...@@ -512,15 +531,16 @@ void SCH_COMPONENT::SetRotationMiroir( int type_rotate )
} }
if( Transform ) if( Transform )
{ /* The new matrix transform is the old matrix transform modified by the {
* requested transformation, which is the TempMat transform (rot, mirror ..) /* The new matrix transform is the old matrix transform modified by the
* in order to have (in term of matrix transform): * requested transformation, which is the TempMat transform (rot, mirror ..)
* transform coord = new_m_Transform * coord * in order to have (in term of matrix transform):
* where transform coord is the coord modified by new_m_Transform from the initial * transform coord = new_m_Transform * coord
* value coord. * where transform coord is the coord modified by new_m_Transform from the initial
* new_m_Transform is computed (from old_m_Transform and TempMat) to have: * value coord.
* transform coord = old_m_Transform * coord * TempMat * new_m_Transform is computed (from old_m_Transform and TempMat) to have:
*/ * transform coord = old_m_Transform * coord * TempMat
*/
int NewMatrix[2][2]; int NewMatrix[2][2];
NewMatrix[0][0] = m_Transform[0][0] * TempMat[0][0] + NewMatrix[0][0] = m_Transform[0][0] * TempMat[0][0] +
...@@ -666,8 +686,8 @@ void SCH_COMPONENT::Show( int nestLevel, std::ostream& os ) ...@@ -666,8 +686,8 @@ void SCH_COMPONENT::Show( int nestLevel, std::ostream& os )
/***************************************************************************/ /***************************************************************************/
PartTextStruct::PartTextStruct( const wxPoint& pos, const wxString& text ) : PartTextStruct::PartTextStruct( const wxPoint& pos, const wxString& text ) :
EDA_BaseStruct( DRAW_PART_TEXT_STRUCT_TYPE ) EDA_BaseStruct( DRAW_PART_TEXT_STRUCT_TYPE ),
, EDA_TextStruct( text ) EDA_TextStruct( text )
/***************************************************************************/ /***************************************************************************/
{ {
m_Pos = pos; m_Pos = pos;
...@@ -725,16 +745,16 @@ EDA_Rect PartTextStruct::GetBoundaryBox() const ...@@ -725,16 +745,16 @@ EDA_Rect PartTextStruct::GetBoundaryBox() const
* *
*/ */
{ {
EDA_Rect BoundaryBox; EDA_Rect BoundaryBox;
int hjustify, vjustify; int hjustify, vjustify;
int textlen; int textlen;
int orient; int orient;
int dx, dy, x1, y1, x2, y2; int dx, dy, x1, y1, x2, y2;
SCH_COMPONENT* DrawLibItem = (SCH_COMPONENT*) m_Parent; SCH_COMPONENT* DrawLibItem = (SCH_COMPONENT*) m_Parent;
orient = m_Orient; orient = m_Orient;
wxPoint pos = DrawLibItem->m_Pos; wxPoint pos = DrawLibItem->m_Pos;
x1 = m_Pos.x - pos.x; x1 = m_Pos.x - pos.x;
y1 = m_Pos.y - pos.y; y1 = m_Pos.y - pos.y;
...@@ -822,3 +842,146 @@ EDA_Rect PartTextStruct::GetBoundaryBox() const ...@@ -822,3 +842,146 @@ EDA_Rect PartTextStruct::GetBoundaryBox() const
return BoundaryBox; return BoundaryBox;
} }
/**********************************/
bool SCH_COMPONENT::Save( FILE* f )
/**********************************/
/** Function Save
* Write on file a SCH_COMPONENT decscription
* @param f = output file
* return an error: false if ok, true if error
*/
{
int ii, Failed = FALSE;
char Name1[256], Name2[256];
int hjustify, vjustify;
//this is redundant with the AR entries below, but it makes the
//files backwards-compatible.
if( m_References.GetCount() > 0 )
strncpy( Name1, CONV_TO_UTF8( m_References[0] ), sizeof(Name1) );
else
{
if( m_Field[REFERENCE].m_Text.IsEmpty() )
strncpy( Name1, CONV_TO_UTF8( m_PrefixString ), sizeof(Name1) );
else
strncpy( Name1, CONV_TO_UTF8( m_Field[REFERENCE].m_Text ), sizeof(Name1) );
}
for( ii = 0; ii < (int) strlen( Name1 ); ii++ )
{
if( Name1[ii] <= ' ' )
Name1[ii] = '~';
}
if( !m_ChipName.IsEmpty() )
{
strncpy( Name2, CONV_TO_UTF8( m_ChipName ), sizeof(Name2) );
for( ii = 0; ii < (int) strlen( Name2 ); ii++ )
if( Name2[ii] <= ' ' )
Name2[ii] = '~';
}
else
strncpy( Name2, NULL_STRING, sizeof(Name2) );
fprintf( f, "$Comp\n" );
if( fprintf( f, "L %s %s\n", Name2, Name1 ) == EOF )
{
Failed = TRUE;
return Failed;
}
/* Generation de numero d'unit, convert et Time Stamp*/
if( fprintf( f, "U %d %d %8.8lX\n", m_Multi, m_Convert, m_TimeStamp ) == EOF )
{
Failed = TRUE; return Failed;
}
/* Sortie de la position */
if( fprintf( f, "P %d %d\n", m_Pos.x, m_Pos.y ) == EOF )
{
Failed = TRUE; return Failed;
}
unsigned int i;
for( i = 0; i< m_References.GetCount(); i++ )
{
/*format:
* AR Path="/140/2" Ref="C99"
* where 140 is the uid of the contianing sheet
* and 2 is the timestamp of this component.
* (timestamps are actually 8 hex chars)
* Ref is the conventional component reference for this 'path'
*/
if( fprintf( f, "AR Path=\"%s\" Ref=\"%s\" \n",
CONV_TO_UTF8( m_Paths[i] ),
CONV_TO_UTF8( m_References[i] ) ) == EOF )
{
Failed = TRUE; break;
}
}
for( ii = 0; ii < NUMBER_OF_FIELDS; ii++ )
{
PartTextStruct* field = &m_Field[ii];
if( field->m_Text.IsEmpty() )
continue;
hjustify = 'C';
if( field->m_HJustify == GR_TEXT_HJUSTIFY_LEFT )
hjustify = 'L';
else if( field->m_HJustify == GR_TEXT_HJUSTIFY_RIGHT )
hjustify = 'R';
vjustify = 'C';
if( field->m_VJustify == GR_TEXT_VJUSTIFY_BOTTOM )
vjustify = 'B';
else if( field->m_VJustify == GR_TEXT_VJUSTIFY_TOP )
vjustify = 'T';
if( fprintf( f, "F %d \"%s\" %c %-3d %-3d %-3d %4.4X %c %c", ii,
CONV_TO_UTF8( field->m_Text ),
field->m_Orient == TEXT_ORIENT_HORIZ ? 'H' : 'V',
field->m_Pos.x, field->m_Pos.y,
field->m_Size.x,
field->m_Attributs,
hjustify, vjustify ) == EOF )
{
Failed = TRUE; break;
}
// Save field name, if necessary
if( ii >= FIELD1 && !field->m_Name.IsEmpty() )
{
wxString fieldname = ReturnDefaultFieldName( ii );
if( fieldname != field->m_Name )
if( fprintf( f, " \"%s\"", CONV_TO_UTF8( field->m_Name ) ) == EOF )
{
Failed = TRUE; break;
}
}
if( fprintf( f, "\n" ) == EOF )
{
Failed = TRUE; break;
}
}
if( Failed )
return Failed;
/* Generation du num unit, position, box ( ancienne norme )*/
if( fprintf( f, "\t%-4d %-4d %-4d\n", m_Multi, m_Pos.x, m_Pos.y ) == EOF )
{
Failed = TRUE; return Failed;
}
if( fprintf( f, "\t%-4d %-4d %-4d %-4d\n",
m_Transform[0][0],
m_Transform[0][1],
m_Transform[1][0],
m_Transform[1][1] ) == EOF )
{
Failed = TRUE; return Failed;
}
fprintf( f, "$EndComp\n" );
return Failed;
}
...@@ -79,20 +79,20 @@ public: ...@@ -79,20 +79,20 @@ public:
wxPoint m_Pos; wxPoint m_Pos;
wxString m_ChipName; /* Key to look for in the library, i.e. "74LS00". */ wxString m_ChipName; /* Key to look for in the library, i.e. "74LS00". */
wxString m_PrefixString; /* C, R, U, Q etc - the first character which typically indicates what the component is.
* determined, upon placement, from the library component.
* determined, upon file load, by the first non-digits in the reference fields. */
PartTextStruct m_Field[NUMBER_OF_FIELDS]; PartTextStruct m_Field[NUMBER_OF_FIELDS];
//int m_FlagControlMulti; //int m_FlagControlMulti;
ArrayOfSheetLists m_UsedOnSheets; ArrayOfSheetLists m_UsedOnSheets; // Used as flags when calculating netlist
int m_Convert; /* Gestion (management) des mutiples representations (ex: conversion De Morgan) */ int m_Convert; /* Gestion (management) des mutiples representations (ex: conversion De Morgan) */
int m_Transform[2][2]; /* The rotation/mirror transformation matrix. */ int m_Transform[2][2]; /* The rotation/mirror transformation matrix. */
bool* m_PinIsDangling; // liste des indicateurs de pin non connectee
private:
wxArrayString m_Paths; // /sheet1/C102, /sh2/sh1/U32 etc. wxArrayString m_Paths; // /sheet1/C102, /sh2/sh1/U32 etc.
wxArrayString m_References; // C102, U32 etc. wxArrayString m_References; // C102, U32 etc.
wxArrayString m_PartPerPackageSelections; // "1", "2" etc. when a component has more than 1 partper package wxArrayString m_PartPerPackageSelections; // "1", "2" etc. when a component has more than 1 partper package
wxString m_PrefixString; /* C, R, U, Q etc - the first character which typically indicates what the component is.
* determined, upon placement, from the library component.
* determined, upon file load, by the first non-digits in the reference fields. */
public: public:
SCH_COMPONENT( const wxPoint& pos = wxPoint( 0, 0 ) ); SCH_COMPONENT( const wxPoint& pos = wxPoint( 0, 0 ) );
...@@ -103,6 +103,12 @@ public: ...@@ -103,6 +103,12 @@ public:
return wxT( "SCH_COMPONENT" ); return wxT( "SCH_COMPONENT" );
} }
/** Function Save
* Write on file a SCH_COMPONENT decscription
* @param f = output file
* return an error: false if ok, true if error
*/
bool Save( FILE *f );
SCH_COMPONENT* GenCopy(); SCH_COMPONENT* GenCopy();
void SetRotationMiroir( int type ); void SetRotationMiroir( int type );
...@@ -138,8 +144,11 @@ public: ...@@ -138,8 +144,11 @@ public:
//returns a unique ID, in the form of a path. //returns a unique ID, in the form of a path.
wxString GetPath( DrawSheetPath* sheet ); wxString GetPath( DrawSheetPath* sheet );
const wxString GetRef( DrawSheetPath* sheet ); const wxString GetRef( DrawSheetPath* sheet );
void SetRef( DrawSheetPath* sheet, wxString ref ); void SetRef( DrawSheetPath* sheet, const wxString & ref );
void ClearRefs(); void ClearRefs();
void AddHierarchicalReference(const wxString & path, const wxString & ref);
int GetUnitSelection( DrawSheetPath* aSheet );
void SetUnitSelection( DrawSheetPath* aSheet, int aUnitSelection );
#if defined (DEBUG) #if defined (DEBUG)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
/****************************************/ /****************************************/
#include "fctsys.h" #include "fctsys.h"
#include "gr_basic.h" //#include "gr_basic.h"
#include "common.h" #include "common.h"
#include "program.h" #include "program.h"
...@@ -633,8 +633,7 @@ static int ReadPartDescr( wxWindow* frame, char* Line, FILE* f, ...@@ -633,8 +633,7 @@ static int ReadPartDescr( wxWindow* frame, char* Line, FILE* f,
Name1[j] = 0; Name1[j] = 0;
wxString ref = CONV_FROM_UTF8(Name1); wxString ref = CONV_FROM_UTF8(Name1);
LibItemStruct->m_Paths.Add(path); LibItemStruct->AddHierarchicalReference(path, ref);
LibItemStruct->m_References.Add(ref);
LibItemStruct->m_Field[REFERENCE].m_Text = ref; LibItemStruct->m_Field[REFERENCE].m_Text = ref;
} }
if( Line[0] == 'F' ) if( Line[0] == 'F' )
......
...@@ -351,7 +351,7 @@ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, ...@@ -351,7 +351,7 @@ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame,
/********************************************************/ /********************************************************/
static void ClearUsedFlags( WinEDA_SchematicFrame* frame ) static void ClearUsedFlags( WinEDA_SchematicFrame* frame )
/********************************************************/ /********************************************************/
/* Clear flag FlagControlMulti, used in netlist generation */ /* Clear flag list, used in netlist generation */
{ {
SCH_SCREEN* screen; SCH_SCREEN* screen;
EDA_BaseStruct* DrawList; EDA_BaseStruct* DrawList;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "component_class.h" #include "component_class.h"
#include "class_screen.h" #include "class_screen.h"
#include "class_drawsheet.h"
#include "class_text-label.h" #include "class_text-label.h"
#define DRAWJUNCTION_SIZE 16 /* Rayon du symbole connexion */ #define DRAWJUNCTION_SIZE 16 /* Rayon du symbole connexion */
......
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
/* Fonctions externes */ /* Fonctions externes */
/* Fonctions Locales */ /* Fonctions Locales */
static int SavePartDescr( FILE *f, SCH_COMPONENT * LibItemStruct);
static int SaveSheetDescr( FILE *f, DrawSheetStruct * SheetStruct);
static void SaveLayers(FILE *f); static void SaveLayers(FILE *f);
/* Variables locales */ /* Variables locales */
...@@ -146,11 +144,11 @@ bool WinEDA_SchematicFrame::SaveEEFile(SCH_SCREEN *screen, int FileSave) ...@@ -146,11 +144,11 @@ bool WinEDA_SchematicFrame::SaveEEFile(SCH_SCREEN *screen, int FileSave)
switch(Phead->Type()) switch(Phead->Type())
{ {
case TYPE_SCH_COMPONENT: /* Its a library item. */ case TYPE_SCH_COMPONENT: /* Its a library item. */
SavePartDescr( f, (SCH_COMPONENT *) Phead); ((SCH_COMPONENT *) Phead)->Save( f );
break; break;
case DRAW_SHEET_STRUCT_TYPE: /* Its a Sheet item. */ case DRAW_SHEET_STRUCT_TYPE: /* Its a Sheet item. */
SaveSheetDescr( f, (DrawSheetStruct *) Phead); ((DrawSheetStruct *) Phead)->Save( f );
break; break;
case DRAW_SEGMENT_STRUCT_TYPE: /* Its a Segment item. */ case DRAW_SEGMENT_STRUCT_TYPE: /* Its a Segment item. */
...@@ -323,222 +321,6 @@ bool WinEDA_SchematicFrame::SaveEEFile(SCH_SCREEN *screen, int FileSave) ...@@ -323,222 +321,6 @@ bool WinEDA_SchematicFrame::SaveEEFile(SCH_SCREEN *screen, int FileSave)
} }
/*******************************************************************/
static int SavePartDescr( FILE *f, SCH_COMPONENT * LibItemStruct)
/*******************************************************************/
/* Routine utilisee dans la routine precedente.
Assure la sauvegarde de la structure LibItemStruct
*/
{
int ii, Failed = FALSE;
char Name1[256], Name2[256];
int hjustify, vjustify;
//this is redundant with the AR entries below, but it makes the
//files backwards-compatible.
if(LibItemStruct->m_References.GetCount() > 0)
strncpy(Name1, CONV_TO_UTF8(LibItemStruct->m_References[0]), sizeof(Name1));
else{
if(LibItemStruct->m_Field[REFERENCE].m_Text.IsEmpty())
strncpy(Name1, CONV_TO_UTF8(LibItemStruct->m_PrefixString),sizeof(Name1));
else
strncpy(Name1, CONV_TO_UTF8(LibItemStruct->m_Field[REFERENCE].m_Text),sizeof(Name1));
}
for (ii = 0; ii < (int)strlen(Name1); ii++){
if (Name1[ii] <= ' ') Name1[ii] = '~';
}
if ( ! LibItemStruct->m_ChipName.IsEmpty() )
{
strncpy(Name2, CONV_TO_UTF8(LibItemStruct->m_ChipName),sizeof(Name2));
for (ii = 0; ii < (int)strlen(Name2); ii++)
if (Name2[ii] <= ' ') Name2[ii] = '~';
}
else strncpy(Name2, NULL_STRING,sizeof(Name2));
fprintf(f, "$Comp\n");
if(fprintf (f, "L %s %s\n", Name2, Name1) == EOF)
{
Failed = TRUE;
return(Failed);
}
/* Generation de numero d'unit, convert et Time Stamp*/
if(fprintf(f, "U %d %d %8.8lX\n",
LibItemStruct->m_Multi,
LibItemStruct->m_Convert,
LibItemStruct->m_TimeStamp) == EOF)
{
Failed = TRUE; return(Failed);
}
/* Sortie de la position */
if(fprintf(f, "P %d %d\n",
LibItemStruct->m_Pos.x, LibItemStruct->m_Pos.y) == EOF)
{
Failed = TRUE; return(Failed);
}
unsigned int i;
for(i=0; i< LibItemStruct->m_References.GetCount(); i++){
/*format:
AR Path="/140/2" Ref="C99"
where 140 is the uid of the contianing sheet
and 2 is the timestamp of this component.
(timestamps are actually 8 hex chars)
Ref is the conventional component reference for this 'path'
*/
/*printf("AR Path=\"%s\" Ref=\"%s\" \n",
CONV_TO_UTF8( LibItemStruct->m_Paths[i]),
CONV_TO_UTF8( LibItemStruct->m_References[i] ) ); */
if( fprintf(f, "AR Path=\"%s\" Ref=\"%s\" \n",
CONV_TO_UTF8( LibItemStruct->m_Paths[i]),
CONV_TO_UTF8( LibItemStruct->m_References[i] ) ) == EOF )
{
Failed = TRUE; break;
}
}
for( ii = 0; ii < NUMBER_OF_FIELDS; ii++ )
{
PartTextStruct * field = & LibItemStruct->m_Field[ii];
if( field->m_Text.IsEmpty() ) continue;
hjustify = 'C';
if ( field->m_HJustify == GR_TEXT_HJUSTIFY_LEFT)
hjustify = 'L';
else if ( field->m_HJustify == GR_TEXT_HJUSTIFY_RIGHT)
hjustify = 'R';
vjustify = 'C';
if ( field->m_VJustify == GR_TEXT_VJUSTIFY_BOTTOM)
vjustify = 'B';
else if ( field->m_VJustify == GR_TEXT_VJUSTIFY_TOP)
vjustify = 'T';
if( fprintf(f,"F %d \"%s\" %c %-3d %-3d %-3d %4.4X %c %c", ii,
CONV_TO_UTF8(field->m_Text),
field->m_Orient == TEXT_ORIENT_HORIZ ? 'H' : 'V',
field->m_Pos.x, field->m_Pos.y,
field->m_Size.x,
field->m_Attributs,
hjustify, vjustify) == EOF)
{
Failed = TRUE; break;
}
// Save field name, if necessary
if ( ii >= FIELD1 && ! field->m_Name.IsEmpty() )
{
wxString fieldname = ReturnDefaultFieldName(ii);
if ( fieldname != field->m_Name )
if( fprintf(f," \"%s\"", CONV_TO_UTF8(field->m_Name) ) == EOF)
{
Failed = TRUE; break;
}
}
if( fprintf(f,"\n") == EOF)
{
Failed = TRUE; break;
}
}
if (Failed) return(Failed);
/* Generation du num unit, position, box ( ancienne norme )*/
if(fprintf(f, "\t%-4d %-4d %-4d\n",
LibItemStruct->m_Multi,
LibItemStruct->m_Pos.x, LibItemStruct->m_Pos.y) == EOF)
{
Failed = TRUE; return(Failed);
}
if( fprintf(f, "\t%-4d %-4d %-4d %-4d\n",
LibItemStruct->m_Transform[0][0],
LibItemStruct->m_Transform[0][1],
LibItemStruct->m_Transform[1][0],
LibItemStruct->m_Transform[1][1]) == EOF)
{
Failed = TRUE; return(Failed);
}
fprintf(f, "$EndComp\n");
return(Failed);
}
/*******************************************************************/
static int SaveSheetDescr( FILE *f, DrawSheetStruct * SheetStruct)
/*******************************************************************/
/* Routine utilisee dans la routine precedente.
Assure la sauvegarde de la structure LibItemStruct
*/
{
int ii;
int Failed = FALSE;
DrawSheetLabelStruct * SheetLabel;
fprintf(f, "$Sheet\n");
if (fprintf(f, "S %-4d %-4d %-4d %-4d\n",
SheetStruct->m_Pos.x,SheetStruct->m_Pos.y,
SheetStruct->m_Size.x,SheetStruct->m_Size.y) == EOF){
Failed = TRUE; return(Failed);
}
//save the unique timestamp, like other shematic parts.
if( fprintf(f, "U %8.8lX\n", SheetStruct->m_TimeStamp) == EOF ){
Failed = TRUE; return(Failed);
}
/* Generation de la liste des 2 textes (sheetname et filename) */
if ( ! SheetStruct->m_SheetName.IsEmpty())
{
if(fprintf(f,"F0 \"%s\" %d\n", CONV_TO_UTF8(SheetStruct->m_SheetName),
SheetStruct->m_SheetNameSize) == EOF)
{
Failed = TRUE; return(Failed);
}
}
if( ! SheetStruct->GetFileName().IsEmpty())
{
if(fprintf(f,"F1 \"%s\" %d\n",
CONV_TO_UTF8(SheetStruct->GetFileName()),
SheetStruct->m_FileNameSize) == EOF)
{
Failed = TRUE; return(Failed);
}
}
/* Generation de la liste des labels (entrees) de la sous feuille */
ii = 2;
SheetLabel = SheetStruct->m_Label;
while( SheetLabel != NULL )
{
int type = 'U', side = 'L';
if( SheetLabel->m_Text.IsEmpty() ) continue;
if( SheetLabel->m_Edge ) side = 'R';
switch(SheetLabel->m_Shape)
{
case NET_INPUT: type = 'I'; break;
case NET_OUTPUT: type = 'O'; break;
case NET_BIDI: type = 'B'; break;
case NET_TRISTATE: type = 'T'; break;
case NET_UNSPECIFIED: type = 'U'; break;
}
if(fprintf(f,"F%d \"%s\" %c %c %-3d %-3d %-3d\n", ii,
CONV_TO_UTF8(SheetLabel->m_Text), type, side,
SheetLabel->m_Pos.x, SheetLabel->m_Pos.y,
SheetLabel->m_Size.x) == EOF)
{
Failed = TRUE; break;
}
ii++;
SheetLabel = (DrawSheetLabelStruct*)SheetLabel->Pnext;
}
fprintf(f, "$EndSheet\n");
return(Failed);
}
/****************************/ /****************************/
static void SaveLayers(FILE *f) static void SaveLayers(FILE *f)
/****************************/ /****************************/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment