← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Footprint Wizards Update

 

Le 03/01/2017 à 14:03, Miguel Angel Ajo Pelayo a écrit :
> Sorry, I just found the email. Sorry for my delayed answer.
> 
> The code looks good to me, I believe it's a good change, it's not backwards compatible with the
> older footprint wizards, but the changes to make them compatible seem simple enough, and well
> documented.
> 
> 
> If I had comments to make, it'd be why are we using uInteger, uEtc for parameter types.
> 
> Could we use the python convention to have constants in upcase, and may be se
> 
> INTEGER_PARAM, .... ETC_PARAM on the python side ?
> 
> this is more nitpicking than anything. 
> 
> 
> Nice work.

I agree, and I am willing to commit this change (the API is better), but because it breaks the
compatibility (yes, the changes to make old wizards compatible is simple and documented) I need the
Wayne's opinion before committing the changes.

Attached a patch against the lastest kicad version (the initial patch is not applicable to the
current kicad version).

> 
> 
> On Mon, Nov 14, 2016 at 8:33 PM, jp charras <jp.charras@xxxxxxxxxx <mailto:jp.charras@xxxxxxxxxx>>
> wrote:
> 
>     Le 10/10/2016 à 02:30, Oliver Walters a écrit :
>     > Anyone have any comments on this? I have fixed many issues that were raised last time. If I can get
>     > some feedback, we can progress towards merge.
>     >
>     > On Thu, Sep 29, 2016 at 6:18 PM, Oliver Walters <oliver.henry.walters@xxxxxxxxx <mailto:oliver.henry.walters@xxxxxxxxx>
>     > <mailto:oliver.henry.walters@xxxxxxxxx <mailto:oliver.henry.walters@xxxxxxxxx>>> wrote:
>     >
>     >     Hi All,
>     >
>     >     I have improved upon the footprint wizards patch, taking suggestions into account.
>     >
>     >     patch file attached.
>     >
>     >     There is an in-depth discussion of the new
>     >     format: https://github.com/KiCad/Footprint_Wizards/wiki <https://github.com/KiCad/Footprint_Wizards/wiki>
>     >     <https://github.com/KiCad/Footprint_Wizards/wiki <https://github.com/KiCad/Footprint_Wizards/wiki>>
>     >
>     >     In particular, instructions on how to update old wizards to the new format:
>     >
>     >     https://github.com/KiCad/Footprint_Wizards/wiki/Updating-Older-Wizards
>     <https://github.com/KiCad/Footprint_Wizards/wiki/Updating-Older-Wizards>
>     >     <https://github.com/KiCad/Footprint_Wizards/wiki/Updating-Older-Wizards
>     <https://github.com/KiCad/Footprint_Wizards/wiki/Updating-Older-Wizards>>
>     >
>     >     Cheers,
>     >
>     >     Oliver
> 
>     Hi Oliver,
> 
>     Sorry for the delay.
>     The patch works fine for me.
>     Be patient.
> 
>     The only drawback for me is the fact it breaks the compatibility with old wizards (not blocking).
>     Just a decision to take.
> 
> 
>     --
>     Jean-Pierre CHARRAS
> 
>     _______________________________________________
>     Mailing list: https://launchpad.net/~kicad-developers <https://launchpad.net/%7Ekicad-developers>
>     Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
>     Unsubscribe : https://launchpad.net/~kicad-developers <https://launchpad.net/%7Ekicad-developers>
>     More help   : https://help.launchpad.net/ListHelp <https://help.launchpad.net/ListHelp>
> 
> 


-- 
Jean-Pierre CHARRAS
 common/dialog_about/AboutDialog_main.cpp           |   6 +
 pcbnew/class_footprint_wizard.h                    |  31 +
 pcbnew/footprint_wizard.cpp                        |  47 +-
 pcbnew/footprint_wizard_frame.cpp                  | 168 +++--
 pcbnew/footprint_wizard_frame.h                    |  23 +-
 pcbnew/pcbnew.cpp                                  |  15 +-
 pcbnew/pcbnew_id.h                                 |   1 +
 .../plugins/FPC_(SMD_type)_footprintwizard.py      | 159 -----
 pcbnew/python/plugins/FPC_wizard.py                | 157 +++++
 pcbnew/python/plugins/FootprintWizardBase.py       | 674 +++++++++++++++++++++
 .../python/plugins/FootprintWizardDrawingAids.py   | 532 ----------------
 .../python/plugins/HelpfulFootprintWizardPlugin.py | 348 -----------
 pcbnew/python/plugins/bga_wizard.py                | 114 +++-
 pcbnew/python/plugins/circular_pad_array_wizard.py |  82 ++-
 pcbnew/python/plugins/qfn_wizard.py                | 218 ++++---
 pcbnew/python/plugins/qfp_wizard.py                | 103 ++--
 pcbnew/python/plugins/sdip_wizard.py               |  36 +-
 pcbnew/python/plugins/touch_slider_wizard.py       |  55 +-
 pcbnew/python/plugins/uss39_barcode.py             |  11 +-
 pcbnew/python/plugins/zip_wizard.py                | 115 ++--
 pcbnew/swig/pcbnew_footprint_wizards.cpp           |  49 +-
 pcbnew/swig/pcbnew_footprint_wizards.h             |   5 +
 scripting/kicadplugins.i                           | 349 +++++++++--
 23 files changed, 1785 insertions(+), 1513 deletions(-)

diff --git a/common/dialog_about/AboutDialog_main.cpp b/common/dialog_about/AboutDialog_main.cpp
index 2fd9e39..32d6790 100644
--- a/common/dialog_about/AboutDialog_main.cpp
+++ b/common/dialog_about/AboutDialog_main.cpp
@@ -151,11 +151,17 @@ static void InitKiCadAboutNew( AboutAppInfo& info )
                 << HtmlHyperlink( wxT( "https://launchpad.net/kicad"; ),
                                   _( "Developer's website on Launchpad" ) )
                 << wxT("</li>" );
+
     description << wxT( "<li>" )
                 << HtmlHyperlink( wxT( "https://github.com/KiCad/"; ),
                                   _( "Our official Repository for component and footprint libraries" ) )
                 << wxT( "</li>" );
 
+    description << wxT( "<li>" )
+                << HtmlHyperlink( wxT( "https://github.com/KiCad/Footprint_Wizards"; ),
+                                  _( "Footprint wizards info on our official repository " ) )
+                << wxT( "</li>" );
+
     description << wxT( "<p><u>" )
                 << _( "Non official repositories" )
                 << wxT( "</u>" );
diff --git a/pcbnew/class_footprint_wizard.h b/pcbnew/class_footprint_wizard.h
index 20fc1d4..2ffc658 100644
--- a/pcbnew/class_footprint_wizard.h
+++ b/pcbnew/class_footprint_wizard.h
@@ -33,6 +33,17 @@
 #include <vector>
 #include <wxPcbStruct.h>
 
+// Allowable parameter types for PCB wizards
+const wxString WIZARD_PARAM_UNITS_MM        = "mm";          // Millimetres
+const wxString WIZARD_PARAM_UNITS_MILS      = "mils";        // Mils / thou
+const wxString WIZARD_PARAM_UNITS_FLOAT     = "float";       // Floating point (dimensionless)
+const wxString WIZARD_PARAM_UNITS_INTEGER   = "integer";     // Integer (dimensionless)
+const wxString WIZARD_PARAM_UNITS_BOOL      = "bool";        // Boolean option
+const wxString WIZARD_PARAM_UNITS_RADIANS   = "radians";     // Angle (radians)
+const wxString WIZARD_PARAM_UNITS_DEGREES   = "degrees";     // Angle (degrees)
+const wxString WIZARD_PARAM_UNITS_PERCENT   = "%";           // Percent (0% -> 100%)
+const wxString WIZARD_PARAM_UNITS_STRING    = "string";      // String
+
 /**
  * Class FOOTPRINT_WIZARD
  * This is the parent class from where any footprint wizard class must
@@ -105,6 +116,20 @@ public:
     virtual wxArrayString   GetParameterErrors( int aPage ) = 0;
 
     /**
+     * Function GetParameterHints
+     * @param aPage is the page we want to know the hints of
+     * @return an array of hints (if any) for the parameters, empty string for no hints
+     */
+    virtual wxArrayString   GetParameterHints( int aPage ) = 0;
+
+    /**
+     * Function GetParamaterDesignators
+     * @param aPage is the page we want to know the designators of
+     * @return an array of designators (blank strings for no designators
+     */
+    virtual wxArrayString    GetParameterDesignators( int aPage ) = 0;
+
+    /**
      * Function SetParameterValues
      * @param aPage is the page we want to set the parameters in
      * @param aValues are the values we want to set into the parameters
@@ -113,6 +138,12 @@ public:
     virtual wxString        SetParameterValues( int aPage, wxArrayString& aValues ) = 0;
 
     /**
+     * Function ResetParameters
+     * Reset all wizard parameters to default values
+     */
+    virtual void ResetParameters() = 0;
+
+    /**
      * Function GetModule
      * This method builds the module itself and returns it to the caller function
      * @return  PCB module built from the parameters given to the class
diff --git a/pcbnew/footprint_wizard.cpp b/pcbnew/footprint_wizard.cpp
index 6e9b17e..02b379c 100644
--- a/pcbnew/footprint_wizard.cpp
+++ b/pcbnew/footprint_wizard.cpp
@@ -203,6 +203,22 @@ void FOOTPRINT_WIZARD_FRAME::SelectCurrentWizard( wxCommandEvent& event )
     SelectFootprintWizard();
 }
 
+void FOOTPRINT_WIZARD_FRAME::DefaultParameters( wxCommandEvent& event )
+{
+    FOOTPRINT_WIZARD* footprintWizard = GetMyWizard();
+
+    if ( footprintWizard == NULL )
+        return;
+
+    footprintWizard->ResetParameters();
+
+    // Reload
+    ReCreateParameterList();
+    ReloadFootprint();
+    DisplayWizardInfos();
+
+}
+
 
 void FOOTPRINT_WIZARD_FRAME::ParametersUpdated( wxGridEvent& event )
 {
@@ -223,39 +239,12 @@ void FOOTPRINT_WIZARD_FRAME::ParametersUpdated( wxGridEvent& event )
     int             count = m_parameterGrid->GetNumberRows();
 
     // Skip extra event, useless
-    if( event.GetString() == m_parameterGrid->GetCellValue( event.GetRow(), m_columnPrmValue ) )
+    if( event.GetString() == m_parameterGrid->GetCellValue( event.GetRow(), WIZ_COL_VALUE ) )
         return;
 
     for( int prm_id = 0; prm_id < count; ++prm_id )
     {
-        wxString value = m_parameterGrid->GetCellValue( prm_id, m_columnPrmValue );
-
-        // if this parameter is expected to be an internal
-        // unit convert it back from the user format
-        if( ptList[prm_id]==wxT( "IU" ) )
-        {
-            // If our locale is set to use, for decimal point, just change it
-            // to be scripting compatible
-            LOCALE_IO   toggle;
-            double      dValue;
-
-            value.ToDouble( &dValue );
-
-            // convert from mils to inches where it's needed
-            if( g_UserUnit==INCHES )
-                dValue = dValue / 1000.0;
-
-            dValue = From_User_Unit( g_UserUnit, dValue );
-
-            // Internal units are int. Print them as int.
-            value.Printf( "%d", KiROUND( dValue ) );
-
-            if(  prmValues[prm_id].EndsWith(".0") )
-            {
-                 prmValues[prm_id].RemoveLast();
-                 prmValues[prm_id].RemoveLast();
-            }
-        }
+        wxString value = m_parameterGrid->GetCellValue( prm_id, WIZ_COL_VALUE);
 
         if( prmValues[prm_id] != value )
         {
diff --git a/pcbnew/footprint_wizard_frame.cpp b/pcbnew/footprint_wizard_frame.cpp
index 6112fa6..bb2e99b 100644
--- a/pcbnew/footprint_wizard_frame.cpp
+++ b/pcbnew/footprint_wizard_frame.cpp
@@ -44,6 +44,8 @@
 #include "footprint_wizard_frame.h"
 #include <footprint_info.h>
 #include <wx/grid.h>
+#include <wx/tokenzr.h>
+#include <wx/numformatter.h>
 
 #include <hotkeys.h>
 #include <wildcards_and_files_ext.h>
@@ -61,6 +63,9 @@ BEGIN_EVENT_TABLE( FOOTPRINT_WIZARD_FRAME, EDA_DRAW_FRAME )
     EVT_TOOL( ID_FOOTPRINT_WIZARD_SELECT_WIZARD,
               FOOTPRINT_WIZARD_FRAME::SelectCurrentWizard )
 
+    EVT_TOOL( ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT,
+              FOOTPRINT_WIZARD_FRAME::DefaultParameters )
+
     EVT_TOOL( ID_FOOTPRINT_WIZARD_NEXT,
               FOOTPRINT_WIZARD_FRAME::Process_Special_Functions )
 
@@ -74,6 +79,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_WIZARD_FRAME, EDA_DRAW_FRAME )
               FOOTPRINT_WIZARD_FRAME::Show3D_Frame )
 
     // listbox events
+
     EVT_LISTBOX( ID_FOOTPRINT_WIZARD_PAGE_LIST, FOOTPRINT_WIZARD_FRAME::ClickOnPageList )
     EVT_GRID_CMD_CELL_CHANGED( ID_FOOTPRINT_WIZARD_PARAMETER_LIST,
                                FOOTPRINT_WIZARD_FRAME::ParametersUpdated )
@@ -81,10 +87,6 @@ BEGIN_EVENT_TABLE( FOOTPRINT_WIZARD_FRAME, EDA_DRAW_FRAME )
     EVT_MENU( ID_SET_RELATIVE_OFFSET, FOOTPRINT_WIZARD_FRAME::OnSetRelativeOffset )
 END_EVENT_TABLE()
 
-// Column index to display parameters in m_parameterGrid
-int FOOTPRINT_WIZARD_FRAME::m_columnPrmName = 0;
-int FOOTPRINT_WIZARD_FRAME::m_columnPrmValue = 1;
-int FOOTPRINT_WIZARD_FRAME::m_columnPrmUnit = 2;
 
 #define FOOTPRINT_WIZARD_FRAME_NAME wxT( "FootprintWizard" )
 
@@ -255,6 +257,13 @@ void FOOTPRINT_WIZARD_FRAME::ExportSelectedFootprint( wxCommandEvent& aEvent )
     Close();
 }
 
+void FOOTPRINT_WIZARD_FRAME::OnGridSize( wxSizeEvent& aSizeEvent )
+{
+    // Resize the parameter columns
+    ResizeParamColumns();
+
+    aSizeEvent.Skip();
+}
 
 void FOOTPRINT_WIZARD_FRAME::OnSize( wxSizeEvent& SizeEv )
 {
@@ -278,9 +287,10 @@ void  FOOTPRINT_WIZARD_FRAME::initParameterGrid()
     m_parameterGrid->CreateGrid( 0, 3 );
 
     // Columns
-    m_parameterGrid->SetColLabelValue( m_columnPrmName, _( "Parameter" ) );
-    m_parameterGrid->SetColLabelValue( m_columnPrmValue, _( "Value" ) );
-    m_parameterGrid->SetColLabelValue( m_columnPrmUnit, _( "Units" ) );
+    m_parameterGrid->SetColLabelValue( WIZ_COL_NAME, _( "Parameter" ) );
+    m_parameterGrid->SetColLabelValue( WIZ_COL_VALUE, _( "Value" ) );
+    m_parameterGrid->SetColLabelValue( WIZ_COL_UNITS, _( "Units" ) );
+
     m_parameterGrid->SetColLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE );
     m_parameterGrid->AutoSizeColumns();
 
@@ -288,6 +298,11 @@ void  FOOTPRINT_WIZARD_FRAME::initParameterGrid()
     m_parameterGrid->AutoSizeRows();
     m_parameterGrid->SetRowLabelSize( 25 );
     m_parameterGrid->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
+
+    m_parameterGrid->DisableDragGridSize();
+    m_parameterGrid->DisableDragColSize();
+
+    m_parameterGrid->Connect( wxEVT_SIZE, wxSizeEventHandler(FOOTPRINT_WIZARD_FRAME::OnGridSize), NULL, this );
 }
 
 
@@ -336,63 +351,117 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateParameterList()
 
     m_parameterGrid->ClearGrid();
 
-    // Get the list of names, values, and types
-    wxArrayString   fpList  = footprintWizard->GetParameterNames( page );
-    wxArrayString   fvList  = footprintWizard->GetParameterValues( page );
-    wxArrayString   ptList  = footprintWizard->GetParameterTypes( page );
+    // Get the list of names, values, types, hints and designators
+    wxArrayString designatorsList    = footprintWizard->GetParameterDesignators( page );
+    wxArrayString namesList          = footprintWizard->GetParameterNames( page );
+    wxArrayString valuesList         = footprintWizard->GetParameterValues( page );
+    wxArrayString typesList          = footprintWizard->GetParameterTypes( page );
+    wxArrayString hintsList          = footprintWizard->GetParameterHints( page );
 
     // Dimension the wxGrid
     if( m_parameterGrid->GetNumberRows() > 0 )
         m_parameterGrid->DeleteRows( 0, m_parameterGrid->GetNumberRows() );
 
-    m_parameterGrid->AppendRows( fpList.size() );
+    m_parameterGrid->AppendRows( namesList.size() );
 
-    wxString value, units;
-    for( unsigned int i = 0; i< fpList.size(); i++ )
+    wxString designator, name, value, units, hint;
+
+    for( unsigned int i = 0; i< namesList.size(); i++ )
     {
-        value   = fvList[i];
+        designator  = designatorsList[i];
+        name        = namesList[i];
+        value       = valuesList[i];
+        units       = typesList[i];
+        hint        = hintsList[i];
 
-        m_parameterGrid->SetCellValue( i, m_columnPrmName, fpList[i] );
-        m_parameterGrid->SetReadOnly( i, m_columnPrmName );
+        m_parameterGrid->SetRowLabelValue( i, designator );
 
-        if( ptList[i]==wxT( "IU" ) )
-        {
-            LOCALE_IO toggle;
+        // Set the 'Name'
+        m_parameterGrid->SetCellValue( i, WIZ_COL_NAME, name );
+        m_parameterGrid->SetReadOnly( i, WIZ_COL_NAME );
+        m_parameterGrid->SetCellAlignment( i, WIZ_COL_NAME, wxALIGN_LEFT, wxALIGN_CENTRE );
 
-            // We are handling internal units, so convert them to the current
-            // system selected units and store into value.
-            double dValue;
+        // Set the editor type of the
 
-            value.ToDouble( &dValue );
+        // Boolean parameters can be displayed using a checkbox
+        if ( units == WIZARD_PARAM_UNITS_BOOL )
+        {
+            wxGridCellBoolEditor *boolEditor = new wxGridCellBoolEditor;
+            boolEditor->UseStringValues("True","False");
+            m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, boolEditor );
 
-            dValue = To_User_Unit( g_UserUnit, dValue );
+            m_parameterGrid->SetCellRenderer( i, WIZ_COL_VALUE, new wxGridCellBoolRenderer );
+        }
+        // Parameters that can be selected from a list of multiple options
+        else if ( units.Contains( "," ) )  // Indicates list of available options
+        {
+            wxStringTokenizer tokenizer( units, "," );
+            wxArrayString options;
 
-            if( g_UserUnit==INCHES )    // we convert inches into mils for more detail
-            {
-                dValue  = dValue * 1000.0;
-                units   = wxT( "mils" );
-            }
-            else if( g_UserUnit==MILLIMETRES )
+            while ( tokenizer.HasMoreTokens() )
             {
-                units = wxT( "mm" );
+                options.Add( tokenizer.GetNextToken() );
             }
 
-            // Use Double2Str to build the string, because useless trailing 0
-            // are removed. The %f format does not remove them
-            std::string s = Double2Str( dValue );
-            value = FROM_UTF8( s.c_str() );
+            m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellChoiceEditor( options ) );
+
+            units = wxT( "" );
         }
-        else if( ptList[i]==wxT( "UNITS" ) )    // 1,2,3,4,5 ... N
+        // Integer parameters
+        else if ( units == WIZARD_PARAM_UNITS_INTEGER )
         {
-            units = wxT( "" );
+            m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellNumberEditor );
+        }
+        // Non-integer numerical parameters
+        else if ( ( units == WIZARD_PARAM_UNITS_MM )      ||
+                  ( units == WIZARD_PARAM_UNITS_MILS )    ||
+                  ( units == WIZARD_PARAM_UNITS_FLOAT )   ||
+                  ( units == WIZARD_PARAM_UNITS_RADIANS ) ||
+                  ( units == WIZARD_PARAM_UNITS_DEGREES ) ||
+                  ( units == WIZARD_PARAM_UNITS_PERCENT ) )
+        {
+            m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellFloatEditor );
+
+            // Convert separators to the locale-specific character
+            value.Replace( ",", wxNumberFormatter::GetDecimalSeparator() );
+            value.Replace( ".", wxNumberFormatter::GetDecimalSeparator() );
         }
 
-        m_parameterGrid->SetCellValue( i, m_columnPrmValue, value );
-        m_parameterGrid->SetCellValue( i, m_columnPrmUnit, units );
-        m_parameterGrid->SetReadOnly( i, m_columnPrmUnit );
+
+        // Set the 'Units'
+        m_parameterGrid->SetCellValue( i, WIZ_COL_UNITS, units );
+        m_parameterGrid->SetReadOnly( i, WIZ_COL_UNITS );
+        m_parameterGrid->SetCellAlignment( i, WIZ_COL_UNITS, wxALIGN_LEFT, wxALIGN_CENTRE );
+
+        // Set the 'Value'
+        m_parameterGrid->SetCellValue( i, WIZ_COL_VALUE, value );
+        m_parameterGrid->SetCellAlignment( i, WIZ_COL_VALUE, wxALIGN_CENTRE, wxALIGN_CENTRE );
     }
 
+    ResizeParamColumns();
+
+}
+
+void FOOTPRINT_WIZARD_FRAME::ResizeParamColumns()
+{
+
+    // Parameter grid is not yet configured
+    if ( ( m_parameterGrid == NULL ) || ( m_parameterGrid->GetNumberCols() == 0 ) )
+        return;
+
+    // first auto-size the columns to ensure enough space around text
     m_parameterGrid->AutoSizeColumns();
+
+    // Auto-size the value column
+    int width = m_parameterGrid->GetClientSize().GetWidth() -
+                m_parameterGrid->GetRowLabelSize() -
+                m_parameterGrid->GetColSize( WIZ_COL_NAME ) -
+                m_parameterGrid->GetColSize( WIZ_COL_UNITS );
+
+    if ( width > m_parameterGrid->GetColMinimalAcceptableWidth() )
+    {
+        m_parameterGrid->SetColSize( WIZ_COL_VALUE, width );
+    }
 }
 
 
@@ -593,6 +662,13 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateHToolbar()
                                 _( "Select the wizard script to load and run" ) );
 
         m_mainToolBar->AddSeparator();
+
+        m_mainToolBar->AddTool( ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT, wxEmptyString,
+                                KiBitmap( reload_xpm ),
+                                _( "Reset the wizard parameters to default values ") );
+
+        m_mainToolBar->AddSeparator();
+
         m_mainToolBar->AddTool( ID_FOOTPRINT_WIZARD_PREVIOUS, wxEmptyString,
                                 KiBitmap( lib_previous_xpm ),
                                 _( "Select previous parameters page" ) );
@@ -655,13 +731,13 @@ FOOTPRINT_WIZARD_MESSAGES::FOOTPRINT_WIZARD_MESSAGES( FOOTPRINT_WIZARD_FRAME* aP
                      wxCAPTION | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT )
 {
     m_canClose = false;
-	wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
-	SetSizer( bSizer );
+    wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
+    SetSizer( bSizer );
 
-	m_messageWindow = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
+    m_messageWindow = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
                                       wxDefaultPosition, wxDefaultSize,
                                       wxTE_MULTILINE|wxTE_READONLY );
-	bSizer->Add( m_messageWindow, 1, wxEXPAND, 0 );
+    bSizer->Add( m_messageWindow, 1, wxEXPAND, 0 );
 
     m_config = aCfg;
 
@@ -670,7 +746,7 @@ FOOTPRINT_WIZARD_MESSAGES::FOOTPRINT_WIZARD_MESSAGES( FOOTPRINT_WIZARD_FRAME* aP
     SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
 
     m_messageWindow->SetMinSize( wxSize( 350, 250 ) );
-	Layout();
+    Layout();
 
     bSizer->SetSizeHints( this );
 }
diff --git a/pcbnew/footprint_wizard_frame.h b/pcbnew/footprint_wizard_frame.h
index 97fabe1..302d746 100644
--- a/pcbnew/footprint_wizard_frame.h
+++ b/pcbnew/footprint_wizard_frame.h
@@ -39,9 +39,16 @@ class wxGrid;
 class wxGridEvent;
 class FOOTPRINT_EDIT_FRAME;
 
-// A helper class to display messages when building a footprin
+// A helper class to display messages when building a footprint
 class FOOTPRINT_WIZARD_MESSAGES;
 
+enum WizardParameterColumnNames
+{
+    WIZ_COL_NAME = 0,
+    WIZ_COL_VALUE,
+    WIZ_COL_UNITS
+};
+
 /**
  * Class FOOTPRINT_WIZARD_FRAME
  */
