Commit 85dc98a5 authored by dickelbeck's avatar dickelbeck

more netclass work

parent f9f671d1
...@@ -4,6 +4,24 @@ KiCad ChangeLog 2009 ...@@ -4,6 +4,24 @@ KiCad ChangeLog 2009
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.
2009-Aug-16 UPDATE Dick Hollenbeck <dick@softplc.com>
================================================================================
++pcbnew
* first of my work on NETCLASSes, more to come.
wrote: BOARD* BOARD_ITEM::GetBoard(), int BOARD_CONNECTED_ITEM::GetClearance( BOARD_CONNECTED_ITEM* ),
rewrote: classes NETCLASS, NETCLASSES, BOARD::SynchronizeNetsAndNetClasses(),
added: NETCLASS::m_ViaDrillSize, NETCLASS::m_Description. Removed netname from
EQUIPOTs in *.brd file (were redundant with NETCLASSes in *.brd file). NETCLASSes
now follow EQUIPOTs in *.brd file.
NETs and NETCLASSes are linked by pointer from class NET.
BOARD::SynchronizeNetsAndNetClasses() will validate pointers, NETs and NETCLASSes.
* Pay particular attention to the new:
int BOARD_CONNECTED_ITEM::GetClearance( BOARD_CONNECTED_ITEM* aItem ) const
which considers two netclasses and finds the largest distance from the two
involved.
* I would love to see class NETINFO_ITEM renamed to NET.
2009-Aug-6 UPDATE Dick Hollenbeck <dick@softplc.com> 2009-Aug-6 UPDATE Dick Hollenbeck <dick@softplc.com>
================================================================================ ================================================================================
++pcbnew ++pcbnew
......
...@@ -189,6 +189,7 @@ public: ...@@ -189,6 +189,7 @@ public:
{ {
wxMessageBox(wxT("virtual BOARD_ITEM::Move used, should not occur"), GetClass()); wxMessageBox(wxT("virtual BOARD_ITEM::Move used, should not occur"), GetClass());
} }
/** /**
* Function Rotate * Function Rotate
* Rotate this object. * Rotate this object.
...@@ -199,6 +200,7 @@ public: ...@@ -199,6 +200,7 @@ public:
{ {
wxMessageBox(wxT("virtual BOARD_ITEM::Rotate used, should not occur"), GetClass()); wxMessageBox(wxT("virtual BOARD_ITEM::Rotate used, should not occur"), GetClass());
} }
/** /**
* Function Flip * Function Flip
* Flip this object, i.e. change the board side for this object * Flip this object, i.e. change the board side for this object
...@@ -208,8 +210,16 @@ public: ...@@ -208,8 +210,16 @@ public:
{ {
wxMessageBox(wxT("virtual BOARD_ITEM::Flip used, should not occur"), GetClass()); wxMessageBox(wxT("virtual BOARD_ITEM::Flip used, should not occur"), GetClass());
} }
/**
* Function GetBoard
* returns the BOARD in which this BOARD_ITEM resides, or NULL if none.
*/
virtual BOARD* GetBoard() const;
}; };
class NETCLASS;
/** /**
* Class BOARD_CONNECTED_ITEM * Class BOARD_CONNECTED_ITEM
...@@ -253,6 +263,19 @@ public: ...@@ -253,6 +263,19 @@ public:
*/ */
int GetZoneSubNet() const; int GetZoneSubNet() const;
void SetZoneSubNet( int aSubNetCode ); void SetZoneSubNet( int aSubNetCode );
/**
* Function GetClearance
* returns the clearance in 1/10000 inches to aItem from this BOARD_CONNECTED_ITEM.
*/
virtual int GetClearance( BOARD_CONNECTED_ITEM* aItem ) const;
/**
* Function GetNetClass
* returns the NETCLASS for this item.
*/
virtual NETCLASS* GetNetClass() const;
}; };
......
...@@ -20,7 +20,7 @@ wxPoint BOARD_ITEM::ZeroOffset( 0, 0 ); ...@@ -20,7 +20,7 @@ wxPoint BOARD_ITEM::ZeroOffset( 0, 0 );
/* Constructor */ /* Constructor */
BOARD::BOARD( EDA_BaseStruct* parent, WinEDA_BasePcbFrame* frame ) : BOARD::BOARD( EDA_BaseStruct* parent, WinEDA_BasePcbFrame* frame ) :
BOARD_ITEM( (BOARD_ITEM*)parent, TYPE_PCB ), BOARD_ITEM( (BOARD_ITEM*)parent, TYPE_PCB ),
m_NetClassesList(this) m_NetClasses(this)
{ {
m_PcbFrame = frame; m_PcbFrame = frame;
m_Status_Pcb = 0; // Mot d'etat: Bit 1 = Chevelu calcule m_Status_Pcb = 0; // Mot d'etat: Bit 1 = Chevelu calcule
...@@ -37,10 +37,6 @@ BOARD::BOARD( EDA_BaseStruct* parent, WinEDA_BasePcbFrame* frame ) : ...@@ -37,10 +37,6 @@ BOARD::BOARD( EDA_BaseStruct* parent, WinEDA_BasePcbFrame* frame ) :
m_Layer[layer].m_Name = ReturnPcbLayerName( layer, true ); m_Layer[layer].m_Name = ReturnPcbLayerName( layer, true );
m_Layer[layer].m_Type = LT_SIGNAL; m_Layer[layer].m_Type = LT_SIGNAL;
} }
// Add the default Netclass to list
NETCLASS * default_netclass = new NETCLASS(this);
m_NetClassesList.AddNetclass( default_netclass );
} }
...@@ -538,7 +534,7 @@ void BOARD::DisplayInfo( WinEDA_DrawFrame* frame ) ...@@ -538,7 +534,7 @@ void BOARD::DisplayInfo( WinEDA_DrawFrame* frame )
txt.Printf( wxT( "%d" ), GetNodesCount() ); txt.Printf( wxT( "%d" ), GetNodesCount() );
Affiche_1_Parametre( frame, POS_AFF_NBNODES, _( "Nodes" ), txt, DARKCYAN ); Affiche_1_Parametre( frame, POS_AFF_NBNODES, _( "Nodes" ), txt, DARKCYAN );
txt.Printf( wxT( "%d" ), m_NetInfo->GetNetsCount() ); txt.Printf( wxT( "%d" ), m_NetInfo->GetCount() );
Affiche_1_Parametre( frame, POS_AFF_NBNETS, _( "Nets" ), txt, RED ); Affiche_1_Parametre( frame, POS_AFF_NBNETS, _( "Nets" ), txt, RED );
/* These parameters are known only if the full ratsnest is available, /* These parameters are known only if the full ratsnest is available,
...@@ -822,7 +818,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR* inspector, const void* testData, ...@@ -822,7 +818,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR* inspector, const void* testData,
*/ */
NETINFO_ITEM* BOARD::FindNet( int anetcode ) const NETINFO_ITEM* BOARD::FindNet( int anetcode ) const
{ {
// the first valid netcode is 1 and the last is m_NetInfo->GetNetsCount()-1. // the first valid netcode is 1 and the last is m_NetInfo->GetCount()-1.
// zero is reserved for "no connection" and is not used. // zero is reserved for "no connection" and is not used.
// NULL is returned for non valid netcodes // NULL is returned for non valid netcodes
NETINFO_ITEM* item = m_NetInfo->GetNetItem( anetcode ); NETINFO_ITEM* item = m_NetInfo->GetNetItem( anetcode );
...@@ -850,7 +846,7 @@ NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname ) const ...@@ -850,7 +846,7 @@ NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname ) const
// zero is reserved for "no connection" and is not used. // zero is reserved for "no connection" and is not used.
if( !aNetname.IsEmpty() ) if( !aNetname.IsEmpty() )
{ {
for( unsigned ii = 1; ii < m_NetInfo->GetNetsCount(); ii++ ) for( unsigned ii = 1; ii < m_NetInfo->GetCount(); ii++ )
{ {
NETINFO_ITEM* item = m_NetInfo->GetNetItem( ii ); NETINFO_ITEM* item = m_NetInfo->GetNetItem( ii );
if( item && item->GetNetname() == aNetname ) if( item && item->GetNetname() == aNetname )
...@@ -909,15 +905,15 @@ static bool s_SortByNodes( const NETINFO_ITEM* a, const NETINFO_ITEM* b ) ...@@ -909,15 +905,15 @@ static bool s_SortByNodes( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
*/ */
int BOARD::ReturnSortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount ) int BOARD::ReturnSortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount )
{ {
if( m_NetInfo->GetNetsCount() == 0 ) if( m_NetInfo->GetCount() == 0 )
return 0; return 0;
// Build the list // Build the list
std::vector <NETINFO_ITEM*> netBuffer; std::vector <NETINFO_ITEM*> netBuffer;
netBuffer.reserve( m_NetInfo->GetNetsCount() ); netBuffer.reserve( m_NetInfo->GetCount() );
for( unsigned ii = 1; ii < m_NetInfo->GetNetsCount(); ii++ ) for( unsigned ii = 1; ii < m_NetInfo->GetCount(); ii++ )
{ {
if( m_NetInfo->GetNetItem( ii )->GetNet() > 0 ) if( m_NetInfo->GetNetItem( ii )->GetNet() > 0 )
netBuffer.push_back( m_NetInfo->GetNetItem( ii ) ); netBuffer.push_back( m_NetInfo->GetNetItem( ii ) );
...@@ -941,14 +937,14 @@ bool BOARD::Save( FILE* aFile ) const ...@@ -941,14 +937,14 @@ bool BOARD::Save( FILE* aFile ) const
bool rc = false; bool rc = false;
BOARD_ITEM* item; BOARD_ITEM* item;
// save the netclasses
m_NetClassesList.Save( aFile );
// save the nets // save the nets
for( unsigned ii = 0; ii < m_NetInfo->GetNetsCount(); ii++ ) for( unsigned ii = 0; ii < m_NetInfo->GetCount(); ii++ )
if( !m_NetInfo->GetNetItem( ii )->Save( aFile ) ) if( !m_NetInfo->GetNetItem( ii )->Save( aFile ) )
goto out; goto out;
// Saved nets do not include netclass names, so save netclasses after nets.
m_NetClasses.Save( aFile );
// save the modules // save the modules
for( item = m_Modules; item; item = item->Next() ) for( item = m_Modules; item; item = item->Next() )
if( !item->Save( aFile ) ) if( !item->Save( aFile ) )
......
...@@ -97,7 +97,7 @@ public: ...@@ -97,7 +97,7 @@ public:
std::vector<RATSNEST_ITEM> m_LocalRatsnest; /* Rastnest list relative to a given footprint std::vector<RATSNEST_ITEM> m_LocalRatsnest; /* Rastnest list relative to a given footprint
* (used while moving a footprint) */ * (used while moving a footprint) */
NETCLASS_LIST m_NetClassesList; // List of current netclasses. There is always the default netclass NETCLASSES m_NetClasses; ///< List of current netclasses. There is always the default netclass
ZONE_CONTAINER* m_CurrentZoneContour; // zone contour currently in progress ZONE_CONTAINER* m_CurrentZoneContour; // zone contour currently in progress
...@@ -338,15 +338,15 @@ public: ...@@ -338,15 +338,15 @@ public:
int ReturnSortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount ); int ReturnSortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount );
/** /**
* Function TransfertDesignRulesToNets * Function SynchronizeNetsAndNetClasses
* copies Netclass parameters to each net, corresponding to its net class. * copies NETCLASS info to each NET, based on NET membership in a NETCLASS.
* Must be called after a Design Rules edition, or after reading a netlist (or editing the list of nets) * Must be called after a Design Rules edition, or after reading a netlist (or editing the list of nets)
* Also this function removes the non existing nets in netclasses and add net nets in default netclass * Also this function removes the non existing nets in netclasses and add net nets in default netclass
* (this happens after reading a netlist) * (this happens after reading a netlist)
* @param none * @param none
* @return none * @return none
*/ */
void TransfertDesignRulesToNets(); void SynchronizeNetsAndNetClasses();
/** /**
* Function Save * Function Save
......
...@@ -68,3 +68,50 @@ void BOARD_CONNECTED_ITEM::SetZoneSubNet( int aSubNetCode ) ...@@ -68,3 +68,50 @@ void BOARD_CONNECTED_ITEM::SetZoneSubNet( int aSubNetCode )
{ {
m_ZoneSubnet = aSubNetCode; m_ZoneSubnet = aSubNetCode;
} }
int BOARD_CONNECTED_ITEM::GetClearance( BOARD_CONNECTED_ITEM* aItem ) const
{
NETCLASS* hisclass = aItem->GetNetClass();
NETCLASS* myclass = GetNetClass();
wxASSERT( hisclass );
wxASSERT( myclass );
if( myclass )
{
if( hisclass )
return MAX( hisclass->GetClearance(), myclass->GetClearance() );
else
return myclass->GetClearance();
}
else if( hisclass )
{
return hisclass->GetClearance();
}
return 0;
}
NETCLASS* BOARD_CONNECTED_ITEM::GetNetClass() const
{
// It is important that this be implemented without any sequential searching.
// Simple array lookups should be fine, performance-wise.
BOARD* board = GetBoard();
wxASSERT( board );
if( board )
{
NETINFO_ITEM* net = board->FindNet( GetNet() );
wxASSERT( net );
if( net )
{
NETCLASS* netclass = net->GetNetClass();
wxASSERT( netclass );
return netclass;
}
}
return NULL;
}
...@@ -315,3 +315,11 @@ void BOARD_ITEM::UnLink() ...@@ -315,3 +315,11 @@ void BOARD_ITEM::UnLink()
list->Remove( this ); list->Remove( this );
} }
BOARD* BOARD_ITEM::GetBoard() const
{
// overload this function as needed.
return (BOARD*) GetParent();
}
This diff is collapsed.
This diff is collapsed.
...@@ -85,11 +85,12 @@ public: ...@@ -85,11 +85,12 @@ public:
*/ */
NETINFO_ITEM* GetNetItem( int aNetcode ); NETINFO_ITEM* GetNetItem( int aNetcode );
/** Function GetCount() /**
* Function GetCount
* @return the number of nets ( always >= 1 ) * @return the number of nets ( always >= 1 )
* becuse the first net is the "not connected" net and always exists * becuse the first net is the "not connected" net and always exists
*/ */
unsigned GetNetsCount() { return m_NetBuffer.size(); } unsigned GetCount() { return m_NetBuffer.size(); }
/** /**
* Function Append * Function Append
...@@ -152,12 +153,15 @@ class NETINFO_ITEM ...@@ -152,12 +153,15 @@ class NETINFO_ITEM
private: private:
int m_NetCode; // this is a number equivalent to the net name int m_NetCode; // this is a number equivalent to the net name
// Used for fast comparisons in rastnest and DRC computations. // Used for fast comparisons in rastnest and DRC computations.
wxString m_Netname; // Full net name like /mysheet/mysubsheet/vout used by eeschema wxString m_Netname; // Full net name like /mysheet/mysubsheet/vout used by eeschema
wxString m_ShortNetname; // short net name, like vout from /mysheet/mysubsheet/vout wxString m_ShortNetname; // short net name, like vout from /mysheet/mysubsheet/vout
wxString m_NetClassName; // Net Class name. if void this is equivalent to "default" (the first wxString m_NetClassName; // Net Class name. if void this is equivalent to "default" (the first
// item of the net classes list // item of the net classes list
NET_DESIGN_PARAMS m_NetParams; // values of net classes parameters NETCLASS* m_NetClass;
public: public:
...@@ -177,83 +181,97 @@ public: ...@@ -177,83 +181,97 @@ public:
NETINFO_ITEM( BOARD_ITEM* aParent ); NETINFO_ITEM( BOARD_ITEM* aParent );
~NETINFO_ITEM(); ~NETINFO_ITEM();
/**
/** Functions SetClassParameters * Function SetClass
* copy the class parameters in the locale buffer m_NetParams * sets \a aNetclass into this NET
*/ */
void SetClassParameters(const NET_DESIGN_PARAMS& aParams ) void SetClass( const NETCLASS* aNetClass )
{ {
m_NetParams = aParams; m_NetClass = (NETCLASS*) aNetClass;
if( aNetClass )
m_NetClassName = aNetClass->GetName();
else
m_NetClassName = NETCLASS::Default;
} }
/** Functions SetClass NETCLASS* GetNetClass()
* copy the class Name and class parmeters
*/
void SetClass(const NETCLASS& aNetclass )
{ {
m_NetParams = aNetclass.m_NetParams; return m_NetClass;
m_NetClassName = aNetclass.m_Name;
} }
/** Functions GetClassName /**
* @return the class Name * Function GetClassName
* returns the class name
*/ */
wxString GetClassName( ) const const wxString& GetClassName( ) const
{ {
return m_NetClassName; return m_NetClassName;
} }
/** function GetTracksWidth()
* @return the "default" value for tracks thickness used to route this net /**
* Function GetTrackWidth
* returns the width of tracks used to route this net.
*/ */
int GetTracksWidth() int GetTrackWidth()
{ {
return m_NetParams.m_TracksWidth; wxASSERT( m_NetClass );
return m_NetClass->GetTrackWidth();
} }
/** Function GetTracksMinWidth() /**
* @return the Minimum value for tracks thickness (used in DRC) * Function GetTrackMinWidth
* returns the Minimum value for tracks thickness (used in DRC)
*/ */
int GetTracksMinWidth() int GetTrackMinWidth()
{ {
return m_NetParams.m_TracksMinWidth = 150; wxASSERT( m_NetClass );
return m_NetClass->GetTrackMinWidth();
} }
/** Function /**
* @return the "Default" value for vias sizes used to route this net * Function GetViaSize
* returns the size of vias used to route this net
*/ */
int GetViasSize() int GetViaSize()
{ {
return m_NetParams.m_ViasSize; wxASSERT( m_NetClass );
return m_NetClass->GetViaSize();
} }
/** Function GetViasMinSize() /**
* @return the Minimum value for vias sizes (used in DRC) * Function GetViaDrillSize
* returns the size of via drills used to route this net
*/ */
int GetViasMinSize() int GetViaDrillSize()
{ {
return m_NetParams.m_ViasMinSize; wxASSERT( m_NetClass );
return m_NetClass->GetViaDrillSize();
} }
/** Function GetClearance() /**
* @return the "Default" clearance when routing * Function GetViaMinSize
* returns the Minimum value for via sizes (used in DRC)
*/ */
int GetClearance() int GetViaMinSize()
{ {
return m_NetParams.m_Clearance; wxASSERT( m_NetClass );
return m_NetClass->GetViaMinSize();
} }
/** Function GetMinClearance() /**
* @return the Minimum value for clearance (used in DRC) * Function GetClearance
* returns the clearance when routing near aBoardItem
*/ */
int GetMinClearance() int GetClearance( BOARD_ITEM* aBoardItem )
{ {
return m_NetParams.m_MinClearance; wxASSERT( m_NetClass );
return m_NetClass->GetClearance();
} }
......
...@@ -23,13 +23,16 @@ NETINFO_ITEM::NETINFO_ITEM( BOARD_ITEM* aParent ) ...@@ -23,13 +23,16 @@ NETINFO_ITEM::NETINFO_ITEM( BOARD_ITEM* aParent )
m_Flag = 0; m_Flag = 0;
m_RatsnestStartIdx = 0; // Starting point of ratsnests of this net in a general buffer of ratsnest m_RatsnestStartIdx = 0; // Starting point of ratsnests of this net in a general buffer of ratsnest
m_RatsnestEndIdx = 0; // Ending point of ratsnests of this net m_RatsnestEndIdx = 0; // Ending point of ratsnests of this net
}
m_NetClassName = NETCLASS::Default;
m_NetClass = 0;
}
/* destructor */
NETINFO_ITEM::~NETINFO_ITEM() NETINFO_ITEM::~NETINFO_ITEM()
{ {
// m_NetClass is not owned by me.
} }
...@@ -60,12 +63,14 @@ int NETINFO_ITEM:: ReadDescr( FILE* File, int* LineNum ) ...@@ -60,12 +63,14 @@ int NETINFO_ITEM:: ReadDescr( FILE* File, int* LineNum )
continue; continue;
} }
/*
if( strncmp( Line, "NetClass", 8 ) == 0 ) if( strncmp( Line, "NetClass", 8 ) == 0 )
{ {
ReadDelimitedText( Ltmp, Line + 8, sizeof(Ltmp) ); ReadDelimitedText( Ltmp, Line + 8, sizeof(Ltmp) );
m_NetClassName = CONV_FROM_UTF8( Ltmp ); m_NetClassName = CONV_FROM_UTF8( Ltmp );
continue; continue;
} }
*/
} }
return 1; return 1;
...@@ -86,7 +91,7 @@ bool NETINFO_ITEM::Save( FILE* aFile ) const ...@@ -86,7 +91,7 @@ bool NETINFO_ITEM::Save( FILE* aFile ) const
fprintf( aFile, "Na %d \"%s\"\n", GetNet(), CONV_TO_UTF8( m_Netname ) ); fprintf( aFile, "Na %d \"%s\"\n", GetNet(), CONV_TO_UTF8( m_Netname ) );
fprintf( aFile, "St %s\n", "~" ); fprintf( aFile, "St %s\n", "~" );
fprintf( aFile, "NetClass \"%s\"\n", CONV_TO_UTF8(m_NetClassName) ); // fprintf( aFile, "NetClass \"%s\"\n", CONV_TO_UTF8(m_NetClassName) );
if( fprintf( aFile, "$EndEQUIPOT\n" ) != sizeof("$EndEQUIPOT\n") - 1 ) if( fprintf( aFile, "$EndEQUIPOT\n" ) != sizeof("$EndEQUIPOT\n") - 1 )
goto out; goto out;
......
...@@ -29,7 +29,7 @@ NETINFO_LIST::~NETINFO_LIST() ...@@ -29,7 +29,7 @@ NETINFO_LIST::~NETINFO_LIST()
*/ */
NETINFO_ITEM* NETINFO_LIST::GetNetItem( int aNetcode ) NETINFO_ITEM* NETINFO_LIST::GetNetItem( int aNetcode )
{ {
if( aNetcode < 0 || ( aNetcode > (int) ( GetNetsCount() - 1 ) ) ) if( aNetcode < 0 || ( aNetcode > (int) ( GetCount() - 1 ) ) )
return NULL; return NULL;
return m_NetBuffer[aNetcode]; return m_NetBuffer[aNetcode];
} }
...@@ -40,7 +40,7 @@ NETINFO_ITEM* NETINFO_LIST::GetNetItem( int aNetcode ) ...@@ -40,7 +40,7 @@ NETINFO_ITEM* NETINFO_LIST::GetNetItem( int aNetcode )
*/ */
void NETINFO_LIST::DeleteData() void NETINFO_LIST::DeleteData()
{ {
for( unsigned ii = 0; ii < GetNetsCount(); ii++ ) for( unsigned ii = 0; ii < GetCount(); ii++ )
delete m_NetBuffer[ii]; delete m_NetBuffer[ii];
m_NetBuffer.clear(); m_NetBuffer.clear();
...@@ -125,7 +125,8 @@ void NETINFO_LIST::BuildListOfNets() ...@@ -125,7 +125,8 @@ void NETINFO_LIST::BuildListOfNets()
} }
m_Parent->m_NbNodes = nodes_count; m_Parent->m_NbNodes = nodes_count;
m_Parent->TransfertDesignRulesToNets( );
m_Parent->SynchronizeNetsAndNetClasses( );
m_Parent->m_Status_Pcb |= NET_CODES_OK; m_Parent->m_Status_Pcb |= NET_CODES_OK;
......
...@@ -202,39 +202,44 @@ void DIALOG_DESIGN_RULES::SetRoutableLayerStatus() ...@@ -202,39 +202,44 @@ void DIALOG_DESIGN_RULES::SetRoutableLayerStatus()
*/ */
void DIALOG_DESIGN_RULES::InitRulesList() void DIALOG_DESIGN_RULES::InitRulesList()
{ {
for( int ii = 0; ; ii++ ) NETCLASSES& netclasses = m_Pcb->m_NetClasses;
int ii = 0;
for( NETCLASSES::iterator i=netclasses.begin(); i!=netclasses.end(); ++i, ++ii )
{ {
const NETCLASS* netclass = m_Pcb->m_NetClassesList.GetNetClass( ii ); NETCLASS* netclass = i->second;
if( netclass == NULL )
break;
// Creates one entry if needed // Creates one entry if needed
if( ii >= m_gridNetClassesProperties->GetNumberRows() ) if( ii >= m_gridNetClassesProperties->GetNumberRows() )
m_gridNetClassesProperties->AppendRows(); m_gridNetClassesProperties->AppendRows();
// Init name // Init name
m_gridNetClassesProperties->SetRowLabelValue( ii, netclass->m_Name ); m_gridNetClassesProperties->SetRowLabelValue( ii, netclass->GetName() );
// Init data // Init data
wxString msg; wxString msg;
msg = ReturnStringFromValue( g_UnitMetric, msg = ReturnStringFromValue( g_UnitMetric,
netclass->m_NetParams.m_TracksWidth, netclass->GetTrackWidth(),
m_Parent->m_InternalUnits, false ); m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_TRACKSIZE_POSITION, msg ); m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_TRACKSIZE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric, msg = ReturnStringFromValue( g_UnitMetric,
netclass->m_NetParams.m_ViasSize, netclass->GetViaSize(),
m_Parent->m_InternalUnits, false ); m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_VIASIZE_POSITION, msg ); m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_VIASIZE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric, msg = ReturnStringFromValue( g_UnitMetric,
netclass->m_NetParams.m_Clearance, netclass->GetClearance(),
m_Parent->m_InternalUnits, false ); m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_CLEARANCE_POSITION, msg ); m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_CLEARANCE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric, msg = ReturnStringFromValue( g_UnitMetric,
netclass->m_NetParams.m_TracksMinWidth, netclass->GetTrackMinWidth(),
m_Parent->m_InternalUnits, false ); m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_MINTRACKSIZE_POSITION, msg ); m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_MINTRACKSIZE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric, msg = ReturnStringFromValue( g_UnitMetric,
netclass->m_NetParams.m_ViasMinSize, netclass->GetViaMinSize(),
m_Parent->m_InternalUnits, false ); m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_MINVIASIZE_POSITION, msg ); m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_MINVIASIZE_POSITION, msg );
} }
...@@ -245,53 +250,57 @@ void DIALOG_DESIGN_RULES::InitRulesList() ...@@ -245,53 +250,57 @@ void DIALOG_DESIGN_RULES::InitRulesList()
*/ */
void DIALOG_DESIGN_RULES::CopyRulesListToBoard() void DIALOG_DESIGN_RULES::CopyRulesListToBoard()
{ {
m_Pcb->m_NetClassesList.ClearList(); NETCLASSES& netclasses = m_Pcb->m_NetClasses;
netclasses.Clear();
for( int ii = 0; ii < m_gridNetClassesProperties->GetNumberRows(); ii++ ) for( int ii = 0; ii < m_gridNetClassesProperties->GetNumberRows(); ii++ )
{ {
NETCLASS* netclass = new NETCLASS( m_Pcb, NETCLASS netclass( m_Pcb, m_gridNetClassesProperties->GetRowLabelValue( ii ) );
m_gridNetClassesProperties->GetRowLabelValue( ii ) );
m_Pcb->m_NetClassesList.AddNetclass( netclass );
// Init data // Init data
netclass->m_NetParams.m_TracksWidth = netclass.SetTrackWidth(
ReturnValueFromString( g_UnitMetric, ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii, m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_TRACKSIZE_POSITION ), RULE_GRID_TRACKSIZE_POSITION ),
m_Parent->m_InternalUnits ); m_Parent->m_InternalUnits ));
netclass->m_NetParams.m_ViasSize = netclass.SetViaSize(
ReturnValueFromString( g_UnitMetric, ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii, m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_VIASIZE_POSITION ), RULE_GRID_VIASIZE_POSITION ),
m_Parent->m_InternalUnits ); m_Parent->m_InternalUnits ));
netclass->m_NetParams.m_Clearance = netclass.SetClearance(
ReturnValueFromString( g_UnitMetric, ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii, m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_CLEARANCE_POSITION ), RULE_GRID_CLEARANCE_POSITION ),
m_Parent->m_InternalUnits ); m_Parent->m_InternalUnits ));
netclass->m_NetParams.m_TracksMinWidth = netclass.SetTrackMinWidth(
ReturnValueFromString( g_UnitMetric, ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii, m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_MINTRACKSIZE_POSITION ), RULE_GRID_MINTRACKSIZE_POSITION ),
m_Parent->m_InternalUnits ); m_Parent->m_InternalUnits ));
netclass->m_NetParams.m_ViasMinSize = netclass.SetViaMinSize(
ReturnValueFromString( g_UnitMetric, ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii, m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_MINVIASIZE_POSITION ), RULE_GRID_MINVIASIZE_POSITION ),
m_Parent->m_InternalUnits ); m_Parent->m_InternalUnits ));
// Copy the list of nets associated to this netclass: // Copy the list of nets associated to this netclass:
for( unsigned idx = 0; idx < m_StockNets.size(); idx++ ) for( unsigned idx = 0; idx < m_StockNets.size(); idx++ )
{ {
if( m_NetsLinkToClasses[idx] == ii ) if( m_NetsLinkToClasses[idx] == ii )
netclass->AddMember( m_StockNets[idx]->GetNetname() ); netclass.Add( m_StockNets[idx]->GetNetname() );
} }
// this calls copy constructor, so all data must be in 'netclass' before Add()ing.
m_Pcb->m_NetClasses.Add( netclass );
} }
m_Pcb->TransfertDesignRulesToNets(); m_Pcb->SynchronizeNetsAndNetClasses();
} }
......
...@@ -437,7 +437,7 @@ void CreateSignalsSection( FILE* file, BOARD* pcb ) ...@@ -437,7 +437,7 @@ void CreateSignalsSection( FILE* file, BOARD* pcb )
fputs( "$SIGNALS\n", file ); fputs( "$SIGNALS\n", file );
for( unsigned ii = 0; ii < pcb->m_NetInfo->GetNetsCount() ; ii++ ) for( unsigned ii = 0; ii < pcb->m_NetInfo->GetCount() ; ii++ )
{ {
net = pcb->m_NetInfo->GetNetItem(ii); net = pcb->m_NetInfo->GetNetItem(ii);
if( net->GetNetname() == wxEmptyString ) // dummy equipot (non connexion) if( net->GetNetname() == wxEmptyString ) // dummy equipot (non connexion)
......
...@@ -637,7 +637,7 @@ bool WinEDA_PcbFrame::WriteGeneralDescrPcb( FILE* File ) ...@@ -637,7 +637,7 @@ bool WinEDA_PcbFrame::WriteGeneralDescrPcb( FILE* File )
fprintf( File, "LayerThickness %d\n", GetBoard()->m_BoardSettings->m_LayerThickness ); fprintf( File, "LayerThickness %d\n", GetBoard()->m_BoardSettings->m_LayerThickness );
fprintf( File, "Nmodule %d\n", NbModules ); fprintf( File, "Nmodule %d\n", NbModules );
fprintf( File, "Nnets %d\n", GetBoard()->m_NetInfo->GetNetsCount() ); fprintf( File, "Nnets %d\n", GetBoard()->m_NetInfo->GetCount() );
fprintf( File, "$EndGENERAL\n\n" ); fprintf( File, "$EndGENERAL\n\n" );
return TRUE; return TRUE;
...@@ -787,11 +787,14 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append ) ...@@ -787,11 +787,14 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append )
wxBusyCursor dummy; wxBusyCursor dummy;
// Switch the locale to standard C (needed to read floating point numbers like 1.3) // Switch the locale to standard C (needed to read floating point numbers like 1.3)
SetLocaleTo_C_standard( ); SetLocaleTo_C_standard();
BOARD* board = GetBoard();
NbDraw = NbTrack = NbZone = NbMod = NbNets = -1; NbDraw = NbTrack = NbZone = NbMod = NbNets = -1;
GetBoard()->m_Status_Pcb = 0;
GetBoard()->m_NetClassesList.ClearList(); board->m_Status_Pcb = 0;
board->m_NetClasses.Clear();
while( GetLine( File, Line, &LineNum ) != NULL ) while( GetLine( File, Line, &LineNum ) != NULL )
{ {
...@@ -827,73 +830,72 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append ) ...@@ -827,73 +830,72 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append )
if( strnicmp( Line, "$EQUIPOT", 7 ) == 0 ) if( strnicmp( Line, "$EQUIPOT", 7 ) == 0 )
{ {
NETINFO_ITEM* net = new NETINFO_ITEM( GetBoard() ); NETINFO_ITEM* net = new NETINFO_ITEM( board );
GetBoard()->m_NetInfo->AppendNet( net ); board->m_NetInfo->AppendNet( net );
net->ReadDescr( File, &LineNum ); net->ReadDescr( File, &LineNum );
continue; continue;
} }
if( strnicmp( Line, "$NETCLASS", 8 ) == 0 ) if( strnicmp( Line, "$NETCLASS", 8 ) == 0 )
{ {
NETCLASS* netclass = new NETCLASS( GetBoard() ); NETCLASS netclass( board, wxEmptyString );
netclass->ReadDescr( File, &LineNum );
if( ! GetBoard()->m_NetClassesList.AddNetclass( netclass ) ) netclass.ReadDescr( File, &LineNum );
delete netclass;
board->m_NetClasses.Add( netclass );
continue; continue;
} }
if( strnicmp( Line, "$CZONE_OUTLINE", 7 ) == 0 ) if( strnicmp( Line, "$CZONE_OUTLINE", 7 ) == 0 )
{ {
ZONE_CONTAINER * zone_descr = new ZONE_CONTAINER(GetBoard()); ZONE_CONTAINER * zone_descr = new ZONE_CONTAINER(board);
zone_descr->ReadDescr( File, &LineNum ); zone_descr->ReadDescr( File, &LineNum );
if ( zone_descr->GetNumCorners( ) > 2 ) // should always occur if ( zone_descr->GetNumCorners( ) > 2 ) // should always occur
GetBoard()->Add(zone_descr); board->Add(zone_descr);
else delete zone_descr; else delete zone_descr;
continue; continue;
} }
if( strnicmp( Line, "$MODULE", 7 ) == 0 ) if( strnicmp( Line, "$MODULE", 7 ) == 0 )
{ {
MODULE* Module = new MODULE( GetBoard() ); MODULE* Module = new MODULE( board );
if( Module == NULL ) if( Module == NULL )
continue; continue;
GetBoard()->Add( Module, ADD_APPEND ); board->Add( Module, ADD_APPEND );
Module->ReadDescr( File, &LineNum ); Module->ReadDescr( File, &LineNum );
continue; continue;
} }
if( strnicmp( Line, "$TEXTPCB", 8 ) == 0 ) if( strnicmp( Line, "$TEXTPCB", 8 ) == 0 )
{ {
TEXTE_PCB* pcbtxt = new TEXTE_PCB( GetBoard() ); TEXTE_PCB* pcbtxt = new TEXTE_PCB( board );
GetBoard()->Add( pcbtxt, ADD_APPEND ); board->Add( pcbtxt, ADD_APPEND );
pcbtxt->ReadTextePcbDescr( File, &LineNum ); pcbtxt->ReadTextePcbDescr( File, &LineNum );
continue; continue;
} }
if( strnicmp( Line, "$DRAWSEGMENT", 10 ) == 0 ) if( strnicmp( Line, "$DRAWSEGMENT", 10 ) == 0 )
{ {
DRAWSEGMENT* DrawSegm = new DRAWSEGMENT( GetBoard() ); DRAWSEGMENT* DrawSegm = new DRAWSEGMENT( board );
GetBoard()->Add( DrawSegm, ADD_APPEND ); board->Add( DrawSegm, ADD_APPEND );
DrawSegm->ReadDrawSegmentDescr( File, &LineNum ); DrawSegm->ReadDrawSegmentDescr( File, &LineNum );
continue; continue;
} }
if( strnicmp( Line, "$COTATION", 9 ) == 0 ) if( strnicmp( Line, "$COTATION", 9 ) == 0 )
{ {
COTATION* Cotation = new COTATION( GetBoard() ); COTATION* Cotation = new COTATION( board );
GetBoard()->Add( Cotation, ADD_APPEND ); board->Add( Cotation, ADD_APPEND );
Cotation->ReadCotationDescr( File, &LineNum ); Cotation->ReadCotationDescr( File, &LineNum );
continue; continue;
} }
if( strnicmp( Line, "$MIREPCB", 8 ) == 0 ) if( strnicmp( Line, "$MIREPCB", 8 ) == 0 )
{ {
MIREPCB* Mire = new MIREPCB( GetBoard() ); MIREPCB* Mire = new MIREPCB( board );
GetBoard()->Add( Mire, ADD_APPEND ); board->Add( Mire, ADD_APPEND );
Mire->ReadMirePcbDescr( File, &LineNum ); Mire->ReadMirePcbDescr( File, &LineNum );
continue; continue;
} }
...@@ -901,7 +903,7 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append ) ...@@ -901,7 +903,7 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append )
if( strnicmp( Line, "$TRACK", 6 ) == 0 ) if( strnicmp( Line, "$TRACK", 6 ) == 0 )
{ {
#ifdef PCBNEW #ifdef PCBNEW
TRACK* insertBeforeMe = Append ? NULL : GetBoard()->m_Track.GetFirst(); TRACK* insertBeforeMe = Append ? NULL : board->m_Track.GetFirst();
ReadListeSegmentDescr( File, insertBeforeMe, TYPE_TRACK, ReadListeSegmentDescr( File, insertBeforeMe, TYPE_TRACK,
&LineNum, NbTrack ); &LineNum, NbTrack );
#endif #endif
...@@ -911,7 +913,7 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append ) ...@@ -911,7 +913,7 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append )
if( strnicmp( Line, "$ZONE", 5 ) == 0 ) if( strnicmp( Line, "$ZONE", 5 ) == 0 )
{ {
#ifdef PCBNEW #ifdef PCBNEW
SEGZONE* insertBeforeMe = Append ? NULL : GetBoard()->m_Zone.GetFirst(); SEGZONE* insertBeforeMe = Append ? NULL : board->m_Zone.GetFirst();
ReadListeSegmentDescr( File, insertBeforeMe, TYPE_ZONE, ReadListeSegmentDescr( File, insertBeforeMe, TYPE_ZONE,
&LineNum, NbZone ); &LineNum, NbZone );
...@@ -926,16 +928,8 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append ) ...@@ -926,16 +928,8 @@ int WinEDA_PcbFrame::ReadPcbFile( FILE* File, bool Append )
BestZoom(); BestZoom();
// One netclass *must* exists (the default netclass) board->SynchronizeNetsAndNetClasses( );
if( GetBoard()->m_NetClassesList.GetNetClassCount() == 0 ) board->m_Status_Pcb = 0;
{
NETCLASS* ncdefault = new NETCLASS(GetBoard());
GetBoard()->m_NetClassesList.AddNetclass( ncdefault );
}
GetBoard()->TransfertDesignRulesToNets( );
GetBoard()->m_Status_Pcb = 0;
return 1; return 1;
} }
...@@ -970,6 +964,8 @@ int WinEDA_PcbFrame::SavePcbFormatAscii( FILE* aFile ) ...@@ -970,6 +964,8 @@ int WinEDA_PcbFrame::SavePcbFormatAscii( FILE* aFile )
WriteSheetDescr( GetScreen(), aFile ); WriteSheetDescr( GetScreen(), aFile );
WriteSetup( aFile, this, GetBoard() ); WriteSetup( aFile, this, GetBoard() );
GetBoard()->SynchronizeNetsAndNetClasses();
rc = GetBoard()->Save( aFile ); rc = GetBoard()->Save( aFile );
SetLocaleTo_Default( ); // revert to the current locale SetLocaleTo_Default( ); // revert to the current locale
......
...@@ -119,7 +119,7 @@ void WinEDA_BasePcbFrame::Compile_Ratsnest( wxDC* DC, bool display_status_pcb ) ...@@ -119,7 +119,7 @@ void WinEDA_BasePcbFrame::Compile_Ratsnest( wxDC* DC, bool display_status_pcb )
if( display_status_pcb ) if( display_status_pcb )
{ {
msg.Printf( wxT( " %d" ), m_Pcb->m_NetInfo->GetNetsCount() ); msg.Printf( wxT( " %d" ), m_Pcb->m_NetInfo->GetCount() );
Affiche_1_Parametre( this, 8, wxT( "Nets" ), msg, CYAN ); Affiche_1_Parametre( this, 8, wxT( "Nets" ), msg, CYAN );
} }
...@@ -427,7 +427,7 @@ void WinEDA_BasePcbFrame::Build_Board_Ratsnest( wxDC* DC ) ...@@ -427,7 +427,7 @@ void WinEDA_BasePcbFrame::Build_Board_Ratsnest( wxDC* DC )
unsigned current_net_code = 1; // 1er net_code a analyser (net_code = 0 -> no connect) unsigned current_net_code = 1; // 1er net_code a analyser (net_code = 0 -> no connect)
noconn = 0; noconn = 0;
for( ; current_net_code < m_Pcb->m_NetInfo->GetNetsCount(); current_net_code++ ) for( ; current_net_code < m_Pcb->m_NetInfo->GetCount(); current_net_code++ )
{ {
NETINFO_ITEM* net = m_Pcb->FindNet( current_net_code ); NETINFO_ITEM* net = m_Pcb->FindNet( current_net_code );
if( net == NULL ) //Should not occur if( net == NULL ) //Should not occur
...@@ -661,7 +661,7 @@ void WinEDA_BasePcbFrame::Tst_Ratsnest( wxDC* DC, int ref_netcode ) ...@@ -661,7 +661,7 @@ void WinEDA_BasePcbFrame::Tst_Ratsnest( wxDC* DC, int ref_netcode )
if( (m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 ) if( (m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
Build_Board_Ratsnest( DC ); Build_Board_Ratsnest( DC );
for( int net_code = 1; net_code < (int) m_Pcb->m_NetInfo->GetNetsCount(); net_code++ ) for( int net_code = 1; net_code < (int) m_Pcb->m_NetInfo->GetCount(); net_code++ )
{ {
net = m_Pcb->FindNet( net_code ); net = m_Pcb->FindNet( net_code );
if( net == NULL ) //Should not occur if( net == NULL ) //Should not occur
......
...@@ -1114,7 +1114,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) ...@@ -1114,7 +1114,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError )
int highestNetCode = -1; int highestNetCode = -1;
// for( EQUIPOT* equipot = aBoard->m_Equipots; equipot; equipot = equipot->Next() ) // for( EQUIPOT* equipot = aBoard->m_Equipots; equipot; equipot = equipot->Next() )
// highestNetCode = MAX( highestNetCode, equipot->GetNet() ); // highestNetCode = MAX( highestNetCode, equipot->GetNet() );
highestNetCode = aBoard->m_NetInfo->GetNetsCount() - 1; highestNetCode = aBoard->m_NetInfo->GetCount() - 1;
deleteNETs(); deleteNETs();
// expand the net vector to highestNetCode+1, setting empty to NULL // expand the net vector to highestNetCode+1, setting empty to NULL
...@@ -1124,7 +1124,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError ) ...@@ -1124,7 +1124,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError )
for( unsigned i=1; i<nets.size(); ++i ) for( unsigned i=1; i<nets.size(); ++i )
nets[i] = new NET( pcb->network ); nets[i] = new NET( pcb->network );
for( unsigned ii = 0; ii < aBoard->m_NetInfo->GetNetsCount(); ii++ ) for( unsigned ii = 0; ii < aBoard->m_NetInfo->GetCount(); ii++ )
{ {
NETINFO_ITEM* net = aBoard->m_NetInfo->GetNetItem(ii); NETINFO_ITEM* net = aBoard->m_NetInfo->GetNetItem(ii);
int netcode = net->GetNet(); int netcode = net->GetNet();
......
...@@ -37,7 +37,7 @@ void WinEDA_PcbFrame::ListNetsAndSelect( wxCommandEvent& event ) ...@@ -37,7 +37,7 @@ void WinEDA_PcbFrame::ListNetsAndSelect( wxCommandEvent& event )
WinEDA_TextFrame List( this, _( "List Nets" ) ); WinEDA_TextFrame List( this, _( "List Nets" ) );
for( unsigned ii = 0; ii < GetBoard()->m_NetInfo->GetNetsCount(); ii++ ) for( unsigned ii = 0; ii < GetBoard()->m_NetInfo->GetCount(); ii++ )
{ {
net = GetBoard()->m_NetInfo->GetNetItem( ii ); net = GetBoard()->m_NetInfo->GetNetItem( ii );
wxString Line; wxString Line;
...@@ -58,7 +58,7 @@ void WinEDA_PcbFrame::ListNetsAndSelect( wxCommandEvent& event ) ...@@ -58,7 +58,7 @@ void WinEDA_PcbFrame::ListNetsAndSelect( wxCommandEvent& event )
unsigned netcode = (unsigned) selection; unsigned netcode = (unsigned) selection;
// Search for the net selected. // Search for the net selected.
for( unsigned ii = 0; ii < GetBoard()->m_NetInfo->GetNetsCount(); ii++ ) for( unsigned ii = 0; ii < GetBoard()->m_NetInfo->GetCount(); ii++ )
{ {
net = GetBoard()->m_NetInfo->GetNetItem( ii ); net = GetBoard()->m_NetInfo->GetNetItem( ii );
if( !WildCompareString( netFilter, net->GetNetname(), false ) ) if( !WildCompareString( netFilter, net->GetNetname(), false ) )
......
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