Commit 339a90e7 authored by CHARRAS's avatar CHARRAS

Block commands now works with zones.

parent cfd09d02
......@@ -4,6 +4,13 @@ Started 2007-June-11
Please add newer entries at the top, list the date and your name with
email address.
2008-jan-06 UPDATE Jean-Pierre Charras <jean-pierre.charras@inpg.fr>
================================================================================
+pcbnew:
Block commands now works with zones.
Some code cleanning.
2008-Jan-5 UPDATE Dick Hollenbeck <dick@softplc.com>
================================================================================
+pcbnew:
......
......@@ -294,6 +294,20 @@ bool EDA_TextStruct::HitTest( const wxPoint& posref )
return false;
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* @param refArea the given EDA_Rect to test
* @return bool - true if a hit, else false
*/
/*********************************************************/
bool EDA_TextStruct::HitTest( EDA_Rect& refArea )
/*********************************************************/
{
if( refArea.Inside( m_Pos ) )
return true;
return false;
}
/*******************************/
int EDA_TextStruct::Pitch()
......
......@@ -89,6 +89,7 @@ enum SEARCH_RESULT {
class EDA_BaseStruct;
class WinEDA_DrawFrame;
class BOARD;
class EDA_Rect;
/**
* Class INSPECTOR
......@@ -254,6 +255,18 @@ public:
return false; // derived classes should override this function
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* For now, an ending point must be inside this rect.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
virtual bool HitTest( EDA_Rect& refArea )
{
return false; // derived classes should override this function
}
/**
* Function IterateForward
......@@ -418,6 +431,15 @@ public:
*/
bool HitTest( const wxPoint& ref_pos );
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* For now, the anchor must be inside this rect.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea );
/**
* Function Len_Size
* Return the text lenght in internal units
......
......@@ -301,6 +301,15 @@ public:
*/
bool HitTest( const wxPoint& ref_pos );
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* For now, an ending point must be inside this rect.
* @param refPos the given EDA_Rect to test
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea );
/**
* Function GetClass
* returns the class name.
......
This diff is collapsed.
......@@ -18,10 +18,10 @@ class BOARD : public BOARD_ITEM
friend class WinEDA_PcbFrame;
private:
std::vector<MARKER*> m_markers; ///< MARKERs for clearance problems, owned by pointer
std::vector<ZONE_CONTAINER*> m_ZoneDescriptorList; ///< edge zone descriptors, owned by pointer
public:
std::vector<ZONE_CONTAINER*> m_ZoneDescriptorList; ///< edge zone descriptors, owned by pointer
public:
WinEDA_BasePcbFrame* m_PcbFrame; // Window de visualisation
......
......@@ -226,70 +226,109 @@ bool COTATION::ReadCotationDescr( FILE* File, int* LineNum )
return FALSE;
}
#if 0
/**************************************************/
bool COTATION::WriteCotationDescr( FILE* File )
/**************************************************/
/****************************************/
void COTATION::Move(const wxPoint& offset)
/****************************************/
/**
* Function Move
* @param offset : moving vector
*/
{
if( GetState( DELETED ) )
return FALSE;
fprintf( File, "$COTATION\n" );
fprintf( File, "Ge %d %d %lX\n", m_Shape,
m_Layer, m_TimeStamp );
fprintf( File, "Va %d\n", m_Value );
if( !m_Text->m_Text.IsEmpty() )
fprintf( File, "Te \"%s\"\n", CONV_TO_UTF8( m_Text->m_Text ) );
else
fprintf( File, "Te \"?\"\n" );
fprintf( File, "Po %d %d %d %d %d %d %d\n",
m_Text->m_Pos.x, m_Text->m_Pos.y,
m_Text->m_Size.x, m_Text->m_Size.y,
m_Text->m_Width, m_Text->m_Orient,
m_Text->m_Miroir );
fprintf( File, "Sb %d %d %d %d %d %d\n", S_SEGMENT,
Barre_ox, Barre_oy,
Barre_fx, Barre_fy, m_Width );
fprintf( File, "Sd %d %d %d %d %d %d\n", S_SEGMENT,
TraitD_ox, TraitD_oy,
TraitD_fx, TraitD_fy, m_Width );
fprintf( File, "Sg %d %d %d %d %d %d\n", S_SEGMENT,
TraitG_ox, TraitG_oy,
TraitG_fx, TraitG_fy, m_Width );
fprintf( File, "S1 %d %d %d %d %d %d\n", S_SEGMENT,
FlecheD1_ox, FlecheD1_oy,
FlecheD1_fx, FlecheD1_fy, m_Width );
fprintf( File, "S2 %d %d %d %d %d %d\n", S_SEGMENT,
FlecheD2_ox, FlecheD2_oy,
FlecheD2_fx, FlecheD2_fy, m_Width );
m_Pos += offset;
m_Text->m_Pos += offset;
Barre_ox += offset.x; Barre_oy += offset.y;
Barre_fx += offset.x; Barre_fy += offset.y;
TraitG_ox += offset.x; TraitG_oy += offset.y;
TraitG_fx += offset.x; TraitG_fy += offset.y;
TraitD_ox += offset.x; TraitD_oy += offset.y;
TraitD_fx += offset.x; TraitD_fy += offset.y;
FlecheG1_ox += offset.x; FlecheG1_oy += offset.y;
FlecheG1_fx += offset.x; FlecheG1_fy += offset.y;
FlecheG2_ox += offset.x; FlecheG2_oy += offset.y;
FlecheG2_fx += offset.x; FlecheG2_fy += offset.y;
FlecheD1_ox += offset.x; FlecheD1_oy += offset.y;
FlecheD1_fx += offset.x; FlecheD1_fy += offset.y;
FlecheD2_ox += offset.x; FlecheD2_oy += offset.y;
FlecheD2_fx += offset.x; FlecheD2_fy += offset.y;
}
fprintf( File, "S3 %d %d %d %d %d %d\n", S_SEGMENT,
FlecheG1_ox, FlecheG1_oy,
FlecheG1_fx, FlecheG1_fy, m_Width );
fprintf( File, "S4 %d %d %d %d %d %d\n", S_SEGMENT,
FlecheG2_ox, FlecheG2_oy,
FlecheG2_fx, FlecheG2_fy, m_Width );
/******************************************************/
void COTATION::Rotate(const wxPoint& centre, int angle)
/******************************************************/
/**
* Function Rotate
* @param offset : Rotation point
* @param angle : Rotation angle in 0.1 degrees
*/
{
RotatePoint( &m_Pos, centre, 900 );
RotatePoint( &m_Text->m_Pos, centre, 900 );
m_Text->m_Orient += 900;
if( m_Text->m_Orient >= 3600 )
m_Text->m_Orient -= 3600;
if( (m_Text->m_Orient > 900)
&& (m_Text->m_Orient <2700) )
m_Text->m_Orient -= 1800;
RotatePoint( &Barre_ox, &Barre_oy, centre.x, centre.y, 900 );
RotatePoint( &Barre_fx, &Barre_fy, centre.x, centre.y, 900 );
RotatePoint( &TraitG_ox, &TraitG_oy, centre.x, centre.y, 900 );
RotatePoint( &TraitG_fx, &TraitG_fy, centre.x, centre.y, 900 );
RotatePoint( &TraitD_ox, &TraitD_oy, centre.x, centre.y, 900 );
RotatePoint( &TraitD_fx, &TraitD_fy, centre.x, centre.y, 900 );
RotatePoint( &FlecheG1_ox, &FlecheG1_oy, centre.x, centre.y, 900 );
RotatePoint( &FlecheG1_fx, &FlecheG1_fy, centre.x, centre.y, 900 );
RotatePoint( &FlecheG2_ox, &FlecheG2_oy, centre.x, centre.y, 900 );
RotatePoint( &FlecheG2_fx, &FlecheG2_fy, centre.x, centre.y, 900 );
RotatePoint( &FlecheD1_ox, &FlecheD1_oy, centre.x, centre.y, 900 );
RotatePoint( &FlecheD1_fx, &FlecheD1_fy, centre.x, centre.y, 900 );
RotatePoint( &FlecheD2_ox, &FlecheD2_oy, centre.x, centre.y, 900 );
RotatePoint( &FlecheD2_fx, &FlecheD2_fy, centre.x, centre.y, 900 );
}
fprintf( File, "$EndCOTATION\n" );
return 1;
/**********************************************/
void COTATION::Mirror(const wxPoint& axis_pos)
/**********************************************/
/**
* Function Mirror
* Mirror the Dimension , relative to a given horizontal axis
* the text is not mirrored. only its position (and angle) is mirrored
* the layer is not changed
* @param axis_pos : vertical axis position
*/
{
#define INVERT( pos ) (pos) = axis_pos.y - ( (pos) - axis_pos.y )
#define INVERT_ANGLE( phi ) (phi) = -(phi)
INVERT( m_Pos.y );
INVERT( m_Text->m_Pos.y );
INVERT_ANGLE( m_Text->m_Orient );
if( m_Text->m_Orient >= 3600 )
m_Text->m_Orient -= 3600;
if( (m_Text->m_Orient > 900) && (m_Text->m_Orient <2700) )
m_Text->m_Orient -= 1800;
INVERT( Barre_oy );
INVERT( Barre_fy );
INVERT( TraitG_oy );
INVERT( TraitG_fy );
INVERT( TraitD_oy );
INVERT( TraitD_fy );
INVERT( FlecheG1_oy );
INVERT( FlecheG1_fy );
INVERT( FlecheG2_oy );
INVERT( FlecheG2_fy );
INVERT( FlecheD1_oy );
INVERT( FlecheD1_fy );
INVERT( FlecheD2_oy );
INVERT( FlecheD2_fy );
}
#endif
/****************************************/
bool COTATION::Save( FILE* aFile ) const
/****************************************/
{
if( GetState( DELETED ) )
return true;
......@@ -571,3 +610,15 @@ bool COTATION::HitTest( const wxPoint& ref_pos )
return false;
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* @param EDA_Rect : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool COTATION::HitTest( EDA_Rect& refArea )
{
if( refArea.Inside( m_Pos ) )
return true;
return false;
}
......@@ -55,6 +55,27 @@ public:
void Draw( WinEDA_DrawPanel* panel, wxDC* DC, const wxPoint& offset, int mode_color );
/**
* Function Move
* @param offset : moving vector
*/
void Move(const wxPoint& offset);
/**
* Function Rotate
* @param offset : Rotation point
* @param angle : Rotation angle in 0.1 degrees
*/
void Rotate(const wxPoint& centre, int angle);
/**
* Function Mirror
* Mirror the Dimension , relative to a given horizontal axis
* the text is not mirrored. only its position (and angle) is mirrored
* the layer is not changed
* @param axis_pos : vertical axis position
*/
void Mirror(const wxPoint& axis_pos);
/**
* Function Display_Infos
......@@ -73,6 +94,15 @@ public:
*/
bool HitTest( const wxPoint& ref_pos );
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* For now, the anchor must be inside this rect.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea );
/**
* Function GetClass
......
......@@ -219,3 +219,16 @@ bool MIREPCB::HitTest( const wxPoint& refPos )
return abs(dX)<=rayon && abs(dY)<=rayon;
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* @param EDA_Rect : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool MIREPCB::HitTest( EDA_Rect& refArea )
{
if( refArea.Inside( m_Pos ) )
return true;
return false;
}
......@@ -50,6 +50,16 @@ public:
* @return bool - true if a hit, else false
*/
bool HitTest( const wxPoint& refPos );
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* For now, the anchor must be inside this rect.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea );
};
......
......@@ -1284,6 +1284,31 @@ bool MODULE::HitTest( const wxPoint& refPos )
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect the bounds of this object.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool MODULE::HitTest( EDA_Rect& refArea )
{
bool is_out_of_box = false;
SetRectangleExinscrit();
if( m_RealBoundaryBox.m_Pos.x < refArea.GetX() )
is_out_of_box = true;
if( m_RealBoundaryBox.m_Pos.y < refArea.GetY() )
is_out_of_box = true;
if( m_RealBoundaryBox.GetRight() > refArea.GetRight() )
is_out_of_box = true;
if( m_RealBoundaryBox.GetBottom() > refArea.GetBottom() )
is_out_of_box = true;
return is_out_of_box ? false : true;
}
// see class_module.h
SEARCH_RESULT MODULE::Visit( INSPECTOR* inspector, const void* testData,
const KICAD_T scanTypes[] )
......
......@@ -169,6 +169,14 @@ public:
bool HitTest( const wxPoint& refPos );
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect the bounds of this object.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea );
/**
* Function GetReference
* @return wxString - the reference designator text.
......
......@@ -112,27 +112,6 @@ int TEXTE_PCB::ReadTextePcbDescr( FILE* File, int* LineNum )
}
#if 0 // replaced by Save()
/**************************************************/
int TEXTE_PCB::WriteTextePcbDescr( FILE* File )
/**************************************************/
{
if( GetState( DELETED ) )
return 0;
if( m_Text.IsEmpty() )
return 0;
fprintf( File, "$TEXTPCB\n" );
fprintf( File, "Te \"%s\"\n", CONV_TO_UTF8( m_Text ) );
fprintf( File, "Po %d %d %d %d %d %d\n",
m_Pos.x, m_Pos.y, m_Size.x, m_Size.y, m_Width, m_Orient );
fprintf( File, "De %d %d %lX %d\n", m_Layer, m_Miroir, m_TimeStamp, 0 );
fprintf( File, "$EndTEXTPCB\n" );
return 1;
}
#endif
bool TEXTE_PCB::Save( FILE* aFile ) const
{
if( GetState( DELETED ) )
......
......@@ -68,6 +68,17 @@ public:
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* @param refArea the given EDA_Rect to test
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea )
{
return EDA_TextStruct::HitTest( refArea );
}
/**
* Function GetClass
* returns the class name.
......
......@@ -875,6 +875,22 @@ bool TRACK::HitTest( const wxPoint& ref_pos )
return false;
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* For now, an ending point must be inside this rect.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool TRACK::HitTest( EDA_Rect& refArea )
{
if( refArea.Inside( m_Start ) )
return true;
if( refArea.Inside( m_End ) )
return true;
return false;
}
#if defined (DEBUG)
......
......@@ -200,6 +200,15 @@ public:
*/
bool HitTest( const wxPoint& refPos );
/**
* Function HitTest (overlayed)
* tests if the given wxRect intersect this object.
* For now, an ending point must be inside this rect.
* @param refPos A wxPoint to test
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea );
/**
* Function GetClass
* returns the class name.
......
......@@ -10,6 +10,7 @@
#include "common.h"
#include "PolyLine.h"
#include "pcbnew.h"
#include "trigo.h"
/**********************/
/* Class EDGE_ZONE */
......@@ -51,7 +52,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD* parent ) :
m_PadOption = THERMAL_PAD;
utility = 0; // flags used in polygon calculations
utility2 = 0; // flags used in polygon calculations
m_Poly = new CPolyLine( NULL ); // Outlines
m_Poly = new CPolyLine(); // Outlines
}
......@@ -403,6 +404,29 @@ int ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos )
return -1;
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect contains the bounds of this object.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool ZONE_CONTAINER::HitTest( EDA_Rect& refArea )
{
bool is_out_of_box = false;
CRect rect = m_Poly->GetCornerBounds();
if( rect.left < refArea.GetX() )
is_out_of_box = true;
if( rect.top < refArea.GetY() )
is_out_of_box = true;
if( rect.right > refArea.GetRight() )
is_out_of_box = true;
if( rect.bottom > refArea.GetBottom() )
is_out_of_box = true;
return is_out_of_box ? false : true;
}
/************************************************************/
void ZONE_CONTAINER::Display_Infos( WinEDA_DrawFrame* frame )
......@@ -448,3 +472,79 @@ void ZONE_CONTAINER::Display_Infos( WinEDA_DrawFrame* frame )
msg.Printf( wxT( "%d" ), m_Poly->m_HatchLines.size() );
Affiche_1_Parametre( frame, text_pos, _( "Hatch lines" ), msg, BLUE );
}
/* Geometric transformations: */
/**
* Function Move
* Move the outlines
* @param offset = moving vector
*/
void ZONE_CONTAINER::Move(const wxPoint& offset )
{
for ( unsigned ii = 0; ii < m_Poly->corner.size(); ii++ )
{
m_Poly->corner[ii].x += offset.x;
m_Poly->corner[ii].y += offset.y;
}
m_Poly->Hatch();
}
/**
* Function Move
* Move the outlines
* @param centre = rot centre
* @param angle = in 0.1 degree
*/
void ZONE_CONTAINER::Rotate( const wxPoint& centre, int angle)
{
for ( unsigned ii = 0; ii < m_Poly->corner.size(); ii++ )
{
wxPoint pos;
pos.x = m_Poly->corner[ii].x;
pos.y = m_Poly->corner[ii].y;
RotatePoint(&pos, centre, angle );
m_Poly->corner[ii].x = pos.x;
m_Poly->corner[ii].y = pos.y;
}
m_Poly->Hatch();
}
/**
* Function Mirror
* flip the outlines , relative to a given horizontal axis
* @param mirror_ref = vertical axis position
*/
void ZONE_CONTAINER::Mirror( const wxPoint& mirror_ref)
{
for ( unsigned ii = 0; ii < m_Poly->corner.size(); ii++ )
{
m_Poly->corner[ii].y -= mirror_ref.y;
m_Poly->corner[ii].y = - m_Poly->corner[ii].y;
m_Poly->corner[ii].y += mirror_ref.y;
}
m_Poly->Hatch();
}
/** Function copy
* copy data from the source.
* flags and some poinetrs are NOT copied
*/
void ZONE_CONTAINER::Copy( ZONE_CONTAINER * src )
{
m_Parent = src->m_Parent;
m_Layer = src->m_Layer;
SetNet(src->GetNet());
m_TimeStamp = GetTimeStamp();
m_Poly->Copy(src->m_Poly); // copy outlines
m_CornerSelection = -1; // For corner moving, corner index to drag, or -1 if no selection
m_ZoneClearance = src->m_ZoneClearance; // clearance value
m_GridFillValue = src->m_GridFillValue; // Grid used for filling
m_PadOption = src->m_PadOption;
m_Poly->SetHatch(src->m_Poly->GetHatchStyle());
}
......@@ -44,6 +44,12 @@ public:
wxPoint & GetPosition( ) { static wxPoint pos ;return pos; }
void UnLink(void) {};
/** Function copy
* copy data from the source.
* flags and some poinetrs are NOT copied
*/
void Copy( ZONE_CONTAINER * src );
void Display_Infos( WinEDA_DrawFrame* frame );
/** Function Draw
......@@ -81,6 +87,14 @@ public:
*/
int HitTestForEdge( const wxPoint& refPos );
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect contains the bounds of this object.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool HitTest( EDA_Rect& refArea );
/** Function Fill_Zone()
* Calculate the zone filling
* The zone outline is a frontier, and can be complex (with holes)
......@@ -92,6 +106,32 @@ public:
* @return error level (0 = no error)
*/
int Fill_Zone( WinEDA_PcbFrame* frame, wxDC* DC, bool verbose = TRUE);
/* Geometric transformations: */
/**
* Function Move
* Move the outlines
* @param offset = moving vector
*/
void Move(const wxPoint& offset );
/**
* Function Move
* Move the outlines
* @param centre = rot centre
* @param angle = in 0.1 degree
*/
void Rotate( const wxPoint& centre, int angle);
/**
* Function Mirror
* Mirror the outlines , relative to a given horizontal axis
* the layer is not changed
* @param mirror_ref = vertical axis position
*/
void Mirror( const wxPoint& mirror_ref);
};
/*******************/
......
......@@ -85,27 +85,6 @@ void DRAWSEGMENT::Copy( DRAWSEGMENT* source )
m_TimeStamp = source->m_TimeStamp;
}
#if 0 // replaced by Save()
/********************************************************/
bool DRAWSEGMENT::WriteDrawSegmentDescr( FILE* File )
/********************************************************/
{
if( GetState( DELETED ) )
return FALSE;
fprintf( File, "$DRAWSEGMENT\n" );
fprintf( File, "Po %d %d %d %d %d %d\n",
m_Shape,
m_Start.x, m_Start.y,
m_End.x, m_End.y, m_Width );
fprintf( File, "De %d %d %d %lX %X\n",
m_Layer, m_Type, m_Angle,
m_TimeStamp, ReturnStatus() );
fprintf( File, "$EndDRAWSEGMENT\n" );
return TRUE;
}
#endif
bool DRAWSEGMENT::Save( FILE* aFile ) const
{
......@@ -270,6 +249,22 @@ bool DRAWSEGMENT::HitTest( const wxPoint& ref_pos )
}
/**
* Function HitTest (overlayed)
* tests if the given EDA_Rect intersect this object.
* For now, an ending point must be inside this rect.
* @param refArea : the given EDA_Rect
* @return bool - true if a hit, else false
*/
bool DRAWSEGMENT::HitTest( EDA_Rect& refArea )
{
if( refArea.Inside( m_Start ) )
return true;
if( refArea.Inside( m_End ) )
return true;
return false;
}
/**************************************************/
/* Class SCREEN: classe de gestion d'un affichage */
......
SET(POLYGON_SRCS
cdisplaylist_stuff.cpp
GenericPolygonClipperLibrary.cpp
math_for_graphics.cpp
php_polygon.cpp
......
// PolyLine.cpp ... implementation of CPolyLine class
// from FreePCB.
// PolyLine.cpp ... implementation of CPolyLine class from FreePCB.
//
// Adaptation for kicad
//
using namespace std;
#define SetSize reserve // used in conversion from freePCB to kicad.: The code using it must be rewitten
#include <math.h>
#include <vector>
......@@ -12,30 +14,13 @@ using namespace std;
#define to_int(x) (int)round((x))
/* Stuff to compile PolyLine.cpp, used in std::vector as CArray. does not work. must be redesigned, only for test */
#define SetSize reserve
#define pi 3.14159265359
#define DENOM 10 // to use mils for php clipping
//#define DENOM 1 // to use internal units for php clipping
// dl is a pointer to CDisplayList for drawing graphic elements
// if dl = NULL, doesn't draw anything but can still hold data
//
CPolyLine::CPolyLine( CDisplayList * dl )
{
m_dlist = dl;
m_HatchStyle = 0;
m_sel_box = 0;
m_gpc_poly = new gpc_polygon;
m_gpc_poly->num_contours = 0;
m_php_poly = new polygon;
}
CPolyLine::CPolyLine()
{
m_dlist = NULL;
m_HatchStyle = 0;
m_sel_box = 0;
utility = 0;
......@@ -496,8 +481,8 @@ int CPolyLine::RestoreArcs( std::vector<CArc> * arc_array, std::vector<CPolyLine
// find arcs and replace them
bool bFound;
int arc_start;
int arc_end;
int arc_start = 0;
int arc_end = 0;
for( unsigned iarc=0; iarc<arc_array->size(); iarc++ )
{
int arc_xi = (*arc_array)[iarc].xi;
......@@ -804,22 +789,6 @@ void CPolyLine::InsertCorner( int ic, int x, int y )
//
void CPolyLine::Undraw()
{
if( m_dlist && bDrawn )
{
// remove display elements, if present
for( unsigned i=0; i<dl_side.size(); i++ )
m_dlist->Remove( dl_side[i] );
for( unsigned i=0; i<dl_side_sel.size(); i++ )
m_dlist->Remove( dl_side_sel[i] );
for( unsigned i=0; i<dl_corner_sel.size(); i++ )
m_dlist->Remove( dl_corner_sel[i] );
// remove pointers
dl_side.clear();
dl_side_sel.clear();
dl_corner_sel.clear();
}
m_HatchLines.clear();
bDrawn = FALSE;
}
......@@ -828,274 +797,18 @@ void CPolyLine::Undraw()
// if side style is ARC_CW or ARC_CCW but endpoints are not angled,
// convert to STRAIGHT
//
void CPolyLine::Draw( CDisplayList * dl )
void CPolyLine::Draw( )
{
// first, undraw if necessary
if( bDrawn )
Undraw();
// use new display list if provided
if( dl )
m_dlist = dl;
#if 0
int i_start_contour = 0;
if( m_dlist )
{
// set up std::vectors
dl_side.SetSize( corner.size() );
if( m_sel_box )
{
dl_side_sel.SetSize( corner.size() );
dl_corner_sel.SetSize( corner.size() );
}
else
{
dl_side_sel.clear();
dl_corner_sel.clear();
}
// now draw elements
for( int ic=0; ic<corner.size(); ic++ )
{
m_id.ii = ic;
int xi = corner[ic].x;
int yi = corner[ic].y;
int xf, yf;
if( corner[ic].end_contour == FALSE && ic < corner.size()-1 )
{
xf = corner[ic+1].x;
yf = corner[ic+1].y;
}
else
{
xf = corner[i_start_contour].x;
yf = corner[i_start_contour].y;
i_start_contour = ic+1;
}
// draw
if( m_sel_box )
{
m_id.sst = ID_SEL_CORNER;
dl_corner_sel[ic] = m_dlist->AddSelector( m_id, m_ptr, m_layer, DL_HOLLOW_RECT,
1, 0, 0, xi-m_sel_box, yi-m_sel_box,
xi+m_sel_box, yi+m_sel_box, 0, 0 );
}
if( ic<(corner.size()-1) || corner[ic].end_contour )
{
// draw side
if( xi == xf || yi == yf )
{
// if endpoints not angled, make side STRAIGHT
side_style[ic] = STRAIGHT;
}
int g_type = DL_LINE;
if( side_style[ic] == STRAIGHT )
g_type = DL_LINE;
else if( side_style[ic] == ARC_CW )
g_type = DL_ARC_CW;
else if( side_style[ic] == ARC_CCW )
g_type = DL_ARC_CCW;
m_id.sst = ID_SIDE;
dl_side[ic] = m_dlist->Add( m_id, m_ptr, m_layer, g_type,
1, m_w, 0, xi, yi, xf, yf, 0, 0 );
if( m_sel_box )
{
m_id.sst = ID_SEL_SIDE;
dl_side_sel[ic] = m_dlist->AddSelector( m_id, m_ptr, m_layer, g_type,
1, m_w, 0, xi, yi, xf, yf, 0, 0 );
}
}
}
// if( m_HatchStyle )
// Hatch();
}
#endif
Hatch();
bDrawn = TRUE;
}
// start dragging new corner to be inserted into side, make side and hatching invisible
//
void CPolyLine::StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y )
{
if( !m_dlist )
ASSERT(0);
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
int post_c;
if( ic == iend )
post_c = istart;
else
post_c = ic + 1;
int xi = corner[ic].x;
int yi = corner[ic].y;
int xf = corner[post_c].x;
int yf = corner[post_c].y;
m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf,
LAY_SELECTION, LAY_SELECTION, 1, 1, DSS_STRAIGHT, DSS_STRAIGHT,
0, 0, 0, 0, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[ic], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/
}
// cancel dragging inserted corner, make side and hatching visible again
//
void CPolyLine::CancelDraggingToInsertCorner( int ic )
{
if( !m_dlist )
ASSERT(0);
int post_c;
if( ic == (int)(corner.size()-1) )
post_c = 0;
else
post_c = ic + 1;
m_dlist->StopDragging();
/* m_dlist->Set_visible( dl_side[ic], 1 );
for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 1 );
*/
}
// start dragging corner to new position, make adjacent sides and hatching invisible
//
void CPolyLine::StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y )
{
if( !m_dlist )
ASSERT(0);
// see if corner is the first or last corner of an open contour
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
if( !GetClosed()
&& icont == GetNumContours() - 1
&& (ic == istart || ic == iend) )
{
// yes
int style, xi, yi, iside;
if( ic == istart )
{
// first corner
iside = ic;
xi = GetX( ic+1 );
yi = GetY( ic+1 );
style = GetSideStyle( iside );
// reverse arc since we are drawing from corner 1 to 0
if( style == CPolyLine::ARC_CW )
style = CPolyLine::ARC_CCW;
else if( style == CPolyLine::ARC_CCW )
style = CPolyLine::ARC_CW;
}
else
{
// last corner
iside = ic - 1;
xi = GetX( ic-1 );
yi = GetY( ic-1);
style = GetSideStyle( iside );
}
m_dlist->StartDraggingArc( pDC, style, GetX(ic), GetY(ic), xi, yi, LAY_SELECTION, 1, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[iside], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/
}
else
{
// no
// get indexes for preceding and following corners
int pre_c, post_c;
int poly_side_style1, poly_side_style2;
int style1 = DSS_STRAIGHT, style2 = DSS_STRAIGHT;
if( ic == istart )
{
pre_c = iend;
post_c = istart+1;
poly_side_style1 = side_style[iend];
poly_side_style2 = side_style[istart];
}
else if( ic == iend )
{
// last side
pre_c = ic-1;
post_c = istart;
poly_side_style1 = side_style[ic-1];
poly_side_style2 = side_style[ic];
}
else
{
pre_c = ic-1;
post_c = ic+1;
poly_side_style1 = side_style[ic-1];
poly_side_style2 = side_style[ic];
}
if( poly_side_style1 == STRAIGHT )
style1 = DSS_STRAIGHT;
else if( poly_side_style1 == ARC_CW )
style1 = DSS_ARC_CW;
else if( poly_side_style1 == ARC_CCW )
style1 = DSS_ARC_CCW;
if( poly_side_style2 == STRAIGHT )
style2 = DSS_STRAIGHT;
else if( poly_side_style2 == ARC_CW )
style2 = DSS_ARC_CW;
else if( poly_side_style2 == ARC_CCW )
style2 = DSS_ARC_CCW;
int xi = corner[pre_c].x;
int yi = corner[pre_c].y;
int xf = corner[post_c].x;
int yf = corner[post_c].y;
m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf,
LAY_SELECTION, LAY_SELECTION, 1, 1, style1, style2,
0, 0, 0, 0, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[pre_c], 0 );
m_dlist->Set_visible( dl_side[ic], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/ }
}
// cancel dragging corner to new position, make sides and hatching visible again
//
// highlight side by drawing line over it
//
void CPolyLine::HighlightSide( int is )
{
if( !m_dlist )
ASSERT(0);
if( GetClosed() && is >= (int)corner.size() )
return;
if( !GetClosed() && is >= (int)(corner.size()-1) )
return;
int style = DL_LINE;
if( side_style[is] == CPolyLine::STRAIGHT )
style = DL_LINE;
else if( side_style[is] == CPolyLine::ARC_CW )
style = DL_ARC_CW;
else if( side_style[is] == CPolyLine::ARC_CCW )
style = DL_ARC_CCW;
m_dlist->HighLight( style,
m_dlist->Get_x( dl_side_sel[is] ),
m_dlist->Get_y( dl_side_sel[is] ),
m_dlist->Get_xf( dl_side_sel[is] ),
m_dlist->Get_yf( dl_side_sel[is] ),
m_dlist->Get_w( dl_side_sel[is]) );
}
int CPolyLine::GetX( int ic )
{
return corner[ic].x;
......@@ -1672,16 +1385,13 @@ int CPolyLine::TestIntersection( CPolyLine * poly )
void CPolyLine::Copy( CPolyLine * src )
{
Undraw();
m_dlist = src->m_dlist;
m_sel_box = src->m_sel_box;
// copy corners
for( unsigned i=0; i< src->corner.size(); i++ )
corner.push_back(src->corner[i]);
for( unsigned ii=0; ii < src->corner.size(); ii++ )
corner.push_back(src->corner[ii]);
// copy side styles
int nsides = src->GetNumSides();
side_style.SetSize(nsides);
for( int i=0; i<nsides; i++ )
side_style[i] = src->side_style[i];
for( unsigned ii=0; ii < src->side_style.size(); ii++ )
side_style.push_back(src->side_style[ii]);
// don't copy the Gpc_poly, just clear the old one
FreeGpcPoly();
}
......
......@@ -10,8 +10,6 @@
//
// When used for copper areas, the first contour is the outer edge
// of the area, subsequent ones are "holes" in the copper.
//
// If a CDisplayList pointer is provided, the polyline can draw itself
#ifndef POLYLINE_H
#define POLYLINE_H
......@@ -68,7 +66,7 @@ public:
enum { DEF_SIZE = 50, DEF_ADD = 50 }; // number of array elements to add at a time
// constructors/destructor
CPolyLine( CDisplayList * dl );
// CPolyLine( CDisplayList * dl = NULL );
CPolyLine();
~CPolyLine();
......@@ -84,16 +82,16 @@ public:
void RemoveAllContours( void );
// drawing functions
void HighlightSide( int is );
/* void HighlightSide( int is );
void HighlightCorner( int ic );
void StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y);
void StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y);
void CancelDraggingToInsertCorner( int ic );
void CancelDraggingToMoveCorner( int ic );
void Undraw();
void Draw( CDisplayList * dl = NULL );
*/ void Undraw();
void Draw( );
void Hatch();
void MakeVisible( bool visible = TRUE );
// void MakeVisible( bool visible = TRUE );
void MoveOrigin( int x_off, int y_off );
// misc. functions
......@@ -152,7 +150,7 @@ public:
void ClipPhpPolygon( int php_op, CPolyLine * poly );
private:
CDisplayList * m_dlist; // display list
// CDisplayList * m_dlist; // display list
int m_layer; // layer to draw on
int m_Width; // line width
int m_sel_box; // corner selection box width/2
......@@ -161,9 +159,9 @@ public:
std::vector <CPolyPt> corner; // array of points for corners
std::vector <int> side_style; // array of styles for sides
private:
std::vector <dl_element*> dl_side; // graphic elements
std::vector <dl_element*> dl_side_sel;
std::vector <dl_element*> dl_corner_sel;
// std::vector <dl_element*> dl_side; // graphic elements
// std::vector <dl_element*> dl_side_sel;
// std::vector <dl_element*> dl_corner_sel;
public:
int m_HatchStyle; // hatch style, see enum above
std::vector <CSegment> m_HatchLines; // hatch lines
......
/* stuff for class CDisplayList */
#include "PolyLine.h"
void CDisplayList::Set_visible( dl_element * el, int visible )
{
}
int CDisplayList::StopDragging()
{
return 0;
}
int CDisplayList::CancelHighLight()
{
return 0;
}
id CDisplayList::Remove( dl_element * element )
{
return 0;
}
int CDisplayList::Get_w( dl_element * el )
{
return 0;
}
int CDisplayList::Get_x( dl_element * el )
{
return 0;
}
int CDisplayList::Get_y( dl_element * el )
{
return 0;
}
int CDisplayList::Get_xf( dl_element * el )
{
return 0;
}
int CDisplayList::Get_yf( dl_element * el )
{
return 0;
}
int CDisplayList::HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer )
{
return 0;
}
int CDisplayList::StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf,
int layer1, int layer2, int w1, int w2,
int style1, int style2,
int layer_no_via, int via_w, int via_holew, int dir,
int crosshair )
{
return 0;
}
int CDisplayList::StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi,
int layer, int w, int crosshair )
{
return 0;
}
......@@ -4,11 +4,6 @@
#ifndef FP_DISPLAY_LIST_H
#define FP_DISPLAY_LIST_H
//#define DL_MAX_LAYERS 32
#define DL_MAGIC 2674
#define PCBU_PER_WU 25400 // conversion from PCB units to world units
// graphics element types
enum
{
......@@ -31,25 +26,6 @@ enum
DL_X // X
};
// dragging line shapes
enum
{
DS_NONE = 0,
DS_LINE_VERTEX, // vertex between two lines
DS_LINE, // line
DS_ARC_STRAIGHT, // straight line (used when drawing polylines)
DS_ARC_CW, // clockwise arc (used when drawing polylines)
DS_ARC_CCW // counterclockwise arc (used when drawing polylines)
};
// styles of line segment when dragging line or line vertex
enum
{
DSS_STRAIGHT = 100, // straight line
DSS_ARC_CW, // clockwise arc
DSS_ARC_CCW // counterclockwise arc
};
// inflection modes for DS_LINE and DS_LINE_VERTEX
enum
{
......@@ -60,210 +36,4 @@ enum
};
class CDisplayList;
// this structure contains an element of the display list
class dl_element
{
friend class CDisplayList;
public:
CDisplayList * dlist;
int magic;
dl_element * prev;
dl_element * next;
id m_id; // identifier (see ids.h)
void * ptr; // pointer to object drawing this element
int gtype; // type of primitive
int visible; // 0 to hide
//private:
int sel_vert; // for selection rectangles, 1 if part is vertical
int w; // width (for round or square shapes)
int holew; // hole width (for round holes)
int x_org, y_org; // x origin (for rotation, reflection, etc.)
int x, y; // starting or center position of element
int xf, yf; // opposite corner (for rectangle or line)
int radius; // radius of corners for DL_RRECT
int layer; // layer to draw on
int orig_layer; // for elements on highlight layer,
// the original layer, the highlight will
// only be drawn if this layer is visible
};
class CDisplayList
{
private:
// display-list parameters for each layer
dl_element m_start[MAX_LAYERS], m_end[MAX_LAYERS];
int m_rgb[MAX_LAYERS][3]; // layer colors
BOOL m_vis[MAX_LAYERS]; // layer visibility flags
int m_layer_in_order[MAX_LAYERS]; // array of layers in draw order
int m_order_for_layer[MAX_LAYERS]; // draw order for each layer
// window parameters
int m_pcbu_per_wu; // i.e. nm per world unit
CRect m_client_r; // client rect (pixels)
CRect m_screen_r; // client rect (screen coords)
int m_pane_org_x; // left border of drawing pane (pixels)
int m_pane_org_y; // bottom border of drawing pane (pixels)
int m_bottom_pane_h; // height of bottom pane
CDC * memDC; // pointer to memory DC
double m_scale; // world units per pixel
int m_org_x; // world x-coord of left side of screen (world units)
int m_org_y; // world y-coord of bottom of screen (world units)
int m_max_x; // world x_coord of right side of screen (world units)
int m_max_y; // world y_coord of top of screen (world units)
int w_ext_x, w_ext_y; // window extents (world units)
int v_ext_x, v_ext_y; // viewport extents (pixels)
double m_wu_per_pixel_x; // ratio w_ext_x/v_ext_x (world units per pixel)
double m_wu_per_pixel_y; // ratio w_ext_y/v_ext_y (world units per pixel)
double m_pcbu_per_pixel_x;
double m_pcbu_per_pixel_y;
// general dragging parameters
int m_drag_angle; // angle of rotation of selection rectangle (starts at 0)
int m_drag_side; // 0 = no change, 1 = switch to opposite
int m_drag_vert; // 1 if item being dragged is a vertical part
// parameters for dragging polyline sides and trace segments
// that can be modified while dragging
int m_drag_flag; // 1 if dragging something
int m_drag_shape; // shape
int m_last_drag_shape; // last shape drawn
int m_drag_x; // last cursor position for dragged shape
int m_drag_y;
int m_drag_xi; // start of rubberband drag line
int m_drag_yi;
int m_drag_xf; // end of rubberband drag line
int m_drag_yf;
int m_drag_layer_1; // line layer
int m_drag_w1; // line width
int m_drag_style1; // line style
int m_inflection_mode; // inflection mode
int m_last_inflection_mode; // last mode drawn
// extra parameters when dragging vertex between 2 line segments
int m_drag_style2;
int m_drag_layer_2;
int m_drag_w2;
// parameters used to draw leading via if necessary
int m_drag_layer_no_via;
int m_drag_via_w;
int m_drag_via_holew;
int m_drag_via_drawn;
// arrays of lines and ratlines being dragged
// these can be rotated and flipped while being dragged
int m_drag_layer; // layer
int m_drag_max_lines; // max size of array for line segments
int m_drag_num_lines; // number of line segments to drag
CPoint * m_drag_line_pt; // array of relative coords for line endpoints
int m_drag_max_ratlines; // max size of ratline array
int m_drag_num_ratlines; // number of ratlines to drag
CPoint * m_drag_ratline_start_pt; // absolute coords for ratline start points
CPoint * m_drag_ratline_end_pt; // relative coords for ratline endpoints
int m_drag_ratline_width;
// cursor parameters
int m_cross_hairs; // 0 = none, 1 = cross-hairs, 2 = diagonals
CPoint m_cross_left, m_cross_right, m_cross_top, m_cross_bottom; // end-points
CPoint m_cross_topleft, m_cross_topright, m_cross_botleft, m_cross_botright;
// grid
int m_visual_grid_on;
double m_visual_grid_spacing; // in world units
public:
CDisplayList( int pcbu_per_wu );
~CDisplayList();
void SetVisibleGrid( BOOL on, double grid );
void SetMapping( CRect *client_r, CRect *screen_r, int pane_org_x, int pane_bottom_h, double scale, int org_x, int org_y );
void SetDCToWorldCoords( CDC * pDC, CDC * mDC, int pcbu_org_x, int pcbu_org_y );
void SetLayerRGB( int layer, int r, int g, int b );
void SetLayerVisible( int layer, BOOL vis );
void SetLayerDrawOrder( int layer, int order )
{ m_layer_in_order[order] = layer; m_order_for_layer[layer] = order; };
dl_element * Add( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo,
int radius=0, int orig_layer=LAY_SELECTION );
dl_element * AddSelector( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo, int radius=0 );
void RemoveAll();
void RemoveAllFromLayer( int layer );
id Remove( dl_element * element );
void Draw( CDC * pDC );
int HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer=LAY_SELECTION );
int CancelHighLight();
void * TestSelect( int x, int y, id * sel_id, int * layer,
id * exclude_id = NULL, void * exclude_ptr = NULL, id * include_id = NULL,
int n_include_ids=1 );
int StartDraggingArray( CDC * pDC, int x, int y, int vert, int layer, int crosshair = 1 );
int StartDraggingRatLine( CDC * pDC, int x, int y, int xf, int yf, int layer,
int w, int crosshair = 1 );
int StartDraggingRectangle( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf, int vert, int layer );
int StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf,
int layer1, int layer2, int w1, int w2,
int style1, int style2,
int layer_no_via, int via_w, int via_holew, int dir,
int crosshair );
int StartDraggingLine( CDC * pDC, int x, int y, int xi, int yi, int layer1, int w,
int layer_no_via, int via_w, int via_holew,
int crosshair, int style, int inflection_mode );
int StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi,
int layer, int w, int crosshair );
void SetDragArcStyle( int style );
void Drag( CDC * pDC, int x, int y );
int StopDragging();
void ChangeRoutingLayer( CDC * pDC, int layer1, int layer2, int w );
void IncrementDragAngle( CDC * pDC );
int MakeDragLineArray( int num_lines );
int MakeDragRatlineArray( int num_ratlines, int width );
int AddDragLine( CPoint pi, CPoint pf );
int AddDragRatline( CPoint pi, CPoint pf );
int GetDragAngle();
void FlipDragSide( CDC * pDC );
int GetDragSide();
void SetUpCrosshairs( int type, int x, int y );
void SetInflectionMode( int mode ){ m_inflection_mode = mode; };
CPoint ScreenToPCB( CPoint point );
CPoint PCBToScreen( CPoint point );
CPoint WindowToPCB( CPoint point );
// set element parameters
void Set_gtype( dl_element * el, int gtype );
void Set_visible( dl_element * el, int visible );
void Set_sel_vert( dl_element * el, int sel_vert );
void Set_w( dl_element * el, int w );
void Set_holew( dl_element * el, int holew );
void Set_x_org( dl_element * el, int x_org );
void Set_y_org( dl_element * el, int y_org );
void Set_x( dl_element * el, int x );
void Set_y( dl_element * el, int y );
void Set_xf( dl_element * el, int xf );
void Set_yf( dl_element * el, int yf );
void Set_id( dl_element * el, id * id );
void Set_layer( dl_element * el, int layer );
void Set_radius( dl_element * el, int radius );
void Move( dl_element * el, int dx, int dy );
// get element parameters
void * Get_ptr( dl_element * el );
int Get_gtype( dl_element * el );
int Get_visible( dl_element * el );
int Get_sel_vert( dl_element * el );
int Get_w( dl_element * el );
int Get_holew( dl_element * el );
int Get_x_org( dl_element * el );
int Get_y_org( dl_element * el );
int Get_x( dl_element * el );
int Get_y( dl_element * el );
int Get_xf( dl_element * el );
int Get_yf( dl_element * el );
int Get_radius( dl_element * el );
int Get_layer( dl_element * el );
id Get_id( dl_element * el );
};
#endif // #ifndef FP_DISPLAY_LIST_H
......@@ -9,8 +9,7 @@ OBJECTS= \
php_polygon.o\
php_polygon_vertex.o\
PolyLine.o\
math_for_graphics.o\
cdisplaylist_stuff.o
math_for_graphics.o
GenericPolygonClipperLibrary.o: GenericPolygonClipperLibrary.cpp GenericPolygonClipperLibrary.h
......
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