@@ -54,11 +61,6 @@ private:
     int             m_parameterGridWidth;   ///< size of the grid
     FOOTPRINT_WIZARD_MESSAGES* m_messagesFrame;
 
-    // Column index to display parameters in m_parameterGrid
-    static int      m_columnPrmName;
-    static int      m_columnPrmValue;
-    static int      m_columnPrmUnit;
-
 protected:
     wxString        m_wizardName;           ///< name of the current wizard
     wxString        m_wizardDescription;    ///< description of the wizard
@@ -76,6 +78,8 @@ private:
 
     void                OnSize( wxSizeEvent& event ) override;
 
+    void                OnGridSize( wxSizeEvent& aSizeEvent );
+
     /**
      * Function ExportSelectedFootprint();
      * will let the caller exit from the wait loop, and get the built footprint
@@ -104,6 +108,11 @@ private:
     void                ReCreateParameterList();
 
     /**
+     * Expand the 'Value' column to fill available
+     */
+    void                ResizeParamColumns();
+
+    /**
      * Function initParameterGrid
      * Prepare the grid where parameters are displayed
      */
@@ -168,6 +177,8 @@ private:
 
     void                SelectCurrentWizard( wxCommandEvent& event );
 
+    void                DefaultParameters( wxCommandEvent& event );
+
     /**
      * Function ParametersUpdated
      * Update the footprint python parameters values from the values in grid
diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp
index b760c5b..2767f1d 100644
--- a/pcbnew/pcbnew.cpp
+++ b/pcbnew/pcbnew.cpp
@@ -200,7 +200,6 @@ PGM_BASE& Pgm()
 #if defined( KICAD_SCRIPTING )
 static bool scriptingSetup()
 {
-    wxString path_frag;
 
 #if defined( __WINDOWS__ )
     // If our python.exe (in kicad/bin) exists, force our kicad python environment
@@ -227,14 +226,6 @@ static bool scriptingSetup()
         wxSetEnv( wxT( "PATH" ), kipython );
     }
 
-    // wizard plugins are stored in ../share/kicad/scripting/plugins.
-    // so add the base scripting path to python scripting default search paths
-    // which are ( [KICAD_PATH] is an environment variable to define)
-    // [KICAD_PATH]/scripting
-    // [KICAD_PATH]/scripting/plugins
-    // Add this default search path:
-    path_frag = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
-
 #elif defined( __WXMAC__ )
 
     // This path is given to LoadPlugins() from kicadplugins.i, which
@@ -278,13 +269,9 @@ static bool scriptingSetup()
 
     wxSetEnv( wxT( "PYTHONPATH" ), pypath );
 
-    // Add this default search path:
-    path_frag = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
 #endif
 
-    // path_frag is the path to the bundled scripts and plugins, all other paths are
-    // determined by the python pcbnew.py initialisation code.
-    if( !pcbnewInitPythonScripting( TO_UTF8( path_frag ) ) )
+    if ( !pcbnewInitPythonScripting( TO_UTF8( PyScriptingPath() ) ) )
     {
         wxLogError( "pcbnewInitPythonScripting() failed." );
         return false;
diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h
index 801a4e0..19614c0 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -392,6 +392,7 @@ enum pcbnew_ids
     ID_FOOTPRINT_WIZARD_PAGES_WINDOW,
     ID_FOOTPRINT_WIZARD_PARAMETERS_WINDOW,
     ID_FOOTPRINT_WIZARD_SELECT_WIZARD,
+    ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT,
     ID_FOOTPRINT_WIZARD_EXPORT_TO_BOARD,
 
     ID_UPDATE_PCB_FROM_SCH,
diff --git a/pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py b/pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py
deleted file mode 100644
index bf77e93..0000000
--- a/pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py
+++ /dev/null
@@ -1,159 +0,0 @@
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-#  MA 02110-1301, USA.
-#
-
-from __future__ import division
-import pcbnew
-
-import HelpfulFootprintWizardPlugin as HFPW
-
-
-class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
-
-    def GetName(self):
-        return "FPC (SMT connector)"
-
-    def GetDescription(self):
-        return "FPC (SMT connector) Footprint Wizard"
-
-    def GetValue(self):
-        pins = self.parameters["Pads"]["*n"]
-        return "FPC_%d" % pins
-
-    def GenerateParameterList(self):
-        self.AddParam( "Pads", "n", self.uNatural, 40 )
-        self.AddParam( "Pads", "pitch", self.uMM, 0.5 )
-        self.AddParam( "Pads", "width", self.uMM, 0.25 )
-        self.AddParam( "Pads", "height", self.uMM, 1.6)
-        self.AddParam( "Shield", "shield_to_pad", self.uMM, 1.6 )
-        self.AddParam( "Shield", "from_top", self.uMM, 1.3 )
-        self.AddParam( "Shield", "width", self.uMM, 1.5 )
-        self.AddParam( "Shield", "height", self.uMM, 2 )
-
-
-    # build a rectangular pad
-    def smdRectPad(self,module,size,pos,name):
-        pad = pcbnew.D_PAD(module)
-        pad.SetSize(size)
-        pad.SetShape(pcbnew.PAD_SHAPE_RECT)
-        pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
-        pad.SetLayerSet( pad.SMDMask() )
-        pad.SetPos0(pos)
-        pad.SetPosition(pos)
-        pad.SetPadName(name)
-        return pad
-
-    def CheckParameters(self):
-        p = self.parameters
-        self.CheckParamInt( "Pads", "*n" )  # not internal units preceded by "*"
-
-
-    def BuildThisFootprint(self):
-        p = self.parameters
-        pad_count       = int(p["Pads"]["*n"])
-        pad_width       = p["Pads"]["width"]
-        pad_height      = p["Pads"]["height"]
-        pad_pitch       = p["Pads"]["pitch"]
-        shl_width       = p["Shield"]["width"]
-        shl_height      = p["Shield"]["height"]
-        shl_to_pad      = p["Shield"]["shield_to_pad"]
-        shl_from_top    = p["Shield"]["from_top"]
-
-        offsetX         = pad_pitch * ( pad_count-1 ) / 2
-        size_pad = pcbnew.wxSize( pad_width, pad_height )
-        size_shld = pcbnew.wxSize(shl_width, shl_height)
-        size_text = self.GetTextSize()  # IPC nominal
-
-        # Gives a position and size to ref and value texts:
-        textposy = pad_height/2 + pcbnew.FromMM(1) + self.GetTextThickness()
-        self.draw.Reference( 0, textposy, size_text )
-
-        textposy = textposy + size_text + self.GetTextThickness()
-        self.draw.Value( 0, textposy, size_text )
-
-        # create a pad array and add it to the module
-        for n in range ( 0, pad_count ):
-            xpos = pad_pitch*n - offsetX
-            pad = self.smdRectPad(self.module,size_pad, pcbnew.wxPoint(xpos,0),str(n+1))
-            self.module.Add(pad)
-
-
-        # Mechanical shield pads: left pad and right pad
-        xpos = -shl_to_pad-offsetX
-        pad_s0_pos = pcbnew.wxPoint(xpos,shl_from_top)
-        pad_s0 = self.smdRectPad(self.module, size_shld, pad_s0_pos, "0")
-        xpos = (pad_count-1) * pad_pitch+shl_to_pad - offsetX
-        pad_s1_pos = pcbnew.wxPoint(xpos,shl_from_top)
-        pad_s1 = self.smdRectPad(self.module, size_shld, pad_s1_pos, "0")
-
-        self.module.Add(pad_s0)
-        self.module.Add(pad_s1)
-
-        # add footprint outline
-        linewidth = self.draw.GetLineThickness()
-        margin = linewidth
-
-        # upper line
-        posy = -pad_height/2 - linewidth/2 - margin
-        xstart = - pad_pitch*0.5-offsetX
-        xend = pad_pitch * pad_count + xstart;
-        self.draw.Line( xstart, posy, xend, posy )
-
-        # lower line
-        posy = pad_height/2 + linewidth/2 + margin
-        self.draw.Line(xstart, posy, xend, posy)
-
-        # around left mechanical pad (the outline around right pad is mirrored/y axix)
-        yend = pad_s0_pos.y + shl_height/2 + margin
-        self.draw.Line(xstart, posy, xstart, yend)
-        self.draw.Line(-xstart, posy, -xstart, yend)
-
-        posy = yend
-        xend = pad_s0_pos.x - (shl_width/2 + linewidth + margin*2)
-        self.draw.Line(xstart, posy, xend, posy)
-
-        # right pad side
-        self.draw.Line(-xstart, posy, -xend, yend)
-
-        # set SMD attribute
-        self.module.SetAttributes(pcbnew.MOD_CMS)
-        
-        # vertical segment at left of the pad
-        xstart = xend
-        yend = posy - (shl_height + linewidth + margin*2)
-        self.draw.Line(xstart, posy, xend, yend)
-
-        # right pad side
-        self.draw.Line(-xstart, posy, -xend, yend)
-
-        # horizontal segment above the pad
-        xstart = xend
-        xend = - pad_pitch*0.5-offsetX
-        posy = yend
-        self.draw.Line(xstart, posy, xend, yend)
-
-        # right pad side
-        self.draw.Line(-xstart, posy,-xend, yend)
-
-        # vertical segment above the pad
-        xstart = xend
-        yend = -pad_height/2 - linewidth/2 - margin
-        self.draw.Line(xstart, posy, xend, yend)
-
-        # right pad side
-        self.draw.Line(-xstart, posy, -xend, yend)
-
-
-FPC_FootprintWizard().register()
diff --git a/pcbnew/python/plugins/FPC_wizard.py b/pcbnew/python/plugins/FPC_wizard.py
new file mode 100644
index 0000000..7f10b0b
--- /dev/null
+++ b/pcbnew/python/plugins/FPC_wizard.py
@@ -0,0 +1,157 @@
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA 02110-1301, USA.
+#
+
+from __future__ import division
+import pcbnew
+
+import FootprintWizardBase
+
+class FPC_FootprintWizard(FootprintWizardBase.FootprintWizard):
+
+    def GetName(self):
+        return "FPC (SMT connector)"
+
+    def GetDescription(self):
+        return "FPC (SMT connector) Footprint Wizard"
+
+    def GetValue(self):
+        pins = self.parameters["Pads"]["n"]
+        return "FPC_%d" % pins
+
+    def GenerateParameterList(self):
+        self.AddParam( "Pads", "n", self.uInteger, 40 )
+        self.AddParam( "Pads", "pitch", self.uMM, 0.5 )
+        self.AddParam( "Pads", "width", self.uMM, 0.25 )
+        self.AddParam( "Pads", "height", self.uMM, 1.6)
+        self.AddParam( "Shield", "shield_to_pad", self.uMM, 1.6 )
+        self.AddParam( "Shield", "from_top", self.uMM, 1.3 )
+        self.AddParam( "Shield", "width", self.uMM, 1.5 )
+        self.AddParam( "Shield", "height", self.uMM, 2 )
+
+
+    # build a rectangular pad
+    def smdRectPad(self,module,size,pos,name):
+        pad = pcbnew.D_PAD(module)
+        pad.SetSize(size)
+        pad.SetShape(pcbnew.PAD_SHAPE_RECT)
+        pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
+        pad.SetLayerSet( pad.SMDMask() )
+        pad.SetPos0(pos)
+        pad.SetPosition(pos)
+        pad.SetPadName(name)
+        return pad
+
+    def CheckParameters(self):
+        #TODO implement custom parameter checking
+        pass
+
+    def BuildThisFootprint(self):
+        p = self.parameters
+        pad_count       = int(p["Pads"]["n"])
+        pad_width       = p["Pads"]["width"]
+        pad_height      = p["Pads"]["height"]
+        pad_pitch       = p["Pads"]["pitch"]
+        shl_width       = p["Shield"]["width"]
+        shl_height      = p["Shield"]["height"]
+        shl_to_pad      = p["Shield"]["shield_to_pad"]
+        shl_from_top    = p["Shield"]["from_top"]
+
+        offsetX         = pad_pitch * ( pad_count-1 ) / 2
+        size_pad = pcbnew.wxSize( pad_width, pad_height )
+        size_shld = pcbnew.wxSize(shl_width, shl_height)
+        size_text = self.GetTextSize()  # IPC nominal
+
+        # Gives a position and size to ref and value texts:
+        textposy = pad_height/2 + pcbnew.FromMM(1) + self.GetTextThickness()
+        self.draw.Reference( 0, textposy, size_text )
+
+        textposy = textposy + size_text + self.GetTextThickness()
+        self.draw.Value( 0, textposy, size_text )
+
+        # create a pad array and add it to the module
+        for n in range ( 0, pad_count ):
+            xpos = pad_pitch*n - offsetX
+            pad = self.smdRectPad(self.module,size_pad, pcbnew.wxPoint(xpos,0),str(n+1))
+            self.module.Add(pad)
+
+
+        # Mechanical shield pads: left pad and right pad
+        xpos = -shl_to_pad-offsetX
+        pad_s0_pos = pcbnew.wxPoint(xpos,shl_from_top)
+        pad_s0 = self.smdRectPad(self.module, size_shld, pad_s0_pos, "0")
+        xpos = (pad_count-1) * pad_pitch+shl_to_pad - offsetX
+        pad_s1_pos = pcbnew.wxPoint(xpos,shl_from_top)
+        pad_s1 = self.smdRectPad(self.module, size_shld, pad_s1_pos, "0")
+
+        self.module.Add(pad_s0)
+        self.module.Add(pad_s1)
+
+        # add footprint outline
+        linewidth = self.draw.GetLineThickness()
+        margin = linewidth
+
+        # upper line
+        posy = -pad_height/2 - linewidth/2 - margin
+        xstart = - pad_pitch*0.5-offsetX
+        xend = pad_pitch * pad_count + xstart;
+        self.draw.Line( xstart, posy, xend, posy )
+
+        # lower line
+        posy = pad_height/2 + linewidth/2 + margin
+        self.draw.Line(xstart, posy, xend, posy)
+
+        # around left mechanical pad (the outline around right pad is mirrored/y axix)
+        yend = pad_s0_pos.y + shl_height/2 + margin
+        self.draw.Line(xstart, posy, xstart, yend)
+        self.draw.Line(-xstart, posy, -xstart, yend)
+
+        posy = yend
+        xend = pad_s0_pos.x - (shl_width/2 + linewidth + margin*2)
+        self.draw.Line(xstart, posy, xend, posy)
+
+        # right pad side
+        self.draw.Line(-xstart, posy, -xend, yend)
+
+        # set SMD attribute
+        self.module.SetAttributes(pcbnew.MOD_CMS)
+        
+        # vertical segment at left of the pad
+        xstart = xend
+        yend = posy - (shl_height + linewidth + margin*2)
+        self.draw.Line(xstart, posy, xend, yend)
+
+        # right pad side
+        self.draw.Line(-xstart, posy, -xend, yend)
+
+        # horizontal segment above the pad
+        xstart = xend
+        xend = - pad_pitch*0.5-offsetX
+        posy = yend
+        self.draw.Line(xstart, posy, xend, yend)
+
+        # right pad side
+        self.draw.Line(-xstart, posy,-xend, yend)
+
+        # vertical segment above the pad
+        xstart = xend
+        yend = -pad_height/2 - linewidth/2 - margin
+        self.draw.Line(xstart, posy, xend, yend)
+
+        # right pad side
+        self.draw.Line(-xstart, posy, -xend, yend)
+
+
+FPC_FootprintWizard().register()
diff --git a/pcbnew/python/plugins/FootprintWizardBase.py b/pcbnew/python/plugins/FootprintWizardBase.py
new file mode 100644
index 0000000..6a57ac1
--- /dev/null
+++ b/pcbnew/python/plugins/FootprintWizardBase.py
@@ -0,0 +1,674 @@
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA 02110-1301, USA.
+#
+
+from __future__ import division
+import pcbnew
+import math
+
+# Base class for creating footprint wizards
+# Inherit this class to make a new wizard
+class FootprintWizard(pcbnew.FootprintWizardPlugin):
+
+    # Copy units from pcbnew
+    uMM         = pcbnew.uMM
+    uMils       = pcbnew.uMils
+    uFloat      = pcbnew.uFloat
+    uInteger    = pcbnew.uInteger
+    uBool       = pcbnew.uBool
+    uRadians    = pcbnew.uRadians
+    uDegrees    = pcbnew.uDegrees
+    uPercent    = pcbnew.uPercent
+    uString     = pcbnew.uString
+
+    """
+    A class to simplify many aspects of footprint creation, leaving only
+    the foot-print specific routines to the wizards themselves
+
+    Generally, you need to implement:
+        GetValue()
+        GenerateParameterList()
+        CheckParameters()
+        BuildThisFootprint()
+        GetName()
+        GetDescription()
+    """
+
+    def __init__(self):
+        pcbnew.FootprintWizardPlugin.__init__(self)
+        self.GenerateParameterList()
+
+    def GetName(self):
+        """
+        Retun the name of the footprint wizard
+        """
+        raise NotImplementedError
+
+    def GetDescription(self):
+        """
+        Return the footprint wizard description
+        """
+        raise NotImplementedError
+
+    def GetValue(self):
+        """
+        Return the value (name) of the generated footprint
+        """
+        raise NotImplementedError
+
+    def GenerateParameterList(self):
+        """
+        Footprint parameter specification is done here
+        """
+    	raise NotImplementedError
+
+    def CheckParameters(self):
+        """
+        Any custom parameter checking should be performed here
+        """
+        raise NotImplementedError
+
+    def BuildThisFootprint(self):
+        """
+        Draw the footprint.
+
+        This is specific to each footprint class, you need to implment
+        this to draw what you want
+        """
+        raise NotImplementedError
+
+    # Do not override this method!
+    def BuildFootprint( self ):
+        """
+        Actually make the footprint. We defer all but the setup to
+        the implementing class
+        """
+
+        self.buildmessages = ""
+        self.module = pcbnew.MODULE(None)  # create a new module
+
+        # Perform default checks on all params
+        for p in self.params:
+            p.ClearErrors()
+            p.Check() # use defaults
+
+        self.CheckParameters() # User error checks
+
+
+        if self.AnyErrors():  # Errors were detected!
+
+            self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
+
+            for p in self.params:
+                if len(p.error_list) > 0:
+                    self.buildmessages +="['{page}']['{name}']:\n".format(page=p.page,name=p.name)
+
+                    for error in p.error_list:
+                        self.buildmessages += "\t" + error + "\n"
+
+            return
+
+        self.buildmessages = ("Building new {name} footprint with the following parameters:\n".format(name=self.name))
+
+        self.buildmessages += self.Show()
+
+        self.draw = FootprintWizardDrawingAids(
+            self.module)
+
+        self.module.SetValue(self.GetValue())
+        self.module.SetReference("%s**" % self.GetReferencePrefix())
+
+        fpid = pcbnew.LIB_ID(self.module.GetValue())  # the name in library
+        self.module.SetFPID(fpid)
+
+        self.SetModule3DModel()  # add a 3d module if specified
+
+        thick = self.GetTextThickness()
+
+        self.module.Reference().SetThickness(thick)
+        self.module.Value().SetThickness(thick)
+
+        self.BuildThisFootprint()  # implementer's build function
+
+        return
+
+    def SetModule3DModel(self):
+        pass
+
+    def GetTextSize(self):
+        """
+        IPC nominal
+        """
+        return pcbnew.FromMM(1.0)
+
+    def GetTextThickness(self):
+        """
+        Thicker than IPC guidelines (10% of text height = 0.12mm)
+        as 5 wires/mm is a common silk screen limitation
+        """
+        return pcbnew.FromMM(0.15)
+
+class FootprintWizardDrawingAids:
+    """
+    Collection of handy functions to simplify drawing shapes from within
+    footprint wizards
+
+    A "drawing context" is provided which can be used to set and retain
+    settings such as line thickness and layer
+    """
+
+    # directions (in degrees, compass-like)
+    dirN = 0
+    dirNE = 45
+    dirE = 90
+    dirSE = 135
+    dirS = 180
+    dirSW = 225
+    dirW = 270
+    dirNW = 315
+
+    # flip constants
+    flipNone = 0
+    flipX = 1  # flip X values, i.e. about Y
+    flipY = 2  # flip Y valuersabout X
+    flipBoth = 3
+
+    xfrmIDENTITY = [1, 0, 0, 0, 1, 0]  # no transform
+
+    # these values come from our KiCad Library Convention 0.11
+    defaultLineThickness = pcbnew.FromMM(0.15)
+
+    def DefaultGraphicLayer(self):
+        return pcbnew.F_SilkS
+
+    def DefaultTextValueLayer(self):
+        return pcbnew.F_Fab
+
+    def __init__(self, module):
+        self.module = module
+        # drawing context defaults
+        self.dc = {
+            'layer': self.DefaultGraphicLayer(),
+            'lineThickness': self.defaultLineThickness,
+            'transforms': [],
+            'transform': self.xfrmIDENTITY
+        }
+
+    def PushTransform(self, mat):
+        """
+        Add a transform to the top of the stack and recompute the
+        overall transform
+        """
+        self.dc['transforms'].append(mat)
+        self.RecomputeTransforms()
+
+    def PopTransform(self, num=1):
+        """
+        Remove a transform from the top of the stack and recompute the
+        overall transform
+        """
+
+        for i in range(num):
+            mat = self.dc['transforms'].pop()
+        self.RecomputeTransforms()
+        return mat
+
+    def ResetTransform(self):
+        """
+        Reset the transform stack to the identity matrix
+        """
+        self.dc['transforms'] = []
+        self.RecomputeTransforms()
+
+    def _ComposeMatricesWithIdentity(self, mats):
+        """
+        Compose a sequence of matrices together by sequential
+        pre-mutiplciation with the identity matrix
+        """
+
+        x = self.xfrmIDENTITY
+
+        for mat in mats:
+            #precompose with each transform in turn
+            x = [
+                x[0] * mat[0] + x[1] * mat[3],
+                x[0] * mat[1] + x[1] * mat[4],
+                x[0] * mat[2] + x[1] * mat[5] + x[2],
+                x[3] * mat[0] + x[4] * mat[3],
+                x[3] * mat[1] + x[4] * mat[4],
+                x[3] * mat[2] + x[4] * mat[5] + x[5]]
+
+        return x
+
+    def RecomputeTransforms(self):
+        """
+        Re-compute the transform stack into a single transform and
+        store in the DC
+        """
+        self.dc['transform'] = self._ComposeMatricesWithIdentity(
+            self.dc['transforms'])
+
+    def TransformTranslate(self, x, y, push=True):
+        """
+        Set up and return a transform matrix representing a translartion
+        optionally pushing onto the stack
+
+        (   1  0   x  )
+        (   0  1   y  )
+        """
+        mat = [1, 0, x, 0, 1, y]
+
+        if push:
+            self.PushTransform(mat)
+        return mat
+
+    def TransformFlipOrigin(self, flip, push=True):
+        """
+        Set up and return a transform matrix representing a horizontal,
+        vertical or both flip about the origin
+        """
+        mat = None
+        if flip == self.flipX:
+            mat = [-1, 0, 0, 0, 1, 0]
+        elif flip == self.flipY:
+            mat = [1, 0, 0, 0, -1, 0]
+        elif flip == self.flipBoth:
+            mat = [-1, 0, 0, 0, -1, 0]
+        elif flip == self.flipNone:
+            mat = self.xfrmIDENTITY
+        else:
+            raise ValueError
+
+        if push:
+            self.PushTransform(mat)
+        return mat
+
+    def TransformFlip(self, x, y, flip=flipNone, push=True):
+        """
+        Set up and return a transform matrix representing a horizontal,
+        vertical or both flip about a point (x,y)
+
+        This is performed by a translate-to-origin, flip, translate-
+        back sequence
+        """
+        mats = [self.TransformTranslate(x, y, push=False),
+                self.TransformFlipOrigin(flip, push=False),
+                self.TransformTranslate(-x, -y, push=False)]
+
+        #distill into a single matrix
+        mat = self._ComposeMatricesWithIdentity(mats)
+
+        if push:
+            self.PushTransform(mat)
+        return mat
+
+    def TransformRotationOrigin(self, rot, push=True):
+        """
+        Set up and return a transform matrix representing a rotation
+        about the origin, and optionally push onto the stack
+
+        (   cos(t)  -sin(t)   0  )
+        (   sin(t)   cos(t)   0  )
+        """
+        rads = rot * math.pi / 180
+        mat = [math.cos(rads), -math.sin(rads), 0,
+               math.sin(rads), math.cos(rads), 0]
+
+        if push:
+            self.PushTransform(mat)
+        return mat
+
+    def TransformRotation(self, x, y, rot, push=True):
+        """
+        Set up and return a transform matrix representing a rotation
+        about the point (x,y), and optionally push onto the stack
+
+        This is performed by a translate-to-origin, rotate, translate-
+        back sequence
+        """
+
+        mats = [self.TransformTranslate(x, y, push=False),
+                self.TransformRotationOrigin(rot, push=False),
+                self.TransformTranslate(-x, -y, push=False)]
+
+        #distill into a single matrix
+        mat = self._ComposeMatricesWithIdentity(mats)
+
+        if push:
+            self.PushTransform(mat)
+        return mat
+
+    def TransformScaleOrigin(self, sx, sy=None, push=True):
+        """
+        Set up and return a transform matrix representing a scale about
+        the origin, and optionally push onto the stack
+
+        (   sx   0   0  )
+        (    0  sy   0  )
+        """
+
+        if sy is None:
+            sy = sx
+
+        mat = [sx, 0, 0, 0, sy, 0]
+
+        if push:
+            self.PushTransform(mat)
+        return mat
+
+    def TransformPoint(self, x, y, mat=None):
+        """
+        Return a point (x, y) transformed by the given matrix, or if
+        that is not given, the drawing context transform
+        """
+
+        if not mat:
+            mat = self.dc['transform']
+
+        return pcbnew.wxPoint(x * mat[0] + y * mat[1] + mat[2],
+                              x * mat[3] + y * mat[4] + mat[5])
+
+    def SetLineThickness(self, lineThickness):
+        """
+        Set the current pen lineThickness used for subsequent drawing
+        operations
+        """
+        self.dc['lineThickness'] = lineThickness
+
+    def SetLineTickness(self, lineThickness):
+        """
+        Old version of SetLineThickness.
+        Does the same thing, but is is only here for compatibility with old scripts
+        Set the current pen lineThickness used for subsequent drawing
+        operations
+        """
+        self.dc['lineThickness'] = lineThickness
+
+    def GetLineThickness(self):
+        """
+        Get the current drawing context line thickness
+        """
+        return self.dc['lineThickness']
+
+    def SetLayer(self, layer):
+        """
+        Set the current drawing layer, used for subsequent drawing
+        operations
+        """
+        self.dc['layer'] = layer
+
+    def GetLayer(self):
+        """
+        return the current drawing layer, used drawing operations
+        """
+        return self.dc['layer']
+
+    def Line(self, x1, y1, x2, y2):
+        """
+        Draw a line from (x1, y1) to (x2, y2)
+        """
+        outline = pcbnew.EDGE_MODULE(self.module)
+        outline.SetWidth(self.GetLineThickness())
+        outline.SetLayer(self.GetLayer())
+        outline.SetShape(pcbnew.S_SEGMENT)
+        start = self.TransformPoint(x1, y1)
+        end = self.TransformPoint(x2, y2)
+        outline.SetStartEnd(start, end)
+        self.module.Add(outline)
+
+    def Circle(self, x, y, r, filled=False):
+        """
+        Draw a circle at (x,y) of radius r
+        If filled is true, the thickness and radius of the line will be set
+        such that the circle appears filled
+        """
+
+        circle = pcbnew.EDGE_MODULE(self.module)
+        start = self.TransformPoint(x, y)
+
+        if filled:
+            circle.SetWidth(r)
+            end = self.TransformPoint(x, y + r/2)
+        else:
+            circle.SetWidth(self.dc['lineThickness'])
+            end = self.TransformPoint(x, y + r)
+
+        circle.SetLayer(self.dc['layer'])
+        circle.SetShape(pcbnew.S_CIRCLE)
+        circle.SetStartEnd(start, end)
+        self.module.Add(circle)
+
+    def Arc(self, cx, cy, sx, sy, a):
+        """
+        Draw an arc based on centre, start and angle
+
+        The transform matrix is applied
+
+        Note that this won't work properly if the result is not a
+        circular arc (eg a horzontal scale)
+        """
+        circle = pcbnew.EDGE_MODULE(self.module)
+        circle.SetWidth(self.dc['lineThickness'])
+
+        center = self.TransformPoint(cx, cy)
+        start = self.TransformPoint(sx, sy)
+
+        circle.SetLayer(self.dc['layer'])
+        circle.SetShape(pcbnew.S_ARC)
+
+        # check if the angle needs to be reverse (a flip scaling)
+        if cmp(self.dc['transform'][0], 0) != cmp(self.dc['transform'][4], 0):
+            a = -a
+
+        circle.SetAngle(a)
+        circle.SetStartEnd(center, start)
+        self.module.Add(circle)
+
+    # extends from (x1,y1) right
+    def HLine(self, x, y, l):
+        """
+        Draw a horizontal line from (x,y), rightwards
+        """
+        self.Line(x, y, x + l, y)
+
+    def VLine(self, x, y, l):
+        """
+        Draw a vertical line from (x1,y1), downwards
+        """
+        self.Line(x, y, x, y + l)
+
+    def Polyline(self, pts, mirrorX=None, mirrorY=None):
+        """
+        Draw a polyline, optinally mirroring around the given points
+        """
+
+        def _PolyLineInternal(pts):
+            if len(pts) < 2:
+                return
+
+            for i in range(0, len(pts) - 1):
+                self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1])
+
+        _PolyLineInternal(pts)  # original
+
+        if mirrorX is not None and mirrorY is not None:
+            self.TransformFlip(mirrorX, mirrorY, self.flipBoth)  # both
+            _PolyLineInternal(pts)
+            self.PopTransform()
+
+        elif mirrorX is not None:
+            self.TransformFlip(mirrorX, 0, self.flipX)
+            _PolyLineInternal(pts)
+            self.PopTransform()
+
+        elif mirrorY is not None:
+            self.TransformFlip(0, mirrorY, self.flipY)
+            _PolyLineInternal(pts)
+            self.PopTransform()
+
+
+    def Reference(self, x, y, size, orientation_degree = 0):
+        """
+        Draw the module's reference as the given point.
+
+        The actual setting of the reference is not done in this drawing
+        aid - that is up to the wizard
+        """
+
+        text_size = pcbnew.wxSize(size, size)
+
+        self.module.Reference().SetPos0(self.TransformPoint(x, y))
+        self.module.Reference().SetTextPosition(
+            self.module.Reference().GetPos0())
+        self.module.Reference().SetSize(text_size)
+        self.module.Reference().SetOrientation(orientation_degree*10)   # internal angles are in 0.1 deg
+
+    def Value(self, x, y, size, orientation_degree = 0):
+        """
+        As for references, draw the module's value
+        """
+        text_size = pcbnew.wxSize(size, size)
+
+        self.module.Value().SetPos0(self.TransformPoint(x, y))
+        self.module.Value().SetTextPosition(self.module.Value().GetPos0())
+        self.module.Value().SetSize(text_size)
+        self.module.Value().SetLayer(self.DefaultTextValueLayer())
+        self.module.Value().SetOrientation(orientation_degree*10)   # internal angles are in 0.1 deg
+
+    def Box(self, x, y, w, h):
+        """
+        Draw a rectangular box, centred at (x,y), with given width and
+        height
+        """
+
+        pts = [[x - w/2, y - h/2],  # left
+               [x + w/2, y - h/2],  # right
+               [x + w/2, y + h/2],  # bottom
+               [x - w/2, y + h/2],  # top
+               [x - w/2, y - h/2]]  # close
+
+        self.Polyline(pts)
+
+    def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0):
+        """
+        Circle radus r centred at (x, y) with a raised or depressed notch
+        at the top
+        Notch height is measured from the top of the circle radius
+        """
+
+        self.TransformRotation(x, y, rotate)
+
+        # find the angle where the notch vertical meets the circle
+        angle_intercept = math.asin(notch_w/(2 * r))
+
+        # and find the co-ords of this point
+        sx = math.sin(angle_intercept) * r
+        sy = -math.cos(angle_intercept) * r
+
+        # NOTE: this may be out by a factor of ten one day
+        arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi)
+
+        self.Arc(x,y, sx, sy, arc_angle)
+
+        pts = [[sx,  sy],
+               [sx,  -r - notch_h],
+               [-sx, -r - notch_h],
+               [-sx, sy]]
+
+        self.Polyline(pts)
+        self.PopTransform()
+
+    def NotchedBox(self, x, y, w, h, notchW, notchH, rotate=0):
+        """
+        Draw a box with a notch in the top edge
+        """
+
+        self.TransformRotation(x, y, rotate)
+
+        # limit to half the overall width
+        notchW = min(x + w/2, notchW)
+
+        # draw notch
+        self.Polyline([  # three sides of box
+            (x - w/2, y - h/2),
+            (x - w/2, y + h/2),
+            (x + w/2, y + h/2),
+            (x + w/2, y - h/2),
+            # the notch
+            (notchW/2, y - h/2),
+            (notchW/2, y - h/2 + notchH),
+            (-notchW/2, y - h/2 + notchH),
+            (-notchW/2, y - h/2),
+            (x - w/2, y - h/2)
+        ])
+
+        self.PopTransform()
+
+    def BoxWithDiagonalAtCorner(self, x, y, w, h,
+                                setback=pcbnew.FromMM(1.27), flip=flipNone):
+        """
+        Draw a box with a diagonal at the top left corner
+        """
+
+        self.TransformFlip(x, y, flip, push=True)
+
+        pts = [[x - w/2 + setback, y - h/2],
+               [x - w/2,           y - h/2 + setback],
+               [x - w/2,           y + h/2],
+               [x + w/2,           y + h/2],
+               [x + w/2,           y - h/2],
+               [x - w/2 + setback, y - h/2]]
+
+        self.Polyline(pts)
+
+        self.PopTransform()
+
+    def BoxWithOpenCorner(self, x, y, w, h,
+                          setback=pcbnew.FromMM(1.27), flip=flipNone):
+        """
+        Draw a box with an opening at the top left corner
+        """
+
+        self.TransformTranslate(x, y)
+        self.TransformFlipOrigin(flip)
+
+        pts = [[- w/2,           - h/2 + setback],
+               [- w/2,           + h/2],
+               [+ w/2,           + h/2],
+               [+ w/2,           - h/2],
+               [- w/2 + setback, - h/2]]
+
+        self.Polyline(pts)
+
+        self.PopTransform(num=2)
+
+    def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)):
+        """
+        Draw a marker arrow facing in the given direction, with the
+        point at (x,y)
+
+        Direction of 0 is north
+        """
+
+        self.TransformTranslate(x, y)
+        self.TransformRotationOrigin(direction)
+
+        pts = [[0,          0],
+               [width / 2,  width / 2],
+               [-width / 2, width / 2],
+               [0,          0]]
+
+        self.Polyline(pts)
+        self.PopTransform(2)
\ No newline at end of file
diff --git a/pcbnew/python/plugins/FootprintWizardDrawingAids.py b/pcbnew/python/plugins/FootprintWizardDrawingAids.py
deleted file mode 100644
index 036f09a..0000000
--- a/pcbnew/python/plugins/FootprintWizardDrawingAids.py
+++ /dev/null
@@ -1,532 +0,0 @@
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-#  MA 02110-1301, USA.
-#
-
-from __future__ import division
-
-import pcbnew
-import math
-
-
-class FootprintWizardDrawingAids:
-    """
-    Collection of handy functions to simplify drawing shapes from within
-    footprint wizards
-
-    A "drawing context" is provided which can be used to set and retain
-    settings such as line thickness and layer
-    """
-
-    # directions (in degrees, compass-like)
-    dirN = 0
-    dirNE = 45
-    dirE = 90
-    dirSE = 135
-    dirS = 180
-    dirSW = 225
-    dirW = 270
-    dirNW = 315
-
-    # flip constants
-    flipNone = 0
-    flipX = 1  # flip X values, i.e. about Y
-    flipY = 2  # flip Y valuersabout X
-    flipBoth = 3
-
-    xfrmIDENTITY = [1, 0, 0, 0, 1, 0]  # no transform
-
-    # these values come from our KiCad Library Convention 0.11
-    defaultLineThickness = pcbnew.FromMM(0.15)
-
-    def DefaultGraphicLayer(self):
-        return pcbnew.F_SilkS
-
-    def DefaultTextValueLayer(self):
-        return pcbnew.F_Fab
-
-    def __init__(self, module):
-        self.module = module
-        # drawing context defaults
-        self.dc = {
-            'layer': self.DefaultGraphicLayer(),
-            'lineThickness': self.defaultLineThickness,
-            'transforms': [],
-            'transform': self.xfrmIDENTITY
-        }
-
-    def PushTransform(self, mat):
-        """
-        Add a transform to the top of the stack and recompute the
-        overall transform
-        """
-        self.dc['transforms'].append(mat)
-        self.RecomputeTransforms()
-
-    def PopTransform(self, num=1):
-        """
-        Remove a transform from the top of the stack and recompute the
-        overall transform
-        """
-
-        for i in range(num):
-            mat = self.dc['transforms'].pop()
-        self.RecomputeTransforms()
-        return mat
-
-    def ResetTransform(self):
-        """
-        Reset the transform stack to the identity matrix
-        """
-        self.dc['transforms'] = []
-        self.RecomputeTransforms()
-
-    def _ComposeMatricesWithIdentity(self, mats):
-        """
-        Compose a sequence of matrices together by sequential
-        pre-mutiplciation with the identity matrix
-        """
-
-        x = self.xfrmIDENTITY
-
-        for mat in mats:
-            #precompose with each transform in turn
-            x = [
-                x[0] * mat[0] + x[1] * mat[3],
-                x[0] * mat[1] + x[1] * mat[4],
-                x[0] * mat[2] + x[1] * mat[5] + x[2],
-                x[3] * mat[0] + x[4] * mat[3],
-                x[3] * mat[1] + x[4] * mat[4],
-                x[3] * mat[2] + x[4] * mat[5] + x[5]]
-
-        return x
-
-    def RecomputeTransforms(self):
-        """
-        Re-compute the transform stack into a single transform and
-        store in the DC
-        """
-        self.dc['transform'] = self._ComposeMatricesWithIdentity(
-            self.dc['transforms'])
-
-    def TransformTranslate(self, x, y, push=True):
-        """
-        Set up and return a transform matrix representing a translartion
-        optionally pushing onto the stack
-
-        (   1  0   x  )
-        (   0  1   y  )
-        """
-        mat = [1, 0, x, 0, 1, y]
-
-        if push:
-            self.PushTransform(mat)
-        return mat
-
-    def TransformFlipOrigin(self, flip, push=True):
-        """
-        Set up and return a transform matrix representing a horizontal,
-        vertical or both flip about the origin
-        """
-        mat = None
-        if flip == self.flipX:
-            mat = [-1, 0, 0, 0, 1, 0]
-        elif flip == self.flipY:
-            mat = [1, 0, 0, 0, -1, 0]
-        elif flip == self.flipBoth:
-            mat = [-1, 0, 0, 0, -1, 0]
-        elif flip == self.flipNone:
-            mat = self.xfrmIDENTITY
-        else:
-            raise ValueError
-
-        if push:
-            self.PushTransform(mat)
-        return mat
-
-    def TransformFlip(self, x, y, flip=flipNone, push=True):
-        """
-        Set up and return a transform matrix representing a horizontal,
-        vertical or both flip about a point (x,y)
-
-        This is performed by a translate-to-origin, flip, translate-
-        back sequence
-        """
-        mats = [self.TransformTranslate(x, y, push=False),
-                self.TransformFlipOrigin(flip, push=False),
-                self.TransformTranslate(-x, -y, push=False)]
-
-        #distill into a single matrix
-        mat = self._ComposeMatricesWithIdentity(mats)
-
-        if push:
-            self.PushTransform(mat)
-        return mat
-
-    def TransformRotationOrigin(self, rot, push=True):
-        """
-        Set up and return a transform matrix representing a rotation
-        about the origin, and optionally push onto the stack
-
-        (   cos(t)  -sin(t)   0  )
-        (   sin(t)   cos(t)   0  )
-        """
-        rads = rot * math.pi / 180
-        mat = [math.cos(rads), -math.sin(rads), 0,
-               math.sin(rads), math.cos(rads), 0]
-
-        if push:
-            self.PushTransform(mat)
-        return mat
-
-    def TransformRotation(self, x, y, rot, push=True):
-        """
-        Set up and return a transform matrix representing a rotation
-        about the point (x,y), and optionally push onto the stack
-
-        This is performed by a translate-to-origin, rotate, translate-
-        back sequence
-        """
-
-        mats = [self.TransformTranslate(x, y, push=False),
-                self.TransformRotationOrigin(rot, push=False),
-                self.TransformTranslate(-x, -y, push=False)]
-
-        #distill into a single matrix
-        mat = self._ComposeMatricesWithIdentity(mats)
-
-        if push:
-            self.PushTransform(mat)
-        return mat
-
-    def TransformScaleOrigin(self, sx, sy=None, push=True):
-        """
-        Set up and return a transform matrix representing a scale about
-        the origin, and optionally push onto the stack
-
-        (   sx   0   0  )
-        (    0  sy   0  )
-        """
-
-        if sy is None:
-            sy = sx
-
-        mat = [sx, 0, 0, 0, sy, 0]
-
-        if push:
-            self.PushTransform(mat)
-        return mat
-
-    def TransformPoint(self, x, y, mat=None):
-        """
-        Return a point (x, y) transformed by the given matrix, or if
-        that is not given, the drawing context transform
-        """
-
-        if not mat:
-            mat = self.dc['transform']
-
-        return pcbnew.wxPoint(x * mat[0] + y * mat[1] + mat[2],
-                              x * mat[3] + y * mat[4] + mat[5])
-
-    def SetLineThickness(self, lineThickness):
-        """
-        Set the current pen lineThickness used for subsequent drawing
-        operations
-        """
-        self.dc['lineThickness'] = lineThickness
-
-    def SetLineTickness(self, lineThickness):
-        """
-        Old version of SetLineThickness.
-        Does the same thing, but is is only here for compatibility with old scripts
-        Set the current pen lineThickness used for subsequent drawing
-        operations
-        """
-        self.dc['lineThickness'] = lineThickness
-
-    def GetLineThickness(self):
-        """
-        Get the current drawing context line thickness
-        """
-        return self.dc['lineThickness']
-
-    def SetLayer(self, layer):
-        """
-        Set the current drawing layer, used for subsequent drawing
-        operations
-        """
-        self.dc['layer'] = layer
-
-    def GetLayer(self):
-        """
-        return the current drawing layer, used drawing operations
-        """
-        return self.dc['layer']
-
-    def Line(self, x1, y1, x2, y2):
-        """
-        Draw a line from (x1, y1) to (x2, y2)
-        """
-        outline = pcbnew.EDGE_MODULE(self.module)
-        outline.SetWidth(self.GetLineThickness())
-        outline.SetLayer(self.GetLayer())
-        outline.SetShape(pcbnew.S_SEGMENT)
-        start = self.TransformPoint(x1, y1)
-        end = self.TransformPoint(x2, y2)
-        outline.SetStartEnd(start, end)
-        self.module.Add(outline)
-
-    def Circle(self, x, y, r, filled=False):
-        """
-        Draw a circle at (x,y) of radius r
-        If filled is true, the thickness and radius of the line will be set
-        such that the circle appears filled
-        """
-        circle = pcbnew.EDGE_MODULE(self.module)
-        start = self.TransformPoint(x, y)
-
-        if filled:
-            circle.SetWidth(r)
-            end = self.TransformPoint(x, y + r/2)
-        else:
-            circle.SetWidth(self.dc['lineThickness'])
-            end = self.TransformPoint(x, y + r)
-
-        circle.SetLayer(self.dc['layer'])
-        circle.SetShape(pcbnew.S_CIRCLE)
-        circle.SetStartEnd(start, end)
-        self.module.Add(circle)
-
-    def Arc(self, cx, cy, sx, sy, a):
-        """
-        Draw an arc based on centre, start and angle
-
-        The transform matrix is applied
-
-        Note that this won't work properly if the result is not a
-        circular arc (eg a horzontal scale)
-        """
-        circle = pcbnew.EDGE_MODULE(self.module)
-        circle.SetWidth(self.dc['lineThickness'])
-
-        center = self.TransformPoint(cx, cy)
-        start = self.TransformPoint(sx, sy)
-
-        circle.SetLayer(self.dc['layer'])
-        circle.SetShape(pcbnew.S_ARC)
-
-        # check if the angle needs to be reverse (a flip scaling)
-        if cmp(self.dc['transform'][0], 0) != cmp(self.dc['transform'][4], 0):
-            a = -a
-
-        circle.SetAngle(a)
-        circle.SetStartEnd(center, start)
-        self.module.Add(circle)
-
-    # extends from (x1,y1) right
-    def HLine(self, x, y, l):
-        """
-        Draw a horizontal line from (x,y), rightwards
-        """
-        self.Line(x, y, x + l, y)
-
-    def VLine(self, x, y, l):
-        """
-        Draw a vertical line from (x1,y1), downwards
-        """
-        self.Line(x, y, x, y + l)
-
-    def Polyline(self, pts, mirrorX=None, mirrorY=None):
-        """
-        Draw a polyline, optinally mirroring around the given points
-        """
-
-        def _PolyLineInternal(pts):
-            if len(pts) < 2:
-                return
-
-            for i in range(0, len(pts) - 1):
-                self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1])
-
-        _PolyLineInternal(pts)  # original
-
-        if mirrorX is not None:
-            self.TransformFlip(mirrorX, 0, self.flipX)
-            _PolyLineInternal(pts)
-            self.PopTransform()
-
-        if mirrorY is not None:
-            self.TransformFlipOrigin(0, mirrorY, self.flipY)
-            _PolyLineInternal(pts)
-            self.PopTransform()
-
-        if mirrorX is not None and mirrorY is not None:
-            self.TransformFlip(mirrorX, mirrorY, self.flipBoth)  # both
-            _PolyLineInternal(pts)
-            self.PopTransform()
-
-    def Reference(self, x, y, size, orientation_degree = 0):
-        """
-        Draw the module's reference as the given point.
-
-        The actual setting of the reference is not done in this drawing
-        aid - that is up to the wizard
-        """
-
-        text_size = pcbnew.wxSize(size, size)
-
-        self.module.Reference().SetPos0(self.TransformPoint(x, y))
-        self.module.Reference().SetTextPosition(
-            self.module.Reference().GetPos0())
-        self.module.Reference().SetSize(text_size)
-        self.module.Reference().SetOrientation(orientation_degree*10)   # internal angles are in 0.1 deg
-
-    def Value(self, x, y, size, orientation_degree = 0):
-        """
-        As for references, draw the module's value
-        """
-        text_size = pcbnew.wxSize(size, size)
-
-        self.module.Value().SetPos0(self.TransformPoint(x, y))
-        self.module.Value().SetTextPosition(self.module.Value().GetPos0())
-        self.module.Value().SetSize(text_size)
-        self.module.Value().SetLayer(self.DefaultTextValueLayer())
-        self.module.Value().SetOrientation(orientation_degree*10)   # internal angles are in 0.1 deg
-
-    def Box(self, x, y, w, h):
-        """
-        Draw a rectangular box, centred at (x,y), with given width and
-        height
-        """
-
-        pts = [[x - w/2, y - h/2],  # left
-               [x + w/2, y - h/2],  # right
-               [x + w/2, y + h/2],  # bottom
-               [x - w/2, y + h/2],  # top
-               [x - w/2, y - h/2]]  # close
-
-        self.Polyline(pts)
-
-    def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0):
-        """
-        Circle radus r centred at (x, y) with a raised or depressed notch
-        at the top
-        Notch height is measured from the top of the circle radius
-        """
-
-        self.TransformRotation(x, y, rotate)
-
-        # find the angle where the notch vertical meets the circle
-        angle_intercept = math.asin(notch_w/(2 * r))
-
-        # and find the co-ords of this point
-        sx = math.sin(angle_intercept) * r
-        sy = -math.cos(angle_intercept) * r
-
-        # NOTE: this may be out by a factor of ten one day
-        arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi)
-
-        self.Arc(x,y, sx, sy, arc_angle)
-
-        pts = [[sx,  sy],
-               [sx,  -r - notch_h],
-               [-sx, -r - notch_h],
-               [-sx, sy]]
-
-        self.Polyline(pts)
-        self.PopTransform()
-
-    def NotchedBox(self, x, y, w, h, notchW, notchH, rotate=0):
-        """
-        Draw a box with a notch in the top edge
-        """
-
-        self.TransformRotation(x, y, rotate)
-
-        # limit to half the overall width
-        notchW = min(x + w/2, notchW)
-
-        # draw notch
-        self.Polyline([  # three sides of box
-            (x - w/2, y - h/2),
-            (x - w/2, y + h/2),
-            (x + w/2, y + h/2),
-            (x + w/2, y - h/2),
-            # the notch
-            (notchW/2, y - h/2),
-            (notchW/2, y - h/2 + notchH),
-            (-notchW/2, y - h/2 + notchH),
-            (-notchW/2, y - h/2),
-            (x - w/2, y - h/2)
-        ])
-
-        self.PopTransform()
-
-    def BoxWithDiagonalAtCorner(self, x, y, w, h,
-                                setback=pcbnew.FromMM(1.27), flip=flipNone):
-        """
-        Draw a box with a diagonal at the top left corner
-        """
-
-        self.TransformFlip(x, y, flip, push=True)
-
-        pts = [[x - w/2 + setback, y - h/2],
-               [x - w/2,           y - h/2 + setback],
-               [x - w/2,           y + h/2],
-               [x + w/2,           y + h/2],
-               [x + w/2,           y - h/2],
-               [x - w/2 + setback, y - h/2]]
-
-        self.Polyline(pts)
-
-        self.PopTransform()
-
-    def BoxWithOpenCorner(self, x, y, w, h,
-                          setback=pcbnew.FromMM(1.27), flip=flipNone):
-        """
-        Draw a box with an opening at the top left corner
-        """
-
-        self.TransformTranslate(x, y)
-        self.TransformFlipOrigin(flip)
-
-        pts = [[- w/2,           - h/2 + setback],
-               [- w/2,           + h/2],
-               [+ w/2,           + h/2],
-               [+ w/2,           - h/2],
-               [- w/2 + setback, - h/2]]
-
-        self.Polyline(pts)
-
-        self.PopTransform(num=2)
-
-    def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)):
-        """
-        Draw a marker arrow facing in the given direction, with the
-        point at (x,y)
-
-        Direction of 0 is north
-        """
-
-        self.TransformTranslate(x, y)
-        self.TransformRotationOrigin(direction)
-
-        pts = [[0,          0],
-               [width / 2,  width / 2],
-               [-width / 2, width / 2],
-               [0,          0]]
-
-        self.Polyline(pts)
-        self.PopTransform(2)
diff --git a/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py b/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py
deleted file mode 100644
index d891b7a..0000000
--- a/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py
+++ /dev/null
@@ -1,348 +0,0 @@
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-#  MA 02110-1301, USA.
-#
-
-import pcbnew
-import math
-import FootprintWizardDrawingAids
-
-
-class FootprintWizardParameterManager:
-    """
-    Functions for helpfully managing parameters to a KiCAD Footprint
-    Wizard.
-
-    Abstracts away from whatever structure is used by pcbnew's footprint
-    wizard class
-    """
-
-    def __init__(self):
-        self.parameters = {}
-        self.GenerateParameterList()
-
-    def GenerateParameterList(self):
-        """
-        Construct parameters here, or leave out to have no parameters
-        """
-        pass
-
-    def CheckParameters(self):
-        """
-        Implement this to make checks on parameter values, filling
-        parameter_errors (or using the checker routines)
-
-        Subclasses can implment their own and override the parent
-        defaults and add new ones
-        """
-        pass
-
-    uMM = 1
-    uMils = 2
-    uNatural = 3
-    uBool = 4
-    uString = 5
-
-    def AddParam(self, section, param, unit, default, hint=''):
-        """
-        Add a parameter with some properties.
-
-        TODO: Hints are not supported, as there is as yet nowhere to
-        put them in the KiCAD interface
-        """
-        error = ""
-        val = None
-        if unit == self.uMM:
-            val = pcbnew.FromMM(default)
-        elif unit == self.uMils:
-            val = pcbnew.FromMils(default)
-        elif unit == self.uNatural:
-            val = default
-        elif unit == self.uString:
-            val = str(default)
-        elif unit == self.uBool:
-            val = "True" if default else "False"  # ugly stringing
-        else:
-            error = "Warning: Unknown unit type: %s" % unit
-            return error
-
-        if unit in [self.uNatural, self.uBool, self.uString]:
-            param = "*%s" % param  # star prefix for natural
-
-        if section not in self.parameters:
-            if not hasattr(self, 'page_order'):
-                self.page_order = []
-            self.page_order.append(section)
-            self.parameters[section] = {}
-            if not hasattr(self, 'parameter_order'):
-                self.parameter_order = {}
-            self.parameter_order[section] = []
-
-        self.parameters[section][param] = val
-        self.parameter_order[section].append(param)
-
-        return error
-
-
-    def _PrintParameterTable(self):
-        """
-        Pretty-print the parameters we have
-        """
-        message = ""
-
-        for name, section in self.parameters.iteritems():
-            message += "  %s:\n" % name
-
-            for key, value in section.iteritems():
-                unit = ""
-                if ((type(value) is int or type(value) is float)
-                        and not "*" in key):
-                    unit = "mm"
-
-                if "*" in key:
-                    key = key[1:]
-                else:
-                    value = pcbnew.ToMM(value)
-
-                message += "    %s: %s%s\n" % (key, value, unit)
-
-        return message
-
-
-    def _ParametersHaveErrors(self):
-        """
-        Return true if we discovered errors during parameter processing
-        """
-
-        for name, section in self.parameter_errors.iteritems():
-            for k, v in section.iteritems():
-                if v:
-                    return True
-
-        return False
-
-    def _PrintParameterErrors(self):
-        """
-        Pretty-print parameters with errors
-        """
-        errors = ""
-
-        for name, section in self.parameter_errors.iteritems():
-            printed_section = False
-
-            for key, value in section.iteritems():
-                if value:
-                    if not printed_section:
-                        errors += "  %s:" % name
-
-                    errors += "       %s: %s (have %s)\n" % (
-                        key, value, self.parameters[name][key])
-
-        return errors
-
-    def ProcessParameters(self):
-        """
-        Make sure the parameters we have meet whatever expectations the
-        footprint wizard has of them
-        """
-
-        self.ClearErrors()
-        self.CheckParameters()
-
-        if self._ParametersHaveErrors():
-            return False
-
-        return True
-
-    #################################################################
-    # PARAMETER CHECKERS
-    #################################################################
-
-    def CheckParamInt(self, section, param, min_value=1,
-                      max_value=None, is_multiple_of=1):
-        """
-        Make sure a parameter can be made into an int, and enforce
-        limits if required
-        """
-
-        try:
-            self.parameters[section][param] = (
-                int(self.parameters[section][param]))
-        except ValueError:
-            self.parameter_errors[section][param] = (
-                "Must be a valid integer")
-            return
-
-        if min_value is not None and (
-                self.parameters[section][param] < min_value):
-            self.parameter_errors[section][param] = (
-                "Must be greater than or equal to %d" % (min_value))
-            return
-
-        if max_value is not None and (
-                self.parameters[section][param] > max_value):
-            self.parameter_errors[section][param] = (
-                "Must be less than or equal to %d" % (max_value))
-            return
-
-        if is_multiple_of > 1 and (
-                self.parameters[section][param] % is_multiple_of) > 0:
-            self.parameter_errors[section][param] = (
-                "Must be a multiple of %d" % is_multiple_of)
-            return
-
-        return
-
-    def CheckParamBool(self, section, param):
-        """
-        Make sure a parameter looks like a boolean, convert to native
-        boolean type if so
-        """
-        if str(self.parameters[section][param]).lower() in [
-                "true", "t", "y", "yes", "on", "1", "1.0"]:
-            self.parameters[section][param] = True
-            return
-        elif str(self.parameters[section][param]).lower() in [
-                "false", "f", "n", "no", "off", "0", "0.0"]:
-            self.parameters[section][param] = False
-            return
-
-        self.parameter_errors[section][param] = "Must be boolean (true/false)"
-        return
-
-
-class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin,
-                                   FootprintWizardParameterManager):
-    """
-    A class to simplify many aspects of footprint creation, leaving only
-    the foot-print specific routines to the wizards themselves
-
-    Generally, you need to implement:
-        GetReference()
-        GetValue()
-        GenerateParameterList()
-        CheckParameters()
-        BuildThisFootprint()
-        GetName()
-        GetDescription()
-    """
-    def __init__(self):
-        pcbnew.FootprintWizardPlugin.__init__(self)
-        FootprintWizardParameterManager.__init__(self)
-
-        self.name = self.GetName()
-        self.decription = self.GetDescription()
-        self.image = self.GetImage()
-
-    def GetValue(self):
-        raise NotImplementedError
-
-    # this value come from our KiCad Library Convention 1.0
-    def GetReferencePrefix(self):
-        return "REF"
-
-    def GetImage(self):
-        return ""
-
-    def GetTextSize(self):
-        """
-        IPC nominal
-        """
-        return pcbnew.FromMM(1.0)
-
-    def GetTextThickness(self):
-        """
-        Thicker than IPC guidelines (10% of text height = 0.12mm)
-        as 5 wires/mm is a common silk screen limitation
-        """
-        return pcbnew.FromMM(0.15)
-
-    def SetModule3DModel(self):
-        """
-        Set a 3D model for the module
-
-        Default is to do nothing, you need to implement this if you have
-        a model to set
-
-        FIXME: This doesn't seem to be enabled yet?
-        """
-        pass
-
-    def PutOnGridMM(self, value, gridSizeMM=0.05):
-        """
-        Round the value (in KiCAD internal units 1nm) according to the 
-        provided gridSize in mm.
-        """
-        thresh = pcbnew.FromMM(gridSizeMM)
-        res = round(value/thresh)*thresh
-        return res
-
-    def PutOnGridMils(self, value, gridSizeMil=2):
-        """
-        Round the value (in KiCAD internal units 1nm) according to the 
-        provided gridSize in mil.
-        """
-        thresh = pcbnew.FromMils(gridSizeMil)
-        res = round(value/thresh)*thresh
-        return res
-
-    def BuildThisFootprint(self):
-        """
-        Draw the footprint.
-
-        This is specific to each footprint class, you need to implment
-        this to draw what you want
-        """
-        raise NotImplementedError
-
-    def BuildFootprint( self ):
-        """
-        Actually make the footprint. We defer all but the setup to
-        the implementing class
-        """
-
-        self.buildmessages = ""
-
-        self.module = pcbnew.MODULE(None)  # create a new module
-        # do it first, so if we return early, we don't segfault KiCad
-
-        if not self.ProcessParameters():
-            self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
-            self.buildmessages += self._PrintParameterErrors()
-            return
-
-        self.buildmessages = ("Building new %s footprint with the following parameters:\n"
-                   % self.name)
-
-        self.buildmessages += self._PrintParameterTable()
-
-        self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(
-            self.module)
-
-        self.module.SetValue(self.GetValue())
-        self.module.SetReference("%s**" % self.GetReferencePrefix())
-
-        fpid = pcbnew.LIB_ID(self.module.GetValue())  # the name in library
-        self.module.SetFPID(fpid)
-
-        self.SetModule3DModel()  # add a 3d module if specified
-
-        thick = self.GetTextThickness()
-
-        self.module.Reference().SetThickness(thick)
-        self.module.Value().SetThickness(thick)
-
-        self.BuildThisFootprint()  # implementer's build function
-
-        return
diff --git a/pcbnew/python/plugins/bga_wizard.py b/pcbnew/python/plugins/bga_wizard.py
index 8ce17e6..6705450 100644
--- a/pcbnew/python/plugins/bga_wizard.py
+++ b/pcbnew/python/plugins/bga_wizard.py
@@ -17,7 +17,7 @@
 from __future__ import division
 import pcbnew
 
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
 import PadArray as PA
 
 
@@ -29,7 +29,7 @@ class BGAPadGridArray(PA.PadGridArray):
             n_x + 1)
 
 
-class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
+class BGAWizard(FootprintWizardBase.FootprintWizard):
 
     def GetName(self):
         return "BGA"
@@ -38,35 +38,48 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
         return "Ball Grid Array Footprint Wizard"
 
     def GenerateParameterList(self):
-        self.AddParam("Pads", "pad pitch", self.uMM, 1)
-        self.AddParam("Pads", "pad size", self.uMM, 0.5)
-        self.AddParam("Pads", "row count", self.uNatural, 5)
-        self.AddParam("Pads", "column count", self.uNatural, 5)
-        self.AddParam("Pads", "outline x margin", self.uMM, 1)
-        self.AddParam("Pads", "outline y margin", self.uMM, 1)
+        self.AddParam("Pads", "pitch", self.uMM, 1, designator='p')
+        self.AddParam("Pads", "size", self.uMM, 0.5)
+        self.AddParam("Pads", "columns", self.uInteger, 5, designator="nx")
+        self.AddParam("Pads", "rows", self.uInteger, 5, designator="ny")
+
+        self.AddParam("Package", "width", self.uMM, 6, designator='X')
+        self.AddParam("Package", "length", self.uMM, 6, designator='Y')
+        self.AddParam("Package", "margin", self.uMM, 0.25, min_value=0.2, hint="Courtyard margin")
 
     def CheckParameters(self):
-        self.CheckParamInt("Pads", "*row count")
-        self.CheckParamInt("Pads", "*column count")
+
+        # check that the package is large enough
+        width = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['columns'])
+
+        length = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['rows'])
+
+        self.CheckParam('Package','width',min_value=width,info="Package width is too small (< {w}mm)".format(w=width))
+        self.CheckParam('Package','length',min_value=length,info="Package length is too small (< {l}mm".format(l=length))
 
     def GetValue(self):
-        pins = (self.parameters["Pads"]["*row count"]
-                * self.parameters["Pads"]["*column count"])
+        pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"])
 
-        return "BGA_%d" % pins
+        return "BGA-{n}_{a}x{b}_{x}x{y}mm".format(
+                n = pins,
+                a = self.parameters['Pads']['columns'],
+                b = self.parameters['Pads']['rows'],
+                x = pcbnew.ToMM(self.parameters['Package']['width']),
+                y = pcbnew.ToMM(self.parameters['Package']['length'])
+            )
 
     def BuildThisFootprint(self):
 
         pads = self.parameters["Pads"]
 
-        rows = pads["*row count"]
-        cols = pads["*column count"]
-        pad_size = pads["pad size"]
+        rows = pads["rows"]
+        cols = pads["columns"]
+        pad_size = pads["size"]
         pad_size = pcbnew.wxSize(pad_size, pad_size)
-        pad_pitch = pads["pad pitch"]
+        pad_pitch = pads["pitch"]
 
         # add in the pads
-        pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"])
+        pad = PA.PadMaker(self.module).SMTRoundPad(pads["size"])
 
         pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2,
                                   -((rows - 1) * pad_pitch) / 2)
@@ -74,21 +87,68 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
         array = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch)
         array.AddPadsToModule(self.draw)
 
-        #box
-        ssx = -pin1_pos.x + pads["outline x margin"]
-        ssy = -pin1_pos.y + pads["outline y margin"]
-
-        self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2,
-                                          pads["outline x margin"])
+        # Draw box outline on F.Fab layer
+        self.draw.SetLayer(pcbnew.F_Fab)
+        ssx = self.parameters['Package']['width'] / 2
+        ssy = self.parameters['Package']['length'] / 2
+
+        # Bevel should be 1mm nominal but we'll allow smaller values
+        if pcbnew.ToMM(ssx) < 1:
+            bevel = ssx
+        else:
+            bevel = pcbnew.FromMM(1)
+
+        # Box with 1mm bevel as per IPC7351C
+        self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, bevel)
+
+        # Add IPC markings to F_Silk layer
+        self.draw.SetLayer(pcbnew.F_SilkS)
+        offset = pcbnew.FromMM(0.15)
+        len_x  = 0.5 * ssx
+        len_y  = 0.5 * ssy
+
+        edge = [
+            [ ssx + offset - len_x, -ssy - offset],
+            [ ssx + offset, -ssy - offset],
+            [ ssx + offset, -ssy - offset + len_y],
+               ]
+
+        # Draw three square edges
+        self.draw.Polyline(edge)
+        self.draw.Polyline(edge, mirrorY=0)
+        self.draw.Polyline(edge, mirrorX=0, mirrorY=0)
+
+        # Draw pin-1 marker
+        bevel += offset
+        pin1 = [
+            [ -ssx - offset + len_x, -ssy - offset],
+            [ -ssx - offset + bevel, -ssy - offset],
+            [ -ssx - offset, -ssy - offset + bevel],
+            [ -ssx - offset, -ssy - offset + len_y],
+                ]
+
+        # Remove lines if the package is too small
+        if bevel > len_x:
+            pin1 = pin1[1:]
+
+        if bevel > len_y:
+            pin1 = pin1[:-1]
+
+        self.draw.Polyline(pin1)
+
+        # Draw a circle in the bevel void
+        self.draw.Circle( -ssx, -ssy, pcbnew.FromMM(0.2), filled=True)
 
         # Courtyard
-        cmargin = self.draw.GetLineThickness()
+        cmargin = self.parameters['Package']['margin']
         self.draw.SetLayer(pcbnew.F_CrtYd)
         sizex = (ssx + cmargin) * 2
         sizey = (ssy + cmargin) * 2
+
         # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
-        sizex = self.PutOnGridMM(sizex, 0.1)
-        sizey = self.PutOnGridMM(sizey, 0.1)
+        sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+        sizey = pcbnew.PutOnGridMM(sizey, 0.1)
+
         # set courtyard line thickness to the one defined in KLC
         self.draw.SetLineThickness(pcbnew.FromMM(0.05))
         self.draw.Box(0, 0, sizex, sizey)
diff --git a/pcbnew/python/plugins/circular_pad_array_wizard.py b/pcbnew/python/plugins/circular_pad_array_wizard.py
index 7a42c8f..42c08c9 100644
--- a/pcbnew/python/plugins/circular_pad_array_wizard.py
+++ b/pcbnew/python/plugins/circular_pad_array_wizard.py
@@ -18,11 +18,11 @@ from __future__ import division
 import math
 
 import pcbnew
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
 import PadArray as PA
 
 
-class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
+class circular_pad_array_wizard(FootprintWizardBase.FootprintWizard):
 
     def GetName(self):
         return "Circular Pad Array"
@@ -32,52 +32,84 @@ class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
 
     def GenerateParameterList(self):
 
-        self.AddParam("Pads", "n", self.uNatural, 6)
-        self.AddParam("Pads", "pad width", self.uMM, 1.5)
-        self.AddParam("Pads", "drill", self.uMM, 1)
-        self.AddParam("Pads", "circle diameter", self.uMM, 5)
-        self.AddParam("Pads", "first pad angle", self.uNatural, 0)
-        self.AddParam("Pads", "number clockwise", self.uBool, True)
-        self.AddParam("Pads", "first pad number", self.uNatural, 1)
+        self.AddParam("Pads", "count", self.uInteger, 6, min_value=1, designator='n')
+        self.AddParam("Pads", "center diameter", self.uMM, 5, min_value=0, designator='r', hint="Centre distance between pads")
+        self.AddParam("Pads", "diameter", self.uMM, 1.5)
+        self.AddParam("Pads", "drill", self.uMM, 0.8)
+        self.AddParam("Pads", "angle", self.uDegrees, 0, designator='a')
+
+        self.AddParam("Numbering", "initial", self.uInteger, 1, min_value=1)
+        #self.AddParam("Numbering", "increment", self.uInteger, 1, min_value=1)
+        self.AddParam("Numbering", "clockwise", self.uBool, True)
+
+        self.AddParam("Outline", "diameter", self.uMM, 7, designator='D')
+        self.AddParam("Outline", "margin", self.uMM, 0.25, min_value=0.2)
 
     def CheckParameters(self):
 
-        self.CheckParamInt("Pads", "*n")
-        self.CheckParamInt("Pads", "*first pad number")
-        self.CheckParamBool("Pads", "*number clockwise")
+        pads = self.parameters['Pads']
+        numbering = self.parameters['Numbering']
+        outline = self.parameters['Outline']
+
+        # Check that pads do not overlap
+        pad_dia = pcbnew.ToMM(pads['diameter'])
+        centres = pcbnew.ToMM(pads['center diameter'])
+        n_pads = pads['count']
+
+        self.CheckParam('Pads','diameter',max_value=centres*math.pi/n_pads,info="Pads overlap")
+
+        # Check that the pads fit inside the outline
+        d_min = pad_dia + centres
+
+        self.CheckParam("Outline","diameter",min_value=d_min, info="Outline diameter is too small")
+
 
     def GetValue(self):
-        pins = self.parameters["Pads"]["*n"]
+        pins = self.parameters["Pads"]["count"]
         return "CPA_%d" % pins
 
     def BuildThisFootprint(self):
 
-        prm = self.parameters['Pads']
+        pads = self.parameters['Pads']
+        numbering = self.parameters['Numbering']
+        outline = self.parameters['Outline']
 
-        pad_size = prm['pad width']
+        pad_size = pads['diameter']
 
-        pad = PA.PadMaker(self.module).THPad(
-            prm['pad width'], prm['pad width'], prm['drill'])
+        pad = PA.PadMaker(self.module).THPad(pads['diameter'], pads['diameter'], pads['drill'])
 
         array = PA.PadCircleArray(
-            pad, prm['*n'], prm['circle diameter'] / 2,
-            angle_offset=prm["*first pad angle"],
+            pad, pads['count'], pads['center diameter'] / 2,
+            angle_offset=pads["angle"],
             centre=pcbnew.wxPoint(0, 0),
-            clockwise=prm["*number clockwise"])
+            clockwise=numbering["clockwise"])
 
-        array.SetFirstPadInArray(prm["*first pad number"])
+        array.SetFirstPadInArray(numbering["initial"])
 
         array.AddPadsToModule(self.draw)
 
-        body_radius = (prm['circle diameter'] + prm['pad width'])/2 + self.draw.GetLineThickness()
+        # Draw the outline
+        body_radius = outline['diameter'] / 2
+        self.draw.SetLayer(pcbnew.F_Fab)
+        self.draw.GetLineThickness()
         self.draw.Circle(0, 0, body_radius)
 
+        #silkscreen
+        body_radius += pcbnew.FromMM(0.15)
+        self.draw.SetLayer(pcbnew.F_SilkS)
+        self.draw.Circle(0, 0, body_radius)
+
+        # courtyard
+        self.draw.SetLayer(pcbnew.F_CrtYd)
+        self.draw.SetLineThickness(pcbnew.FromMM(0.05))
+        self.draw.Circle(0, 0, body_radius + outline['margin'])
+
+        # Text size
+
         text_size = self.GetTextSize()  # IPC nominal
         thickness = self.GetTextThickness()
-        textposy = body_radius + self.draw.GetLineThickness()/2 + self.GetTextSize()/2 + thickness
+        textposy = body_radius + self.draw.GetLineThickness()/2 + self.GetTextSize()/2 + thickness + + outline['margin']
         self.draw.Value( 0, textposy, text_size )
         self.draw.Reference( 0, -textposy, text_size )
 
-
-
 circular_pad_array_wizard().register()
diff --git a/pcbnew/python/plugins/qfn_wizard.py b/pcbnew/python/plugins/qfn_wizard.py
index d6c3a61..f36165b 100644
--- a/pcbnew/python/plugins/qfn_wizard.py
+++ b/pcbnew/python/plugins/qfn_wizard.py
@@ -17,55 +17,80 @@
 from __future__ import division
 import pcbnew
 
-import HelpfulFootprintWizardPlugin as HFPW
+import pcbnew
+import FootprintWizardBase
 import PadArray as PA
 
-class QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
+class QFNWizard(FootprintWizardBase.FootprintWizard):
 
     def GetName(self):
         return "QFN"
 
     def GetDescription(self):
-        return "Quad Flat No-lead with Exposed Pad footprint wizard"
+        return "Quad Flat No-lead (QFN) footprint wizard"
 
     def GenerateParameterList(self):
-        self.AddParam("Pads", "n", self.uNatural, 100)
-        self.AddParam("Pads", "pad pitch", self.uMM, 0.5)
-        self.AddParam("Pads", "pad width", self.uMM, 0.25)
-        self.AddParam("Pads", "pad length", self.uMM, 1.5)
+
+        #TODO - Allow different number of pads in x and y directions
+
+        self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
+        self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
+        self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
+        self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
         self.AddParam("Pads", "oval", self.uBool, True)
-        self.AddParam("Pads", "thermal vias", self.uBool, True)
-        self.AddParam("Pads", "thermal vias drill", self.uMM, 0.3)
-        self.AddParam("Pads", "epad subdiv x", self.uNatural, 2)
-        self.AddParam("Pads", "epad subdiv y", self.uNatural, 2)
 
-        self.AddParam("Package", "package width", self.uMM, 14)
-        self.AddParam("Package", "package height", self.uMM, 14)
-        self.AddParam("Package", "courtyard margin", self.uMM, 1)
+        self.AddParam("EPad", "epad", self.uBool, True)
+        self.AddParam("EPad", "width", self.uMM, 10, designator="E2")
+        self.AddParam("EPad", "length", self.uMM, 10, designator="D2")
+        self.AddParam("EPad", "thermal vias", self.uBool, False)
+        self.AddParam("EPad", "thermal vias drill", self.uMM, 1, min_value=0.1)
+        self.AddParam("EPad", "x divisions", self.uInteger, 2, min_value=1)
+        self.AddParam("EPad", "y divisions", self.uInteger, 2, min_value=1)
+        self.AddParam("EPad", "paste margin", self.uMM, 0.1)
+
+        self.AddParam("Package", "width", self.uMM, 14, designator='E')
+        self.AddParam("Package", "height", self.uMM, 14, designator='D')
+        self.AddParam("Package", "margin", self.uMM, 0.25, minValue=0.2)
+
+    @property
+    def pads(self):
+        return self.parameters['Pads']
+
+    @property
+    def epad(self):
+        return self.parameters['EPad']
+
+    @property
+    def package(self):
+        return self.parameters['Package']
 
     def CheckParameters(self):
-        self.CheckParamInt("Pads", "*n", is_multiple_of=4)
-        self.CheckParamBool("Pads", "*oval")
-        self.CheckParamBool("Pads", "*thermal vias")
+        pass
 
     def GetValue(self):
-        return "QFN_%d" % self.parameters["Pads"]["*n"]
+
+        return "QFN-{n}_{ep}{x:g}x{y:g}_Pitch{p:g}mm".format(
+                n = self.pads['n'],
+                ep = "EP_" if self.epad['epad'] else '',
+                x = pcbnew.ToMM(self.package['width']),
+                y = pcbnew.ToMM(self.package['height']),
+                p = pcbnew.ToMM(self.pads['pitch'])
+                )
 
     def BuildThisFootprint(self):
-        pads = self.parameters["Pads"]
 
-        pad_pitch = pads["pad pitch"]
-        pad_length = pads["pad length"]
-        pad_width = pads["pad width"]
+        pad_pitch = self.pads["pitch"]
+        pad_length = self.pads["length"]
+        pad_width = self.pads["width"]
 
-        v_pitch = self.parameters["Package"]["package height"]
-        h_pitch = self.parameters["Package"]["package width"]
+        v_pitch = self.package["height"]
+        h_pitch = self.package["width"]
 
-        pads_per_row = pads["*n"] // 4
+        pads_per_row = int(self.pads["n"] // 4)
 
         row_len = (pads_per_row - 1) * pad_pitch
 
-        pad_shape = pcbnew.PAD_SHAPE_OVAL if pads["*oval"] else pcbnew.PAD_SHAPE_RECT
+        pad_shape = pcbnew.PAD_SHAPE_OVAL if self.pads["oval"] else pcbnew.PAD_SHAPE_RECT
 
         h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width,
                                                  shape=pad_shape, rot_degree=90.0)
@@ -97,79 +122,88 @@ class QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
         array.SetFirstPadInArray(3*pads_per_row + 1)
         array.AddPadsToModule(self.draw)
 
-        lim_x = self.parameters["Package"]["package width"] / 2
-        lim_y = self.parameters["Package"]["package height"] / 2
+        lim_x = self.package["width"] / 2
+        lim_y = self.package["height"] / 2
         inner = (row_len / 2) + pad_pitch
 
         # epad
-        epad_width = self.parameters["Package"]["package height"] - (2*pad_length)
-        epad_length  = self.parameters["Package"]["package width"] - (2*pad_length)
-        epad_subdv_x = pads["*epad subdiv x"]
-        epad_subdv_y = pads["*epad subdiv y"]
-        epad_via_drill = pads["thermal vias drill"]
-
-        if (epad_subdv_y != 0 and epad_subdv_x != 0) and (epad_subdv_y != 1 or epad_subdv_x != 1):
-          # Create the master pad (one area) on front solder mask, and perhaps of front copper layer
-          # at location 0,0
-          emasterpad = PA.PadMaker(self.module).SMDPad( epad_length, epad_width,
-                  shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0.0)
-          emasterpad.SetLayerSet(pcbnew.LSET(pcbnew.F_Mask))  # currently, only on solder mask
-          emasterpad.SetPadName(pads["*n"]+1)
-          self.module.Add(emasterpad)
-
-          px = pcbnew.FromMM(0.1); py = pcbnew.FromMM(0.1)
-          esubpad_size_x = epad_length / epad_subdv_x - px
-          esubpad_size_y = epad_width / epad_subdv_y - py
-          epad1_pos = pcbnew.wxPoint(-(esubpad_size_x*(epad_subdv_x-1)/2), -esubpad_size_y*(epad_subdv_y-1)/2)
-          epad = PA.PadMaker(self.module).SMDPad( esubpad_size_y, esubpad_size_x,
-                  shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0.0)
-          array = PA.EPADGridArray(epad, epad_subdv_x, epad_subdv_y, esubpad_size_x + px, esubpad_size_y + py, pcbnew.wxPoint(0,0))
-          array.SetFirstPadInArray(pads["*n"]+1)
-          array.AddPadsToModule(self.draw)
-          if pads["*thermal vias"]:
-            via_diam = min(esubpad_size_y, esubpad_size_x)/3.
-            thpad = PA.PadMaker(self.module).THRoundPad(via_diam, min(via_diam/2, epad_via_drill))
-            layerset = pcbnew.LSET.AllCuMask()
-            layerset.AddLayer(pcbnew.B_Mask)
-            layerset.AddLayer(pcbnew.F_Mask)
-            thpad.SetLayerSet(layerset)
-            array2 = PA.EPADGridArray(thpad, epad_subdv_x, epad_subdv_y, esubpad_size_x + px, esubpad_size_y + py, pcbnew.wxPoint(0,0))
-            array2.SetFirstPadInArray(pads["*n"]+1)
-            array2.AddPadsToModule(self.draw)
-        else:
-          epad = PA.PadMaker(self.module).SMDPad(epad_length, epad_width)
-          epad_pos = pcbnew.wxPoint(0,0)
-          array = PA.PadLineArray(epad, 1, 1, False, epad_pos)
-          array.SetFirstPadInArray(pads["*n"]+1)
-          array.AddPadsToModule(self.draw)
-          if pads["*thermal vias"]:
-            via_diam = min(epad_length, epad_width)/3.
-            thpad = PA.PadMaker(self.module).THRoundPad( via_diam, min(via_diam/2, epad_via_drill))
-            layerset = pcbnew.LSET.AllCuMask()
-            layerset.AddLayer(pcbnew.B_Mask)
-            layerset.AddLayer(pcbnew.F_Mask)
-            thpad.SetLayerSet(layerset)
-            array2 = PA.PadLineArray(thpad, 1, 1, False, epad_pos)
-            array2.SetFirstPadInArray(pads["*n"]+1)
-            array2.AddPadsToModule(self.draw)
-
-        #top left - diagonal
-        self.draw.Line(-lim_x, -inner, -inner, -lim_y)
-        # top right
-        self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)])
-        # bottom left
-        self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)])
-        # bottom right
-        self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)])
+        epad_width   = self.epad["width"]
+        epad_length  = self.epad["length"]
+
+        epad_ny = self.epad["x divisions"]
+        epad_nx = self.epad["y divisions"]
+
+        epad_via_drill = self.epad["thermal vias drill"]
+
+        # Create a central exposed pad?
+        if self.epad['epad'] == True:
+
+            epad_num = self.pads['n'] + 1
+
+            epad_w = epad_length / epad_nx
+            epad_l = epad_width / epad_ny
+
+            # Create the epad
+            epad = PA.PadMaker(self.module).SMDPad( epad_w, epad_l, shape=pcbnew.PAD_SHAPE_RECT )
+            epad.SetLocalSolderPasteMargin( -1 * self.epad['paste margin'] )
+            # set pad layers
+            layers = pcbnew.LSET(pcbnew.F_Mask)
+            layers.AddLayer(pcbnew.F_Cu)
+            layers.AddLayer(pcbnew.F_Paste)
+            epad.SetPadName(epad_num)
+
+            array = PA.EPADGridArray( epad, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
+            array.SetFirstPadInArray(epad_num)
+            array.AddPadsToModule(self.draw)
+
+            if self.epad['thermal vias']:
+
+                # create the thermal via
+                via_diam = min(epad_w, epad_l) / 2
+                via_drill = min(via_diam / 2, epad_via_drill)
+                via = PA.PadMaker(self.module).THRoundPad(via_diam, via_drill)
+                layers = pcbnew.LSET.AllCuMask()
+                layers.AddLayer(pcbnew.B_Mask)
+                layers.AddLayer(pcbnew.F_Mask)
+                via.SetLayerSet(layers)
+
+                via_array = PA.EPADGridArray(via, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
+                via_array.SetFirstPadInArray(epad_num)
+                via_array.AddPadsToModule(self.draw)
+
+        # Draw the package outline on the F.Fab layer
+        bevel = min( pcbnew.FromMM(1.0), self.package['width']/2, self.package['height']/2 )
+
+        self.draw.SetLayer(pcbnew.F_Fab)
+
+        w = self.package['width']
+        h = self.package['height']
+
+        self.draw.BoxWithDiagonalAtCorner(0, 0, w, h, bevel)
+
+        # Silkscreen
+        self.draw.SetLayer(pcbnew.F_SilkS)
+
+        offset = self.draw.GetLineThickness()
+        clip = row_len / 2 + self.pads['pitch']
+
+        self.draw.Polyline( [ [ clip, -h/2-offset], [ w/2+offset,-h/2-offset], [ w/2+offset, -clip] ] ) # top right
+        self.draw.Polyline( [ [ clip,  h/2+offset], [ w/2+offset, h/2+offset], [ w/2+offset,  clip] ] ) # bottom right
+        self.draw.Polyline( [ [-clip,  h/2+offset], [-w/2-offset, h/2+offset], [-w/2-offset,  clip] ] ) # bottom left
+
+        # Add pin-1 indication as per IPC-7351C
+        self.draw.Line(-clip, -h/2-offset, -w/2-pad_length/2, -h/2-offset)
 
         # Courtyard
-        cmargin = self.parameters["Package"]["courtyard margin"]
+        cmargin = self.package["margin"]
         self.draw.SetLayer(pcbnew.F_CrtYd)
-        sizex = (lim_x + cmargin) * 2 + pad_length/2.
-        sizey = (lim_y + cmargin) * 2 + pad_length/2.
+
+        sizex = (lim_x + cmargin) * 2 + pad_length
+        sizey = (lim_y + cmargin) * 2 + pad_length
+
         # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
-        sizex = self.PutOnGridMM(sizex, 0.1)
-        sizey = self.PutOnGridMM(sizey, 0.1)
+        sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+        sizey = pcbnew.PutOnGridMM(sizey, 0.1)
         # set courtyard line thickness to the one defined in KLC
         thick = self.draw.GetLineThickness()
         self.draw.SetLineThickness(pcbnew.FromMM(0.05))
diff --git a/pcbnew/python/plugins/qfp_wizard.py b/pcbnew/python/plugins/qfp_wizard.py
index 04664aa..4c18e89 100644
--- a/pcbnew/python/plugins/qfp_wizard.py
+++ b/pcbnew/python/plugins/qfp_wizard.py
@@ -17,53 +17,64 @@
 from __future__ import division
 import pcbnew
 
-import HelpfulFootprintWizardPlugin
+import FootprintWizardBase
 import PadArray as PA
 
-
-class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
+class QFPWizard(FootprintWizardBase.FootprintWizard):
 
     def GetName(self):
         return "QFP"
 
     def GetDescription(self):
-        return "Quad Flat Package footprint wizard"
+        return "Quad Flat Package (QFP) footprint wizard"
 
     def GenerateParameterList(self):
-        self.AddParam("Pads", "n", self.uNatural, 100)
-        self.AddParam("Pads", "pad pitch", self.uMM, 0.5)
-        self.AddParam("Pads", "pad width", self.uMM, 0.25)
-        self.AddParam("Pads", "pad length", self.uMM, 1.5)
-        self.AddParam("Pads", "vertical pitch", self.uMM, 15)
-        self.AddParam("Pads", "horizontal pitch", self.uMM, 15)
+        self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
+        self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
+        self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
+        self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
+        self.AddParam("Pads", "horizontal spacing", self.uMM, 15, designator='C1')
+        self.AddParam("Pads", "vertical spacing", self.uMM, 15, designator='C2')
         self.AddParam("Pads", "oval", self.uBool, True)
 
-        self.AddParam("Package", "package width", self.uMM, 14)
-        self.AddParam("Package", "package height", self.uMM, 14)
-        self.AddParam("Package", "courtyard margin", self.uMM, 1)
+        self.AddParam("Package", "width", self.uMM, 14, designator='D1')
+        self.AddParam("Package", "height", self.uMM, 14, designator='E1')
+        self.AddParam("Package", "courtyard margin", self.uMM, 0.25, min_value=0.2)
+
+    @property
+    def pads(self):
+        return self.parameters['Pads']
+
+    @property
+    def package(self):
+        return self.parameters['Package']
 
     def CheckParameters(self):
-        self.CheckParamInt("Pads", "*n", is_multiple_of=4)
-        self.CheckParamBool("Pads", "*oval")
+        # todo - custom checking
+        pass
 
     def GetValue(self):
-        return "QFP_%d" % self.parameters["Pads"]["*n"]
+        return "QFP-{n}_{x:g}x{y:g}_Pitch{p:g}mm".format(
+            n = self.pads['n'],
+            x = pcbnew.ToMM(self.package['width']),
+            y = pcbnew.ToMM(self.package['height']),
+            p = pcbnew.ToMM(self.pads['pitch'])
+            )
 
     def BuildThisFootprint(self):
-        pads = self.parameters["Pads"]
 
-        pad_pitch = pads["pad pitch"]
-        pad_length = self.parameters["Pads"]["pad length"]
-        pad_width = self.parameters["Pads"]["pad width"]
+        pad_pitch = self.pads["pitch"]
+        pad_length = self.pads["length"]
+        pad_width = self.pads["width"]
 
-        v_pitch = pads["vertical pitch"]
-        h_pitch = pads["horizontal pitch"]
+        v_pitch = self.pads["vertical spacing"]
+        h_pitch = self.pads["horizontal spacing"]
 
-        pads_per_row = pads["*n"] // 4
+        pads_per_row = int(self.pads["n"] // 4)
 
         row_len = (pads_per_row - 1) * pad_pitch
 
-        pad_shape = pcbnew.PAD_SHAPE_OVAL if pads["*oval"] else pcbnew.PAD_SHAPE_RECT
+        pad_shape = pcbnew.PAD_SHAPE_OVAL if self.pads["oval"] else pcbnew.PAD_SHAPE_RECT
 
         h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width,
                                                  shape=pad_shape, rot_degree=90.0)
@@ -95,27 +106,49 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
         array.SetFirstPadInArray(3*pads_per_row + 1)
         array.AddPadsToModule(self.draw)
 
-        lim_x = self.parameters["Package"]["package width"] / 2
-        lim_y = self.parameters["Package"]["package height"] / 2
+        offset = pcbnew.FromMM(0.15)
+
+        x = self.parameters["Package"]["width"] / 2 + offset
+        y = self.parameters["Package"]["height"] / 2 + offset
         inner = (row_len / 2) + pad_pitch
 
-        #top left - diagonal
-        self.draw.Line(-lim_x, -inner, -inner, -lim_y)
+        # Add outline to F_Fab layer
+        self.draw.SetLayer(pcbnew.F_Fab)
+
+        bevel = min( pcbnew.FromMM(1.0), self.package['width']/2, self.package['height']/2 )
+
+        w = self.package['width']
+        h = self.package['height']
+
+        # outermost limits of pins
+        right_edge = (h_pitch + pad_length) / 2
+        left_edge = -right_edge
+
+        bottom_edge = (v_pitch + pad_length) / 2
+        top_edge = -bottom_edge
+
+        self.draw.BoxWithDiagonalAtCorner(0, 0, w, h, bevel)
+
+        # Draw silkscreen
+        self.draw.SetLayer(pcbnew.F_SilkS)
+
+        #top left - as per IPC-7351C
+        self.draw.Polyline([(-inner, -y), (-x, -y), (-x, -inner), (left_edge, -inner)])
         # top right
-        self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)])
+        self.draw.Polyline([(inner, -y), (x, -y), (x, -inner)])
         # bottom left
-        self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)])
+        self.draw.Polyline([(-inner, y), (-x, y), (-x, inner)])
         # bottom right
-        self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)])
+        self.draw.Polyline([(inner, y), (x, y), (x, inner)])
 
         # Courtyard
         cmargin = self.parameters["Package"]["courtyard margin"]
         self.draw.SetLayer(pcbnew.F_CrtYd)
-        sizex = (lim_x + cmargin) * 2 + pad_length
-        sizey = (lim_y + cmargin) * 2 + pad_length
+        sizex = (right_edge + cmargin) * 2
+        sizey = (bottom_edge + cmargin) * 2
         # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
-        sizex = self.PutOnGridMM(sizex, 0.1)
-        sizey = self.PutOnGridMM(sizey, 0.1)
+        sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+        sizey = pcbnew.PutOnGridMM(sizey, 0.1)
         # set courtyard line thickness to the one defined in KLC
         thick = self.draw.GetLineThickness()
         self.draw.SetLineThickness(pcbnew.FromMM(0.05))
diff --git a/pcbnew/python/plugins/sdip_wizard.py b/pcbnew/python/plugins/sdip_wizard.py
index fad99f1..ce7afb3 100644
--- a/pcbnew/python/plugins/sdip_wizard.py
+++ b/pcbnew/python/plugins/sdip_wizard.py
@@ -17,7 +17,7 @@
 from __future__ import division
 import pcbnew
 
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
 import PadArray as PA
 
 
@@ -35,7 +35,7 @@ class RowedGridArray(PA.PadGridArray):
             return x+1
 
 
-class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
+class RowedFootprint(FootprintWizardBase.FootprintWizard):
 
     pad_count_key = 'pad count'
     row_count_key = 'row count'
@@ -50,31 +50,25 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
 
     def GenerateParameterList(self):
         # defaults for a DIP package
-        self.AddParam("Pads", self.pad_count_key, self.uNatural, 24)
-        self.AddParam("Pads", self.row_count_key, self.uNatural, 2)
+        self.AddParam("Pads", self.pad_count_key, self.uInteger, 24)
+        self.AddParam("Pads", self.row_count_key, self.uInteger, 2, min_value=1, max_value=2)
 
         self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False)
         self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5)
         self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5)
 
     def CheckParameters(self):
-        self.CheckParamInt("Pads", '*' + self.row_count_key, min_value=1, max_value=2)
-        self.CheckParamInt(
-            "Pads", '*' + self.pad_count_key,
-            is_multiple_of=self.parameters["Pads"]['*' + self.row_count_key])
-
-        # can do this internally to parameter manager?
-        self.CheckParamBool("Body", '*' + self.silkscreen_inside_key)
+        self.CheckParam("Pads", self.pad_count_key, multiple=self.parameters['Pads'][self.row_count_key], info='Pads must be multiple of row count')
 
     def BuildThisFootprint(self):
         pads = self.parameters["Pads"]
         body = self.parameters["Body"]
-        num_pads = pads['*' + self.pad_count_key]
+        num_pads = pads[self.pad_count_key]
         pad_length = pads[self.pad_length_key]
         pad_width = pads[self.pad_width_key]
         row_pitch = pads[self.row_spacing_key]
         pad_pitch = pads[self.pad_pitch_key]
-        num_rows = pads['*' + self.row_count_key]
+        num_rows = pads[self.row_count_key]
 
         pads_per_row = num_pads // num_rows
 
@@ -96,7 +90,7 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
         ssx_offset = -pad_width / 2 - body[self.outline_x_margin_key]
         ssy_offset = -pad_length / 2 - body[self.outline_y_margin_key]
 
-        if body['*' + self.silkscreen_inside_key]:
+        if body[self.silkscreen_inside_key]:
             ssy_offset *= -1
 
         ssx = -pin1_posX - ssx_offset
@@ -110,8 +104,8 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
         sizex = (ssx + cmargin) * 2
         sizey = (ssy + cmargin) * 2
         # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
-        sizex = self.PutOnGridMM(sizex, 0.1)
-        sizey = self.PutOnGridMM(sizey, 0.1)
+        sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+        sizey = pcbnew.PutOnGridMM(sizey, 0.1)
         # set courtyard line thickness to the one defined in KLC
         self.draw.SetLineThickness(pcbnew.FromMM(0.05))
         self.draw.Box(0, 0, sizex, sizey)
@@ -156,8 +150,8 @@ class SDIPWizard(RowedFootprint):
 
     def GetValue(self):
         pads = self.parameters["Pads"]
-        rows = pads['*' + self.row_count_key]
-        pad_count = pads['*' + self.pad_count_key]
+        rows = pads[self.row_count_key]
+        pad_count = pads[self.pad_count_key]
         row_dist_mil = pcbnew.Iu2Mils(int(self.parameters["Pads"][self.row_spacing_key])) #int(self.parameters["Pads"][self.row_spacing_key] / 2.54 * 100)
         pad_shape = ""
 
@@ -185,7 +179,7 @@ class SDIPWizard(RowedFootprint):
 
     def DrawBox(self, ssx, ssy):
 
-        if self.parameters["Pads"]['*' + self.row_count_key] == 2:
+        if self.parameters["Pads"][self.row_count_key] == 2:
 
             #  ----------
             #  |8 7 6 5 |
@@ -208,7 +202,7 @@ class SDIPWizard(RowedFootprint):
 
             #line between pin1 and pin2
             pad_pitch = self.parameters["Pads"][self.pad_pitch_key]
-            pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key]
+            pad_cnt = self.parameters["Pads"][self.pad_count_key]
             line_x = ( pad_cnt/2 - 1) * pad_pitch
             self.draw.VLine(-line_x, -ssy, ssy * 2)
 
@@ -226,7 +220,7 @@ class SOICWizard(RowedFootprint):
         return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard"
 
     def GetValue(self):
-        pad_count = self.parameters["Pads"]['*' + self.pad_count_key]
+        pad_count = self.parameters["Pads"][self.pad_count_key]
         return "%s-%d" % ("SOIC", pad_count)
 
     def GenerateParameterList(self):
diff --git a/pcbnew/python/plugins/touch_slider_wizard.py b/pcbnew/python/plugins/touch_slider_wizard.py
index e27fffb..7fc300a 100644
--- a/pcbnew/python/plugins/touch_slider_wizard.py
+++ b/pcbnew/python/plugins/touch_slider_wizard.py
@@ -24,10 +24,10 @@
 #
 
 from pcbnew import *
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
+import pcbnew
 
-
-class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
+class TouchSliderWizard(FootprintWizardBase.FootprintWizard):
 
     def GetName(self):
         """
@@ -44,16 +44,23 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
         return 'Capacitive Touch Slider wizard'
 
     def GetValue(self):
-        steps = int(self.parameters["Pads"]["*steps"])
-        return "TS"+str(steps)
+        return "TouchSlider-{s}_{x:g}x{y:g}mm".format(
+            s = self.pads['steps'],
+            x = pcbnew.ToMM(self.pads['length']),
+            y = pcbnew.ToMM(self.pads['width'])
+            )
 
     def GenerateParameterList(self):
-        self.AddParam("Pads", "steps", self.uNatural, 4)
-        self.AddParam("Pads", "bands", self.uNatural, 2)
+        self.AddParam("Pads", "steps", self.uInteger, 4, min_value=2)
+        self.AddParam("Pads", "bands", self.uInteger, 2, min_value=1)
         self.AddParam("Pads", "width", self.uMM, 10)
         self.AddParam("Pads", "length", self.uMM, 50)
         self.AddParam("Pads", "clearance", self.uMM, 1)
 
+    @property
+    def pads(self):
+        return self.parameters['Pads']
+
     # build a rectangular pad
     def smdRectPad(self,module,size,pos,name):
         pad = D_PAD(module)
@@ -82,18 +89,8 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
 
     # This method checks the parameters provided to wizard and set errors
     def CheckParameters(self):
-        prms = self.parameters["Pads"]
-        steps = prms["*steps"]
-        bands = prms["*bands"]
-
-        if steps < 1:
-            self.parameter_errors["Pads"]["*steps"]="steps must be positive"
-        if bands < 1:
-            self.parameter_errors["Pads"]["*bands"]="bands must be positive"
-
-        touch_width       = prms["width"]
-        touch_length      = prms["length"]
-        touch_clearance   = prms["clearance"]
+        #TODO - implement custom checks
+        pass
 
     # The start pad is made of a rectangular pad plus a couple of
     # triangular pads facing tips on the middle/right of the first
@@ -177,18 +174,18 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
     # build the footprint from parameters
     # FIX ME: the X and Y position of the footprint can be better.
     def BuildThisFootprint(self):
-        prm = self.parameters["Pads"]
-        steps             = int(prm["*steps"])
-        bands             = int(prm["*bands"])
-        touch_width       = prm["width"]
-        touch_length      = prm["length"]
-        touch_clearance   = prm["clearance"]
+
+        steps             = self.pads["steps"]
+        bands             = self.pads["bands"]
+        touch_width       = self.pads["width"]
+        touch_length      = self.pads["length"]
+        touch_clearance   = self.pads["clearance"]
 
         step_length = float(touch_length) / float(steps)
 
         t_size = self.GetTextSize()
         w_text = self.draw.GetLineThickness()
-        ypos = touch_width/(bands*2) + t_size/2 + w_text
+        ypos = touch_width/2 + t_size/2 + w_text
         self.draw.Value(0, -ypos, t_size)
         ypos += t_size + w_text*2
         self.draw.Reference(0, -ypos, t_size)
@@ -197,9 +194,13 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
         self.module.SetAttributes(MOD_CMS)
 
         # starting pad
-        pos = wxPointMM(0,0)
         band_width = touch_width/bands
 
+        xpos = -0.5 * (steps - 1) * step_length
+        ypos = -0.5 * (bands - 1) * band_width
+
+        pos = wxPointMM(pcbnew.ToMM(xpos), pcbnew.ToMM(ypos))
+
         for b in range(bands):
             self.AddStrip(pos,steps,band_width,step_length,touch_clearance)
             pos += wxPoint(0,band_width)
diff --git a/pcbnew/python/plugins/uss39_barcode.py b/pcbnew/python/plugins/uss39_barcode.py
index ee86775..1c3b234 100644
--- a/pcbnew/python/plugins/uss39_barcode.py
+++ b/pcbnew/python/plugins/uss39_barcode.py
@@ -15,8 +15,7 @@
 
 from __future__ import division
 import pcbnew as B
-
-import HelpfulFootprintWizardPlugin
+import FootprintWizardBase
 
 '''
 Created on Jan 16, 2015
@@ -49,7 +48,7 @@ class Uss39:
         # Reformated text with start and end characters
         return reduce(lambda a1, a2: a1 + [0] + a2, [map(int, ptd[c]) for c in ("*%s*" % self.makePrintable(text))])
 
-class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
+class Uss39Wizard(FootprintWizardBase.FootprintWizard):
     GetName = lambda self: 'BARCODE USS-39'
     GetDescription = lambda self: 'USS-39 Barcode'
     GetReferencePrefix = lambda self: 'BARCODE'
@@ -61,18 +60,20 @@ class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
         self.AddParam("Barcode", "Height", self.uMM, 3.0)
         self.AddParam("Barcode", "Margin", self.uMM, 2.0)
         self.AddParam("Barcode", "Contents", self.uString, 'BARCODE')
+
         self.AddParam("Caption", "Enabled", self.uBool, True)
         self.AddParam("Caption", "Height", self.uMM, 1.2)
         self.AddParam("Caption", "Thickness", self.uMM, 0.12)
 
     def CheckParameters(self):
+
         # Reset constants
         self.CourtyardLineWidth = B.FromMM(0.05)
         # Set bar height to the greater of 6.35mm or 0.15*L
         # Set quiet width to 10*X
         # User-defined parameters
         # Create barcode object
-        self.Barcode = Uss39('=' + str(self.parameters['Barcode']['*Contents']))
+        self.Barcode = Uss39('=' + str(self.parameters['Barcode']['Contents']))
         self.X = int(self.parameters['Barcode']['Pixel Width'])
         self.module.Value().SetText( str(self.Barcode) )
         self.C = len(str(self.Barcode))
@@ -146,4 +147,4 @@ class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
         self.draw.Circle(0, 0, B.FromMM(0.25))
         self.module.Value().SetLayer(B.F_Fab)
 
-Uss39Wizard().register()
+Uss39Wizard().register()
\ No newline at end of file
diff --git a/pcbnew/python/plugins/zip_wizard.py b/pcbnew/python/plugins/zip_wizard.py
index 4ffa7f1..e348617 100644
--- a/pcbnew/python/plugins/zip_wizard.py
+++ b/pcbnew/python/plugins/zip_wizard.py
@@ -17,52 +17,41 @@
 from __future__ import division
 import pcbnew
 
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
 import PadArray as PA
 
 
-class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
-
-    pad_count_key = '#pad count'
-    line_count_key = '#line count'
-    pad_vertical_size_key = 'pad vertical size'
-    pad_horizontal_size_key = 'pad horizontal size'
-    line_spacing_key = 'line spacing'
-    pad_pitch_key = 'pad pitch'
-    drill_size_key = 'drill size'
-
-    courtyard_x_margin_key = 'courtyard x margin'
-    courtyard_y_margin_key = 'courtyard y margin'
-    outline_x_margin_key = 'outline x margin'
-    outline_y_margin_key = 'outline y margin'
-    silkscreen_inside_key = 'silk screen inside'
+class RowedFootprint(FootprintWizardBase.FootprintWizard):
 
     def GenerateParameterList(self):
 
         # defaults for a ZIP package
-        self.AddParam("Pads", self.pad_count_key, self.uNatural, 24)
-        self.AddParam("Pads", self.line_count_key, self.uNatural, 2)
-        self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False)
-        self.AddParam("Body", self.courtyard_x_margin_key, self.uMM, 1)
-        self.AddParam("Body", self.courtyard_y_margin_key, self.uMM, 1)
+        self.AddParam("Pads", "pad count", self.uInteger, 24)
+        self.AddParam("Pads", "line count", self.uInteger, 2)
 
-    def CheckParameters(self):
-        self.CheckParamInt("Pads", '*' + self.pad_count_key)
-        self.CheckParamInt("Pads", '*' + self.line_count_key)
+        self.AddParam("Body", "silkscreen inside", self.uBool, False)
+        self.AddParam("Body", "courtyard margin", self.uMM, 0.5, min_value=0.2)
+
+    @property
+    def pads(self):
+        return self.parameters['Pads']
 
-        # can do this internally to parameter manager?
-        self.CheckParamBool("Body", '*' + self.silkscreen_inside_key)
+    @property
+    def body(self):
+        return self.parameters['Body']
+
+    def CheckParameters(self):
+        # TODO - implement custom checks
+        pass
 
     def BuildThisFootprint(self):
-        pads = self.parameters["Pads"]
-        body = self.parameters["Body"]
 
-        pad_count = pads['*' + self.pad_count_key]
-        pad_Vsize = pads[self.pad_vertical_size_key]
-        pad_Hsize = pads[self.pad_horizontal_size_key]
-        line_pitch = pads[self.line_spacing_key]
-        pad_pitch = pads[self.pad_pitch_key]
-        line_count = pads['*' + self.line_count_key]
+        pad_count = self.pads['pad count']
+        pad_Vsize = self.pads['pad height']
+        pad_Hsize = self.pads['pad width']
+        line_pitch = self.pads['line spacing']
+        pad_pitch = self.pads['pitch']
+        line_count = self.pads['line count']
 
         if line_count == 1:
             singleline = True
@@ -74,12 +63,12 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
 
         array = PA.PadZGridArray(pad, pad_count, line_count, line_pitch, pad_pitch)
         array.AddPadsToModule(self.draw)
-         
+
         # draw the Silk Screen
         pads_per_line = pad_count // line_count
         row_length = pad_pitch * (pads_per_line - 1)  # fenceposts
-        ssx_offset = pad_Hsize / 2 + body[self.outline_x_margin_key]
-        ssy_offset = pad_Vsize / 2 + body[self.outline_y_margin_key]
+        ssx_offset = pad_Hsize / 2 + self.body['outline x margin']
+        ssy_offset = pad_Vsize / 2 + self.body['outline y margin']
 
         pin1posX = pad_pitch * (pad_count - 1) / 2
         pin1posY = line_pitch * (line_count - 1) / 2
@@ -91,7 +80,7 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
         # body inside pads is possible only for 2 rows.
         # for other values, there is no room
         linew = self.draw.GetLineThickness()
-        if body['*'+self.silkscreen_inside_key] and line_count == 2:
+        if self.body['silkscreen inside'] and line_count == 2:
             cornery = pin1posY - ssy_offset
             if cornery < linew:
                 cornery = linew
@@ -99,15 +88,15 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
         self.DrawBox(leftx*2, cornery*2)
 
         # Courtyard
-        cmarginx = body[self.courtyard_x_margin_key]
-        cmarginy = body[self.courtyard_y_margin_key]
+        cmarginx = self.body['courtyard margin']
+        cmarginy = cmarginx
         self.draw.SetLayer(pcbnew.F_CrtYd)
         thick = self.draw.GetLineThickness()
         sizex = (pin1posX + cmarginx) * 2 + pad_Hsize + thick
         sizey = (pin1posY + cmarginy) * 2 + pad_Vsize + thick
         # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
-        sizex = self.PutOnGridMM(sizex, 0.1)
-        sizey = self.PutOnGridMM(sizey, 0.1)
+        sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+        sizey = pcbnew.PutOnGridMM(sizey, 0.1)
         # set courtyard line thickness to the one defined in KLC
         self.draw.SetLineThickness(pcbnew.FromMM(0.05))
         self.draw.Box(0, 0, sizex, sizey)
@@ -152,17 +141,17 @@ class ZIPWizard(RowedFootprint):
     def GenerateParameterList(self):
         RowedFootprint.GenerateParameterList(self)
 
-        self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27)
-        self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 1.2)
-        self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 2)
-        self.AddParam("Pads", self.line_spacing_key, self.uMM, 2.54)
-        self.AddParam("Pads", self.drill_size_key, self.uMM, 0.8)
-        self.AddParam("Body", self.outline_x_margin_key, self.uMM, 1)
-        self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5)
+        self.AddParam("Pads", "pitch", self.uMM, 1.27)
+        self.AddParam("Pads", "pad width", self.uMM, 1.2)
+        self.AddParam("Pads", "pad height", self.uMM, 2)
+        self.AddParam("Pads", "line spacing", self.uMM, 2.54)
+        self.AddParam("Pads", "drill size", self.uMM, 0.8)
+        self.AddParam("Body", 'outline x margin', self.uMM, 1)
+        self.AddParam("Body", 'outline y margin', self.uMM, 0.5)
 
     def GetValue(self):
-        rows = self.parameters["Pads"]['*' + self.line_count_key]
-        pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key]
+        rows = self.pads['line count']
+        pad_cnt = self.pads['pad count']
 
         if rows == 1:
             name = "SIP"
@@ -174,9 +163,9 @@ class ZIPWizard(RowedFootprint):
         return "%s-%d" % (name, pad_cnt)
 
     def GetPad(self):
-        pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key]
-        pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key]
-        drill = self.parameters["Pads"][self.drill_size_key]
+        pad_Vsize = self.pads['pad height']
+        pad_Hsize = self.pads['pad width']
+        drill = self.pads['drill size']
         return PA.PadMaker(self.module).THPad(
             pad_Vsize, pad_Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL)
 
@@ -192,23 +181,23 @@ class ZOICWizard(RowedFootprint):
         return "ZOIC, etc, Footprint Wizard"
 
     def GetValue(self):
-        return "%s-%d" % ("ZOIC", self.parameters["Pads"]['*' + self.pad_count_key])
+        return "%s-%d" % ("ZOIC-", self.pads['pad count'])
 
     def GenerateParameterList(self):
         RowedFootprint.GenerateParameterList(self)
 
         #and override some of them
-        self.AddParam("Pads", self.pad_pitch_key, self.uMM, 0.6)
-        self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 0.6)
-        self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 1.8)
-        self.AddParam("Pads", self.line_spacing_key, self.uMM, 5.2)
+        self.AddParam("Pads", "pitch", self.uMM, 0.6)
+        self.AddParam("Pads", "pad width", self.uMM, 0.6)
+        self.AddParam("Pads", "pad height", self.uMM, 1.8)
+        self.AddParam("Pads", "line spacing", self.uMM, 5.2)
 
-        self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5)
-        self.AddParam("Body", self.outline_y_margin_key, self.uMM, 1)
+        self.AddParam("Body", "outline x margin", self.uMM, 0.5)
+        self.AddParam("Body", "outline y margin", self.uMM, 1)
 
     def GetPad(self):
-        pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key]
-        pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key]
+        pad_Vsize = self.pads['pad height']
+        pad_Hsize = self.pads['pad width']
         return PA.PadMaker(self.module).SMDPad(
             pad_Vsize, pad_Hsize, shape=pcbnew.PAD_SHAPE_RECT)
 
diff --git a/pcbnew/swig/pcbnew_footprint_wizards.cpp b/pcbnew/swig/pcbnew_footprint_wizards.cpp
index e0f3854..3070b7a 100644
--- a/pcbnew/swig/pcbnew_footprint_wizards.cpp
+++ b/pcbnew/swig/pcbnew_footprint_wizards.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@xxxxxx>
- * Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors.
+ * Copyright (C) 2016 KiCad Developers, see CHANGELOG.TXT for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -237,24 +237,9 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterTypes( int aPage )
 
     PyObject*       arglist = Py_BuildValue( "(i)", aPage );
 
-    ret = CallRetArrayStrMethod( "GetParameterNames", arglist );
+    ret = CallRetArrayStrMethod( "GetParameterTypes", arglist );
     Py_DECREF( arglist );
 
-    for( unsigned i = 0; i<ret.GetCount(); i++ )
-    {
-        wxString    rest;
-        wxString    item = ret[i];
-
-        if( item.StartsWith( wxT( "*" ), &rest ) )
-        {
-            ret[i] = wxT( "UNITS" );    // units
-        }
-        else
-        {
-            ret[i] = wxT( "IU" );       // internal units
-        }
-    }
-
     return ret;
 }
 
@@ -284,6 +269,29 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterErrors( int aPage )
     return ret;
 }
 
+wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterHints( int aPage )
+{
+	PyLOCK          lock;
+
+	PyObject*       arglist = Py_BuildValue( "(i)", aPage );
+	wxArrayString   ret = CallRetArrayStrMethod( "GetParameterHints", arglist );
+
+	Py_DECREF( arglist );
+
+	return ret;
+}
+
+wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterDesignators( int aPage )
+{
+	PyLOCK          lock;
+
+	PyObject*       arglist = Py_BuildValue( "(i)", aPage );
+	wxArrayString   ret = CallRetArrayStrMethod( "GetParameterDesignators", arglist );
+
+	Py_DECREF( arglist );
+
+	return ret;
+}
 
 wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString& aValues )
 {
@@ -309,6 +317,13 @@ wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString&
     return res;
 }
 
+void PYTHON_FOOTPRINT_WIZARD::ResetParameters()
+{
+	PyLOCK 	lock;
+
+	CallMethod( "ResetWizard", NULL );
+}
+
 
 // this is a SWIG function declaration -from module.i
 MODULE* PyModule_to_MODULE( PyObject* obj0 );
diff --git a/pcbnew/swig/pcbnew_footprint_wizards.h b/pcbnew/swig/pcbnew_footprint_wizards.h
index 0027639..e18eed3 100644
--- a/pcbnew/swig/pcbnew_footprint_wizards.h
+++ b/pcbnew/swig/pcbnew_footprint_wizards.h
@@ -45,6 +45,7 @@ class PYTHON_FOOTPRINT_WIZARD : public FOOTPRINT_WIZARD
 public:
     PYTHON_FOOTPRINT_WIZARD( PyObject* wizard );
     ~PYTHON_FOOTPRINT_WIZARD();
+
     wxString        GetName() override;
     wxString        GetImage() override;
     wxString        GetDescription() override;
@@ -58,6 +59,10 @@ public:
     wxString        SetParameterValues( int aPage, wxArrayString& aValues ) override;
     MODULE*         GetFootprint( wxString * aMessages ) override;
     void*           GetObject() override;
+    wxArrayString   GetParameterHints( int aPage ) override;
+    wxArrayString	GetParameterDesignators( int aPage = 0) override;
+
+    void			ResetParameters() override;
 };
 
 
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 754e9ba..cd40ea0 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -119,23 +119,19 @@ def LoadPlugins(bundlepath=None):
     if bundlepath:
         plugin_directories.append(bundlepath)
         plugin_directories.append(os.path.join(bundlepath, 'plugins'))
-        plugin_directories.append(os.path.join(bundlepath, 'plugins', 'wizards'))
 
     if kicad_path:
         plugin_directories.append(os.path.join(kicad_path, 'scripting'))
         plugin_directories.append(os.path.join(kicad_path, 'scripting', 'plugins'))
-        plugin_directories.append(os.path.join(kicad_path, 'scripting', 'plugins', 'wizards'))
 
     if config_path:
         plugin_directories.append(os.path.join(config_path, 'scripting'))
         plugin_directories.append(os.path.join(config_path, 'scripting', 'plugins'))
-        plugin_directories.append(os.path.join(config_path, 'scripting', 'plugins', 'wizards'))
 
     if sys.platform.startswith('linux'):
         plugin_directories.append(os.environ['HOME']+'/.kicad_plugins/')
         plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/')
         plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/plugins/')
-        plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/plugins/wizards')
 
     for plugins_dir in plugin_directories:
         if not os.path.isdir(plugins_dir):
@@ -164,7 +160,6 @@ def LoadPlugins(bundlepath=None):
                 pass
 
 
-
 class KiCadPlugin:
     def __init__(self):
         pass
@@ -203,88 +198,295 @@ class FilePlugin(KiCadPlugin):
 
 from math import ceil, floor, sqrt
 
-class FootprintWizardPlugin(KiCadPlugin):
+uMM = "mm"            # Millimetres
+uMils = "mils"        # Mils
+uFloat = "float"      # Natural number units (dimensionless)
+uInteger = "integer"  # Integer (no decimals, numeric, dimensionless)
+uBool = "bool"        # Boolean value
+uRadians = "radians"  # Angular units (radians)
+uDegrees = "degrees"  # Angular units (degrees)
+uPercent = "%"  # Percent (0% -> 100%)
+uString = "string"    # Raw string
+
+uNumeric = [uMM, uMils, uFloat, uInteger, uDegrees, uRadians, uPercent]                  # List of numeric types
+uUnits   = [uMM, uMils, uFloat, uInteger, uBool, uDegrees, uRadians, uPercent, uString]  # List of allowable types
+
+class FootprintWizardParameter(object):
+    _true  = ['true','t','y','yes','on','1',1,]
+    _false = ['false','f','n','no','off','0',0,'',None]
+
+    _bools = _true + _false
+
+    def __init__(self, page, name, units, default, **kwarg):
+        self.page = page
+        self.name = name
+        self.hint = kwarg.get('hint','')               # Parameter hint (shown as mouse-over text)
+        self.designator = kwarg.get('designator',' ')  # Parameter designator such as "e, D, p" (etc)
+
+        if units.lower() in uUnits:
+            self.units = units.lower()
+        elif units.lower() == 'percent':
+            self.units = uPercent
+        elif type(units) in [list, tuple]:  # Convert a list of options into a single string
+            self.units = ",".join([str(el).strip() for el in units])
+        else:
+            self.units = units
+
+        self.multiple = int(kwarg.get('multiple',1))   # Check integer values are multiples of this number
+        self.min_value = kwarg.get('min_value',None)   # Check numeric values are above or equal to this number
+        self.max_value = kwarg.get('max_value',None)   # Check numeric values are below or equal to this number
+
+        self.SetValue(default)
+        self.default = self.raw_value  # Save value as default
+
+    def ClearErrors(self):
+        self.error_list = []
+
+    def AddError(self, err, info=None):
+
+        if err in self.error_list:  # prevent duplicate error messages
+            return
+        if info is not None:
+            err = err + " (" + str(info) + ")"
+
+        self.error_list.append(err)
+
+    def Check(self, min_value=None, max_value=None, multiple=None, info=None):
+
+        if min_value is None:
+            min_value = self.min_value
+        if max_value is None:
+            max_value = self.max_value
+        if multiple is None:
+            multiple = self.multiple
+
+        if self.units not in uUnits and ',' not in self.units:  # Allow either valid units or a list of strings
+            self.AddError("type '{t}' unknown".format(t=self.units),info)
+            self.AddError("Allowable types: " + str(self.units),info)
+
+        if self.units in uNumeric:
+            try:
+                to_num = float(self.raw_value)
+
+                if min_value is not None:  # Check minimum value if it is present
+                    if to_num < min_value:
+                        self.AddError("value '{v}' is below minimum ({m})".format(v=self.raw_value,m=min_value),info)
+
+                if max_value is not None:  # Check maximum value if it is present
+                    if to_num > max_value:
+                        self.AddError("value '{v}' is above maximum ({m})".format(v=self.raw_value,m=max_value),info)
+
+            except:
+                self.AddError("value '{v}' is not of type '{t}'".format(v = self.raw_value, t=self.units),info)
+
+        if self.units == uInteger:  # Perform integer specific checks
+            try:
+                to_int = int(self.raw_value)
+
+                if multiple is not None and multiple > 1:
+                    if (to_int % multiple) > 0:
+                        self.AddError("value '{v}' is not a multiple of {m}".format(v=self.raw_value,m=multiple),info)
+            except:
+                self.AddError("value {'v}' is not an integer".format(v=self.raw_value),info)
+
+        if self.units == uBool:  # Check that the value is of a correct boolean format
+            if self.raw_value in [True,False] or str(self.raw_value).lower() in self._bools:
+                pass
+            else:
+                self.AddError("value '{v}' is not a boolean value".format(v = self.raw_value),info)
+
+    @property
+    def value(self):  # Return the current value, converted to appropriate units (from string representation) if required
+        v = str(self.raw_value)  # Enforce string type for known starting point
+
+        if self.units == uInteger:  # Integer values
+            return int(v)
+        elif self.units in uNumeric:  # Any values that use floating points
+            v = v.replace(",",".")  # Replace "," separators with "."
+            v = float(v)
+
+            if self.units == uMM: # Convert from millimetres to nanometres
+                return FromMM(v)
+
+            elif self.units == uMils:  # Convert from mils to nanometres
+                return FromMils(v)
+
+            else:  # Any other floating-point values
+                return v
+
+        elif self.units == uBool:
+            if v.lower() in self._true:
+                return True
+            else:
+                return False
+        else:
+            return v
+
+    def DefaultValue(self):  # Reset the value of the parameter to its default
+        self.raw_value = str(self.default)
+
+    def SetValue(self, new_value):  # Update the value
+        new_value = str(new_value)
+
+        if len(new_value.strip()) == 0:
+            if not self.units in [uString, uBool]:
+                return  # Ignore empty values unless for strings or bools
+
+        if self.units == uBool:  # Enforce the same boolean representation as is used in KiCad
+            new_value = "1" if new_value.lower() in self._true else "0"
+        elif self.units in uNumeric:
+            new_value = new_value.replace(",", ".")  # Enforce decimal point separators
+        elif ',' in self.units:  # Select from a list of values
+            if new_value not in self.units.split(','):
+                new_value = self.units.split(',')[0]
+
+        self.raw_value = new_value
+
+    def __str__(self):  # pretty-print the parameter
+
+        s = self.name + ": " + str(self.raw_value)
+
+        if self.units in [uMM, uMils, uPercent, uRadians, uDegrees]:
+            s += self.units
+        elif self.units == uBool:  # Special case for Boolean values
+            s = self.name + ": {b}".format(b = "True" if self.value else "False")
+        elif self.units == uString:
+            s = self.name + ": '" + self.raw_value + "'"
+
+        return s
+
+class FootprintWizardPlugin(KiCadPlugin, object):
     def __init__(self):
         KiCadPlugin.__init__(self)
         self.defaults()
 
     def defaults(self):
         self.module = None
-        self.parameters = {}
-        self.parameter_errors={}
-        self.name = "Undefined Footprint Wizard plugin"
-        self.description = ""
+        self.params = []  # List of added parameters that observes addition order
+
+        self.name = "KiCad FP Wizard"
+        self.description = "Undefined Footprint Wizard plugin"
         self.image = ""
         self.buildmessages = ""
 
-    def GetName(self):
+    def AddParam(self, page, name, unit, default, **kwarg):
+
+        if self.GetParam(page,name) is not None:  # Param already exists!
+            return
+
+        param = FootprintWizardParameter(page, name, unit, default, **kwarg)  # Create a new parameter
+        self.params.append(param)
+
+    @property
+    def parameters(self):  # This is a helper function that returns a nested (unordered) dict of the VALUES of parameters
+        pages = {}  # Page dict
+        for p in self.params:
+            if p.page not in pages:
+                pages[p.page] = {}
+
+            pages[p.page][p.name] = p.value  # Return the 'converted' value (convert from string to actual useful units)
+
+        return pages
+
+    @property
+    def values(self):  # Same as above
+        return self.parameters
+
+    def ResetWizard(self):  # Reset all parameters to default values
+        for p in self.params:
+            p.DefaultValue()
+
+    def GetName(self):  # Return the name of this wizard
         return self.name
 
-    def GetImage(self):
+    def GetImage(self):  # Return the filename of the preview image associated with this wizard
         return self.image
 
-    def GetDescription(self):
+    def GetDescription(self):  # Return the description text
         return self.description
 
+    def GetValue(self):
+        raise NotImplementedError
 
-    def GetNumParameterPages(self):
-        return len(self.parameters)
+    def GetReferencePrefix(self):
+        return "REF"  # Default reference prefix for any footprint
 
-    def GetParameterPageName(self,page_n):
-        return self.page_order[page_n]
+    def GetParam(self, page, name):  # Grab a parameter
+        for p in self.params:
+            if p.page == page and p.name == name:
+                return p
 
-    def GetParameterNames(self,page_n):
-        name = self.GetParameterPageName(page_n)
-        return self.parameter_order[name]
+        return None
 
-    def GetParameterValues(self,page_n):
-        name = self.GetParameterPageName(page_n)
-        names = self.GetParameterNames(page_n)
-        values = [self.parameters[name][n] for n in names]
-        return map(lambda x: str(x), values)  # list elements as strings
+    def CheckParam(self, page, name, **kwarg):
+        self.GetParam(page,name).Check(**kwarg)
 
-    def GetParameterErrors(self,page_n):
-        self.CheckParameters()
-        name = self.GetParameterPageName(page_n)
-        names = self.GetParameterNames(page_n)
-        values = [self.parameter_errors[name][n] for n in names]
-        return map(lambda x: str(x), values)  # list elements as strings
+    def AnyErrors(self):
+        return any([len(p.error_list) > 0 for p in self.params])
 
-    def CheckParameters(self):
-        return ""
+    @property
+    def pages(self):  # Return an (ordered) list of the available page names
+        page_list = []
+        for p in self.params:
+            if p.page not in page_list:
+                page_list.append(p.page)
 
-    def ConvertValue(self,v):
-        try:
-            v = float(v)
-        except:
-            pass
-        if type(v) is float:
-            if ceil(v) == floor(v):
-                v = int(v)
-        return v
+        return page_list
 
+    def GetNumParameterPages(self):  # Return the number of parameter pages
+        return len(self.pages)
 
-    def SetParameterValues(self,page_n,values):
-        name = self.GetParameterPageName(page_n)
-        keys = self.GetParameterNames(page_n)
-        for n, key in enumerate(keys):
-            val = self.ConvertValue(values[n])
-            self.parameters[name][key] = val
+    def GetParameterPageName(self,page_n):  # Return the name of a page at a given index
+        return self.pages[page_n]
 
+    def GetParametersByPageName(self, page_name):  # Return a list of parameters on a given page
+        params = []
 
-    def ClearErrors(self):
-        errs={}
+        for p in self.params:
+            if p.page == page_name:
+                params.append(p)
 
-        for page in self.parameters.keys():
-            page_dict = self.parameters[page]
-            page_params = {}
-            for param in page_dict.keys():
-                page_params[param]=""
+        return params
 
-            errs[page]=page_params
+    def GetParametersByPageIndex(self, page_index):  # Return an ordered list of parameters on a given page
+        return self.GetParametersByPageName(self.GetParameterPageName(page_index))
 
-        self.parameter_errors = errs
+    def GetParameterDesignators(self, page_index):  # Return a list of designators associated with a given page
+        params = self.GetParametersByPageIndex(page_index)
+        return [p.designator for p in params]
 
+    def GetParameterNames(self,page_index):  # Return the list of names associated with a given page
+        params = self.GetParametersByPageIndex(page_index)
+        return [p.name for p in params]
+
+    def GetParameterValues(self,page_index):  # Return the list of values associated with a given page
+        params = self.GetParametersByPageIndex(page_index)
+        return [str(p.raw_value) for p in params]
+
+    def GetParameterErrors(self,page_index):  # Return list of errors associated with a given page
+        params = self.GetParametersByPageIndex(page_index)
+        return [str("\n".join(p.error_list)) for p in params]
+
+    def GetParameterTypes(self, page_index):  # Return list of units associated with a given page
+        params = self.GetParametersByPageIndex(page_index)
+        return [str(p.units) for p in params]
+
+    def GetParameterHints(self, page_index):  # Return a list of units associated with a given page
+        params = self.GetParametersByPageIndex(page_index)
+        return [str(p.hint) for p in params]
+
+    def GetParameterDesignators(self, page_index):  # Return a list of designators associated with a given page
+        params = self.GetParametersByPageIndex(page_index)
+        return [str(p.designator) for p in params]
+
+    def SetParameterValues(self, page_index, list_of_values):  # Update values on a given page
+
+        params = self.GetParametersByPageIndex(page_index)
+
+        for i, param in enumerate(params):
+            if i >= len(list_of_values):
+                break
+            param.SetValue(list_of_values[i])
 
     def GetFootprint( self ):
         self.BuildFootprint()
@@ -297,17 +499,30 @@ class FootprintWizardPlugin(KiCadPlugin):
         return self.buildmessages
 
     def Show(self):
-        print "Footprint Wizard Name:        ",self.GetName()
-        print "Footprint Wizard Description: ",self.GetDescription()
+        text  = "Footprint Wizard Name:        {name}\n".format(name=self.GetName())
+        text += "Footprint Wizard Description: {desc}\n".format(desc=self.GetDescription())
+
         n_pages = self.GetNumParameterPages()
-        print " setup pages: ",n_pages
-        for page in range(0,n_pages):
-            name = self.GetParameterPageName(page)
-            values = self.GetParameterValues(page)
-            names =  self.GetParameterNames(page)
-            print "page %d) %s"%(page,name)
-            for n in range (0,len(values)):
-                print "\t%s\t:\t%s"%(names[n],values[n])
+
+        text += "Pages: {n}\n".format(n=n_pages)
+
+        for i in range(n_pages):
+            name = self.GetParameterPageName(i)
+
+            params = self.GetParametersByPageName(name)
+
+            text += "{name}\n".format(name=name)
+
+            for j in range(len(params)):
+                text += ("\t{param}{err}\n".format(
+                    param = str(params[j]),
+                    err = ' *' if len(params[j].error_list) > 0 else ''
+                    ))
+
+        if self.AnyErrors():
+            text += " * Errors exist for these parameters"
+
+        return text
 
 class ActionPlugin(KiCadPlugin):
     def __init__(self):

Follow ups

References