Commit 4a7f92fb authored by jean-pierre charras's avatar jean-pierre charras

Minor code cleaning. Pcbnew: better iniatilization of members in DRAWSEGMENT,...

Minor code cleaning. Pcbnew: better iniatilization of members in DRAWSEGMENT, TRACK, EDGE_MOD (useful when using  python scripting).
parent b1ed22f7
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <wx/graphics.h> #include <wx/graphics.h>
static const bool FILLED = true; static const bool FILLED = true;
static const bool NOT_FILLED = false;
/* Important Note: /* Important Note:
* These drawing functions clip draw item before send these items to wxDC draw * These drawing functions clip draw item before send these items to wxDC draw
...@@ -50,11 +51,8 @@ GR_DRAWMODE g_XorMode = GR_NXOR; ...@@ -50,11 +51,8 @@ GR_DRAWMODE g_XorMode = GR_NXOR;
EDA_COLOR_T g_DrawBgColor = WHITE; EDA_COLOR_T g_DrawBgColor = WHITE;
#define USE_CLIP_FILLED_POLYGONS static void ClipAndDrawPoly( EDA_RECT * ClipBox, wxDC * DC, wxPoint Points[],
int n );
#ifdef USE_CLIP_FILLED_POLYGONS
static void ClipAndDrawFilledPoly( EDA_RECT * ClipBox, wxDC * DC, wxPoint Points[], int n );
#endif
/* These functions are used by corresponding functions /* These functions are used by corresponding functions
* ( GRSCircle is called by GRCircle for instance) after mapping coordinates * ( GRSCircle is called by GRCircle for instance) after mapping coordinates
...@@ -857,14 +855,9 @@ static bool IsGRSPolyDrawable( EDA_RECT* ClipBox, int n, wxPoint Points[] ) ...@@ -857,14 +855,9 @@ static bool IsGRSPolyDrawable( EDA_RECT* ClipBox, int n, wxPoint Points[] )
/* /*
* Draw a new polyline and fill it if Fill, in screen space. * Draw a new polyline and fill it if Fill, in screen space.
*/ */
static void GRSPoly( EDA_RECT* ClipBox, static void GRSPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[],
wxDC* DC, bool Fill, int width,
int n, EDA_COLOR_T Color, EDA_COLOR_T BgColor )
wxPoint Points[],
bool Fill,
int width,
EDA_COLOR_T Color,
EDA_COLOR_T BgColor )
{ {
if( !IsGRSPolyDrawable( ClipBox, n, Points ) ) if( !IsGRSPolyDrawable( ClipBox, n, Points ) )
return; return;
...@@ -878,12 +871,9 @@ static void GRSPoly( EDA_RECT* ClipBox, ...@@ -878,12 +871,9 @@ static void GRSPoly( EDA_RECT* ClipBox,
/* clip before send the filled polygon to wxDC, because under linux /* clip before send the filled polygon to wxDC, because under linux
* (GTK?) polygons having large coordinates are incorrectly drawn * (GTK?) polygons having large coordinates are incorrectly drawn
* (integer overflow in coordinates, I am guessing)
*/ */
#ifdef USE_CLIP_FILLED_POLYGONS ClipAndDrawPoly( ClipBox, DC, Points, n );
ClipAndDrawFilledPoly( ClipBox, DC, Points, n );
#else
DC->DrawPolygon( n, Points ); // does not work very well under linux
#endif
} }
else else
{ {
...@@ -903,47 +893,36 @@ static void GRSPoly( EDA_RECT* ClipBox, ...@@ -903,47 +893,36 @@ static void GRSPoly( EDA_RECT* ClipBox,
/* /*
* Draw a new closed polyline and fill it if Fill, in screen space. * Draw a new closed polyline and fill it if Fill, in screen space.
*/ */
static void GRSClosedPoly( EDA_RECT* ClipBox, static void GRSClosedPoly( EDA_RECT* aClipBox, wxDC* aDC,
wxDC* DC, int aPointCount, wxPoint aPoints[],
int aPointCount, bool aFill, int aWidth,
wxPoint aPoints[], EDA_COLOR_T aColor,
bool Fill, EDA_COLOR_T aBgColor )
int width,
EDA_COLOR_T Color,
EDA_COLOR_T BgColor )
{ {
if( !IsGRSPolyDrawable( ClipBox, aPointCount, aPoints ) ) if( !IsGRSPolyDrawable( aClipBox, aPointCount, aPoints ) )
return; return;
GRSetColorPen( DC, Color, width ); GRSetColorPen( aDC, aColor, aWidth );
if( Fill && ( aPointCount > 2 ) ) if( aFill && ( aPointCount > 2 ) )
{ {
GRLastMoveToX = aPoints[aPointCount - 1].x; GRLastMoveToX = aPoints[aPointCount - 1].x;
GRLastMoveToY = aPoints[aPointCount - 1].y; GRLastMoveToY = aPoints[aPointCount - 1].y;
GRSetBrush( DC, BgColor, FILLED ); GRSetBrush( aDC, aBgColor, FILLED );
#ifdef USE_CLIP_FILLED_POLYGONS ClipAndDrawPoly( aClipBox, aDC, aPoints, aPointCount );
ClipAndDrawFilledPoly( ClipBox, DC, aPoints, aPointCount );
#else
DC->DrawPolygon( aPointCount, aPoints ); // does not work very well under linux
#endif
} }
else else
{ {
GRSetBrush( DC, BgColor ); GRSetBrush( aDC, aBgColor );
DC->DrawLines( aPointCount, aPoints ); aDC->DrawLines( aPointCount, aPoints );
int lastpt = aPointCount - 1;
/* Close the polygon. */ /* Close the polygon. */
if( aPoints[aPointCount - 1] != aPoints[0] ) if( aPoints[lastpt] != aPoints[0] )
{ {
GRLine( ClipBox, GRLine( aClipBox, aDC, aPoints[0].x, aPoints[0].y,
DC, aPoints[lastpt].x, aPoints[lastpt].y,
aPoints[0].x, aWidth, aColor );
aPoints[0].y,
aPoints[aPointCount - 1].x,
aPoints[aPointCount - 1].y,
width,
Color );
} }
} }
} }
...@@ -1153,7 +1132,7 @@ void GRFilledArc( EDA_RECT* ClipBox, ...@@ -1153,7 +1132,7 @@ void GRFilledArc( EDA_RECT* ClipBox,
void GRFilledArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y, void GRFilledArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y,
double StAngle, double EndAngle, int r, double StAngle, double EndAngle, int r,
EDA_COLOR_T Color, EDA_COLOR_T BgColor ) EDA_COLOR_T Color, EDA_COLOR_T BgColor )
{ {
GRFilledArc( ClipBox, DC, x, y, StAngle, EndAngle, r, 0, Color, BgColor ); GRFilledArc( ClipBox, DC, x, y, StAngle, EndAngle, r, 0, Color, BgColor );
...@@ -1322,30 +1301,20 @@ void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, ...@@ -1322,30 +1301,20 @@ void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2,
int aWidth, EDA_COLOR_T aColor, wxPenStyle aStyle ) int aWidth, EDA_COLOR_T aColor, wxPenStyle aStyle )
{ {
wxPoint points[5]; wxPoint points[5];
points[0] = wxPoint(x1, y1); points[0] = wxPoint(x1, y1);
points[1] = wxPoint(x1, y2); points[1] = wxPoint(x1, y2);
points[2] = wxPoint(x2, y2); points[2] = wxPoint(x2, y2);
points[3] = wxPoint(x2, y1); points[3] = wxPoint(x2, y1);
points[4] = points[0]; points[4] = points[0];
GRSetColorPen( aDC, aColor, aWidth, aStyle ); GRSClosedPoly( aClipBox, aDC, 5, points, NOT_FILLED, aWidth,
GRSetBrush( aDC, BLACK ); aColor, aColor );
if( aClipBox )
{
EDA_RECT clipbox(*aClipBox);
clipbox.Inflate(aWidth);
ClipAndDrawFilledPoly(&clipbox, aDC, points, 5); // polygon approach is more accurate
}
else
ClipAndDrawFilledPoly(aClipBox, aDC, points, 5);
} }
void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2,
int aWidth, EDA_COLOR_T aColor, EDA_COLOR_T aBgColor ) int aWidth, EDA_COLOR_T aColor, EDA_COLOR_T aBgColor )
{ {
wxPoint points[5]; wxPoint points[5];
points[0] = wxPoint(x1, y1); points[0] = wxPoint(x1, y1);
points[1] = wxPoint(x1, y2); points[1] = wxPoint(x1, y2);
...@@ -1354,21 +1323,19 @@ void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y ...@@ -1354,21 +1323,19 @@ void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y
points[4] = points[0]; points[4] = points[0];
GRSetBrush( aDC, aBgColor, FILLED ); GRSetBrush( aDC, aBgColor, FILLED );
GRSetColorPen( aDC, aBgColor, aWidth ); GRSetColorPen( aDC, aBgColor, aWidth );
if( aClipBox && (aWidth > 0) ) if( aClipBox && (aWidth > 0) )
{ {
EDA_RECT clipbox(*aClipBox); EDA_RECT clipbox(*aClipBox);
clipbox.Inflate(aWidth); clipbox.Inflate(aWidth);
ClipAndDrawFilledPoly(&clipbox, aDC, points, 5); // polygon approach is more accurate ClipAndDrawPoly(&clipbox, aDC, points, 5); // polygon approach is more accurate
} }
else else
ClipAndDrawFilledPoly(aClipBox, aDC, points, 5); ClipAndDrawPoly(aClipBox, aDC, points, 5 );
} }
#ifdef USE_CLIP_FILLED_POLYGONS
/** /**
* Function ClipAndDrawFilledPoly * Function ClipAndDrawPoly
* Used to clip a polygon and draw it as Filled Polygon * Used to clip a polygon and draw it as Filled Polygon
* uses the Sutherland and Hodgman algo to clip the given poly against a * uses the Sutherland and Hodgman algo to clip the given poly against a
* rectangle. This rectangle is the drawing area this is useful under * rectangle. This rectangle is the drawing area this is useful under
...@@ -1382,7 +1349,7 @@ void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y ...@@ -1382,7 +1349,7 @@ void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y
*/ */
#include <SutherlandHodgmanClipPoly.h> #include <SutherlandHodgmanClipPoly.h>
void ClipAndDrawFilledPoly( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPoints[], int n ) void ClipAndDrawPoly( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPoints[], int n )
{ {
if( aClipBox == NULL ) if( aClipBox == NULL )
{ {
...@@ -1417,9 +1384,6 @@ void ClipAndDrawFilledPoly( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPoints[], in ...@@ -1417,9 +1384,6 @@ void ClipAndDrawFilledPoly( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPoints[], in
} }
#endif
void GRBezier( EDA_RECT* ClipBox, void GRBezier( EDA_RECT* ClipBox,
wxDC* DC, wxDC* DC,
int x1, int x1,
...@@ -1561,7 +1525,7 @@ EDA_COLOR_T ColorFindNearest( const wxColour &aColor ) ...@@ -1561,7 +1525,7 @@ EDA_COLOR_T ColorFindNearest( const wxColour &aColor )
return candidate; return candidate;
} }
void GRDrawAnchor( EDA_RECT *aClipBox, wxDC *aDC, int x, int y, void GRDrawAnchor( EDA_RECT *aClipBox, wxDC *aDC, int x, int y,
int aSize, EDA_COLOR_T aColor ) int aSize, EDA_COLOR_T aColor )
{ {
int anchor_size = aDC->DeviceToLogicalXRel( aSize ); int anchor_size = aDC->DeviceToLogicalXRel( aSize );
......
...@@ -56,9 +56,11 @@ ...@@ -56,9 +56,11 @@
DRAWSEGMENT::DRAWSEGMENT( BOARD_ITEM* aParent, KICAD_T idtype ) : DRAWSEGMENT::DRAWSEGMENT( BOARD_ITEM* aParent, KICAD_T idtype ) :
BOARD_ITEM( aParent, idtype ) BOARD_ITEM( aParent, idtype )
{ {
m_Width = m_Type = m_Angle = 0; m_Type = 0;
m_Angle = 0;
m_Flags = 0; m_Flags = 0;
m_Shape = S_SEGMENT; m_Shape = S_SEGMENT;
m_Width = Millimeter2iu( 0.15 ); // Gives a decent width
} }
......
...@@ -57,7 +57,7 @@ EDGE_MODULE::EDGE_MODULE( MODULE* parent, STROKE_T aShape ) : ...@@ -57,7 +57,7 @@ EDGE_MODULE::EDGE_MODULE( MODULE* parent, STROKE_T aShape ) :
{ {
m_Shape = aShape; m_Shape = aShape;
m_Angle = 0; m_Angle = 0;
m_Width = 120; m_Layer = SILKSCREEN_N_FRONT;
} }
...@@ -251,9 +251,9 @@ void EDGE_MODULE::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) ...@@ -251,9 +251,9 @@ void EDGE_MODULE::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
aList.push_back( MSG_PANEL_ITEM( _( "Value" ), module->GetValue(), BLUE ) ); aList.push_back( MSG_PANEL_ITEM( _( "Value" ), module->GetValue(), BLUE ) );
msg.Printf( wxT( "%8.8lX" ), module->GetTimeStamp() ); msg.Printf( wxT( "%8.8lX" ), module->GetTimeStamp() );
aList.push_back( MSG_PANEL_ITEM( _( "TimeStamp" ), msg, BROWN ) ); aList.push_back( MSG_PANEL_ITEM( _( "TimeStamp" ), msg, BROWN ) );
aList.push_back( MSG_PANEL_ITEM( _( "Mod Layer" ), aList.push_back( MSG_PANEL_ITEM( _( "Mod Layer" ),
module->GetLayerName(), RED ) ); module->GetLayerName(), RED ) );
aList.push_back( MSG_PANEL_ITEM( _( "Seg Layer" ), aList.push_back( MSG_PANEL_ITEM( _( "Seg Layer" ),
GetLayerName(), RED ) ); GetLayerName(), RED ) );
msg = ::CoordinateToString( m_Width ); msg = ::CoordinateToString( m_Width );
aList.push_back( MSG_PANEL_ITEM( _( "Width" ), msg, BLUE ) ); aList.push_back( MSG_PANEL_ITEM( _( "Width" ), msg, BLUE ) );
......
...@@ -50,7 +50,9 @@ PCB_TARGET::PCB_TARGET( BOARD_ITEM* aParent ) : ...@@ -50,7 +50,9 @@ PCB_TARGET::PCB_TARGET( BOARD_ITEM* aParent ) :
BOARD_ITEM( aParent, PCB_TARGET_T ) BOARD_ITEM( aParent, PCB_TARGET_T )
{ {
m_Shape = 0; m_Shape = 0;
m_Size = 5000; m_Size = Millimeter2iu( 5 ); // Gives a decent size
m_Width = Millimeter2iu( 0.15 ); // Gives a decent width
m_Layer = EDGE_N; // a target is on all layers
} }
PCB_TARGET::PCB_TARGET( BOARD_ITEM* aParent, int aShape, LAYER_NUM aLayer, PCB_TARGET::PCB_TARGET( BOARD_ITEM* aParent, int aShape, LAYER_NUM aLayer,
......
...@@ -124,7 +124,7 @@ TRACK* GetTrace( TRACK* aStartTrace, TRACK* aEndTrace, const wxPoint& aPosition, ...@@ -124,7 +124,7 @@ TRACK* GetTrace( TRACK* aStartTrace, TRACK* aEndTrace, const wxPoint& aPosition,
TRACK::TRACK( BOARD_ITEM* aParent, KICAD_T idtype ) : TRACK::TRACK( BOARD_ITEM* aParent, KICAD_T idtype ) :
BOARD_CONNECTED_ITEM( aParent, idtype ) BOARD_CONNECTED_ITEM( aParent, idtype )
{ {
m_Width = 0; m_Width = Millimeter2iu( 0.2 );
m_Shape = S_SEGMENT; m_Shape = S_SEGMENT;
start = end = NULL; start = end = NULL;
SetDrillDefault(); SetDrillDefault();
...@@ -187,6 +187,7 @@ SEGVIA::SEGVIA( BOARD_ITEM* aParent ) : ...@@ -187,6 +187,7 @@ SEGVIA::SEGVIA( BOARD_ITEM* aParent ) :
TRACK( aParent, PCB_VIA_T ) TRACK( aParent, PCB_VIA_T )
{ {
SetShape( VIA_THROUGH ); SetShape( VIA_THROUGH );
m_Width = Millimeter2iu( 0.5 );
} }
......
#!/usr/bin/python # This python script wizard creates a FPC connector
# for Surface Mounted Technology
from pcbnew import * from pcbnew import *
...@@ -6,7 +8,7 @@ class FPCFootprintWizard(FootprintWizardPlugin): ...@@ -6,7 +8,7 @@ class FPCFootprintWizard(FootprintWizardPlugin):
def __init__(self): def __init__(self):
FootprintWizardPlugin.__init__(self) FootprintWizardPlugin.__init__(self)
self.name = "FPC" self.name = "FPC"
self.description = "FPC Footprint Wizard" self.description = "FPC (SMTechnology) Footprint Wizard"
self.parameters = { self.parameters = {
"Pads": "Pads":
{"*n":40, # not internal units preceded by "*" {"*n":40, # not internal units preceded by "*"
...@@ -17,9 +19,9 @@ class FPCFootprintWizard(FootprintWizardPlugin): ...@@ -17,9 +19,9 @@ class FPCFootprintWizard(FootprintWizardPlugin):
{"shield_to_pad": FromMM(1.6), {"shield_to_pad": FromMM(1.6),
"from_top": FromMM(1.3), "from_top": FromMM(1.3),
"width": FromMM(1.5), "width": FromMM(1.5),
"height": FromMM(2)}, "height": FromMM(2)}
} }
self.ClearErrors() self.ClearErrors()
# build a rectangular pad # build a rectangular pad
...@@ -75,48 +77,61 @@ class FPCFootprintWizard(FootprintWizardPlugin): ...@@ -75,48 +77,61 @@ class FPCFootprintWizard(FootprintWizardPlugin):
shl_to_pad = p["Shield"]["shield_to_pad"] shl_to_pad = p["Shield"]["shield_to_pad"]
shl_from_top = p["Shield"]["from_top"] shl_from_top = p["Shield"]["from_top"]
offsetX = pad_pitch*(pads-1)/2
size_pad = wxSize(pad_width,pad_height) size_pad = wxSize(pad_width,pad_height)
size_shld = wxSize(shl_width,shl_height) size_shld = wxSize(shl_width,shl_height)
size_text = wxSize( FromMM( 0.8), FromMM( 0.7) )
textposy = pad_height/2 + FromMM(1)
module.SetReference("FPC"+str(pads)) # give it a reference name module.SetReference("FPC"+str(pads)) # give it a reference name
module.Reference().SetPos0(wxPointMM(-1,-2)) module.Reference().SetPos0(wxPoint(0, textposy))
module.Reference().SetPosition(wxPointMM(-1,-2)) module.Reference().SetTextPosition(module.Reference().GetPos0())
module.Reference().SetSize( size_text )
textposy = textposy + FromMM(1)
module.SetValue("Val***") # give it a default value
module.Value().SetPos0( wxPoint(0, textposy) )
module.Value().SetTextPosition(module.Value().GetPos0())
module.Value().SetSize( size_text )
module.SetLibRef("FPC"+str(pads)) #the name in library
# create a pad array and add it to the module # create a pad array and add it to the module
for n in range (0,pads): for n in range (0,pads):
pad = self.smdRectPad(module,size_pad,wxPoint(pad_pitch*n,0),str(n+1)) xpos = pad_pitch*n - offsetX
pad = self.smdRectPad(module,size_pad,wxPoint(xpos,0),str(n+1))
module.Add(pad) module.Add(pad)
pad_s0 = self.smdRectPad(module, xpos = -shl_to_pad-offsetX
size_shld, pad_s0 = self.smdRectPad(module, size_shld, wxPoint(xpos,shl_from_top), "0")
wxPoint(-shl_to_pad,shl_from_top), xpos = (pads-1)*pad_pitch+shl_to_pad-offsetX
"0") pad_s1 = self.smdRectPad(module, size_shld, wxPoint(xpos,shl_from_top), "0")
pad_s1 = self.smdRectPad(module,
size_shld,
wxPoint((pads-1)*pad_pitch+shl_to_pad,shl_from_top),
"0")
module.Add(pad_s0) module.Add(pad_s0)
module.Add(pad_s1) module.Add(pad_s1)
e = EDGE_MODULE(module) #add outline
e.SetStartEnd(wxPointMM(-1,0),wxPointMM(0,0)) outline = EDGE_MODULE(module)
e.SetWidth(FromMM(0.2)) width = FromMM(0.2)
e.SetLayer(EDGE_LAYER) posy = -pad_height/2 - width/2 -FromMM(0.2)
e.SetShape(S_SEGMENT) outline.SetStartEnd(wxPoint(pad_pitch * pads - pad_pitch*0.5-offsetX, posy),
module.Add(e) wxPoint( - pad_pitch*0.5-offsetX, posy))
outline.SetWidth(width)
module.SetLibRef("FPC"+str(pads)) outline.SetLayer(SILKSCREEN_N_FRONT) #default: not needed
outline.SetShape(S_SEGMENT)
module.Add(outline)
def register():
# create our footprint wizard outline1 = EDGE_MODULE(module)
fpc_wizard = FPCFootprintWizard() outline1.Copy(outline) #copy all settings from outline
posy = pad_height/2 + width/2 +FromMM(0.2)
# register it into pcbnew outline1.SetStartEnd(wxPoint(pad_pitch * pads - pad_pitch*0.5-offsetX, posy),
fpc_wizard.register() wxPoint( - pad_pitch*0.5-offsetX, posy))
module.Add(outline1)
return fpc_wizard
# create our footprint wizard
fpc_wizard = FPCFootprintWizard()
# register it into pcbnew
fpc_wizard.register()
...@@ -164,12 +164,18 @@ class TouchSliderWizard(FootprintWizardPlugin): ...@@ -164,12 +164,18 @@ class TouchSliderWizard(FootprintWizardPlugin):
step_length = float(touch_length) / float(steps) step_length = float(touch_length) / float(steps)
size_text = wxSize( FromMM( 1), FromMM( 1) );
module.SetReference("TS"+str(steps)) # give it a reference name module.SetReference("TS"+str(steps)) # give it a reference name
module.Reference().SetPos0(wxPointMM(-1,-2)) module.Reference().SetPos0(wxPointMM(0,-2))
module.Reference().SetPosition(wxPointMM(-1,-2)) module.Reference().SetTextPosition(module.Reference().GetPos0())
module.Reference().SetSize( size_text );
# starting pad module.SetValue("Val**") # give it a value
module.Value().SetPos0(wxPointMM(0,-3.2))
module.Value().SetTextPosition(module.Value().GetPos0())
module.Value().SetSize( size_text );
# starting pad
pos = wxPointMM(0,0) pos = wxPointMM(0,0)
band_width = touch_width/bands band_width = touch_width/bands
......
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