← Back to team overview

kicad-developers team mailing list archive

[PATCH][RFC] Footprint wizards

 

Hi all,

First time submitting a patch, so here goes....

The attached patch deals with a number of issues with regards to the
footprint wizards manager. It started off as what I imagined was a fairly
simple task to improve the UX of the FP wizards interface, but it evolved
into something a bit more complex as I delved deeper into the source!

Improvements are as follows:

1. I have done away with the use of a leading asterisk to designate the
"units" of a wizard parameter. Multiple parameter types can now be defined
(integer, float, mm, mils, bool, etc..)

2. Input validation. Each type of parameter is now validated properly
within the wizards screen. Integer parameters can only be set to integers,
dimensions can only be floating point, etc.

3. Boolean values are now treated properly. Simply click the cell to
toggle, rather than awkwardly typing "True" or "False"

4. Multiple choice options available - If the python script specifies a
list of options, then a drop-down box will be displayed for that parameter

5. Param designators. Instead of the row numbers being shown, each param
can optionally be assigned a designator (such as 'e' for pitch) which will
be show to the left of that row.

6. "Reset to default" - A new button in the top toolbar which resets all
wizard parameters to their default values

7. More logical parameter checking within the python wizard helpers.
Currently each parameter needs to be explicitly checked e.g. "CheckInt" to
make sure the value is a valid integer. This patch defines a Parameter
holder class that automatically checks values based on their specified
type. Additionally, parameters can have other checks specified when they
are added, e.g:

AddParam("Pads","width",uMM, 2.5, min_value=0.1, max_value=5.5)

8. Fixed script import errors in the case of "bad" scripts. Currently if a
wizard contains any errors, the LoadPlugins functions fail and no
subsequent wizards are loaded. A simple try-except block has been added
around the specific file loading so that one bad plugin file does not break
the others

9. Auto-sizing of the parameter grid. Not a huge deal but the grid width
now expands to fill the available space.

General Notes:

a) To provide the functionality for multiple unit types I have had to
slightly break compatibility with the way that parameter types were defined
in the current wizards. However, I have designed the new Parameter class
(in kicadplugins.i) to be as close-to-compatible as it can be.

I have updated each of the default plugins to be compatible with the new
system. Only minor changes were required.

b) I have also consolidated the helper classes
(HelpfulFootprintWizardPlugin) into the simpler FootprintWizard base class.

c) There is now a GitHub repository for FootprintWizards -
https://github.com/KiCad/Footprint_Wizards - should this patch be accepted
I propose that the default wizards be further improved, and removed from
the source files. Instead, provide a link to the GitHub page or a
download-helper for the scripts. This way the community can contribute
quality wizards, and I shall endeavour to add some good documentation to
the wiki page for wizard creation.

.diff is attached - hopefully this is the right way of doing this?

Please let me know what I can do to help this process along.

Regards,

Oliver
From 1fd9f8dc322cbbb0169b6ffea6361b037d939eb0 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 3 Sep 2016 14:05:33 +1000
Subject: [PATCH 01/16] Refactored code that finds python scripting path, so
 that it can be reused
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/pcbnew.cpp              | 16 ++--------------
 scripting/python_scripting.cpp | 31 +++++++++++++++++++++++++++++++
 scripting/python_scripting.h   |  3 +++
 3 files changed, 36 insertions(+), 14 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0001-Refactored-code-that-finds-python-scripting-path-so-.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0001-Refactored-code-that-finds-python-scripting-path-so-.patch"

diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp
index dc6eb63..7dae0a7 100644
--- a/pcbnew/pcbnew.cpp
+++ b/pcbnew/pcbnew.cpp
@@ -201,7 +201,6 @@ PGM_BASE& Pgm()
 #if defined( KICAD_SCRIPTING )
 static bool scriptingSetup()
 {
-    wxString path_frag;
 
 #if defined( __MINGW32__ )
     // force python environment under Windows:
@@ -224,6 +223,7 @@ static bool scriptingSetup()
             ppath << kipython << wxT( "/pylib;" );
             ppath << kipython << wxT( "/lib;" );
             ppath << kipython << wxT( "/dll" );
+
             wxSetEnv( wxT( "PYTHONPATH" ), ppath );
             // DBG( std::cout << "set PYTHONPATH to "  << TO_UTF8( ppath ) << "\n"; )
 
@@ -239,14 +239,6 @@ static bool scriptingSetup()
         }
     }
 
-    // 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
@@ -290,13 +282,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( wxT( "pcbnewInitPythonScripting() failed." ) );
         return false;
diff --git a/scripting/python_scripting.cpp b/scripting/python_scripting.cpp
index 059ceef..58c9db1 100644
--- a/scripting/python_scripting.cpp
+++ b/scripting/python_scripting.cpp
@@ -37,6 +37,8 @@
 #include <colors.h>
 #include <macros.h>
 
+#include <pgm_base.h>
+
 /* init functions defined by swig */
 
 extern "C" void init_kicad( void );
@@ -359,3 +361,32 @@ wxString PyErrStringWithTraceback()
 
     return err;
 }
+
+/**
+ * Find the Python scripting path
+ */
+wxString PyScriptingPath()
+{
+	wxString path;
+
+	//TODO should this be a user configurable variable eg KISCRIPT ?
+
+#if defined( __MINGW32__ )
+	path = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
+#elif defined( __WXMAC__ )
+	path = GetOSXKicadDataDir() + wxT( "/scripting" );
+#else
+	path = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
+#endif
+
+	wxFileName scriptPath( path );
+
+	scriptPath.MakeAbsolute();
+
+	return scriptPath.GetFullPath();
+}
+
+wxString PyPluginsPath()
+{
+	return PyScriptingPath() + wxFileName::GetPathSeparator() + "plugins";
+}
diff --git a/scripting/python_scripting.h b/scripting/python_scripting.h
index ca25de4..a84bc7c 100644
--- a/scripting/python_scripting.h
+++ b/scripting/python_scripting.h
@@ -59,4 +59,7 @@ public:
 wxArrayString   PyArrayStringToWx( PyObject* arr );
 wxString        PyErrStringWithTraceback();
 
+wxString		PyScriptingPath();
+wxString		PyPluginsPath();
+
 #endif    // __PYTHON_SCRIPTING_H

--------------1.9.5.msysgit.0--



From 24c098b0e777ae04e676a394cc5471ce40552ade Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 9 Sep 2016 00:30:41 +1000
Subject: [PATCH 02/16] Added definitions for allowable unit types for PCB
 wizards
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/class_footprint_wizard.h | 11 +++++++++++
 1 file changed, 11 insertions(+)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0002-Added-definitions-for-allowable-unit-types-for-PCB-w.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0002-Added-definitions-for-allowable-unit-types-for-PCB-w.patch"

diff --git a/pcbnew/class_footprint_wizard.h b/pcbnew/class_footprint_wizard.h
index 20fc1d4..d2b86ab 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";		// 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

--------------1.9.5.msysgit.0--



From e254e48b28d13181e38629bc2d0e3c993ad9271a Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 9 Sep 2016 01:30:40 +1000
Subject: [PATCH 03/16] Added try/except catch around importing of python
 plugins. If one plugin is faulty, it will no longer kill the entire import
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 scripting/kicadplugins.i | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0003-Added-try-except-catch-around-importing-of-python-pl.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0003-Added-try-except-catch-around-importing-of-python-pl.patch"

diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 918896d..a1935bf 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -146,16 +146,18 @@ def LoadPlugins(bundlepath=None):
             if module == '__init__.py' or module[-3:] != '.py':
                 continue
 
-            mod = __import__(module[:-3], locals(), globals())
-
-            module_filename = plugins_dir+"/"+module
-            mtime = os.path.getmtime(module_filename)
-            if hasattr(mod,'register'):
-                KICAD_PLUGINS[module]={"filename":module_filename,
-                                       "modification_time":mtime,
-                                       "object":mod.register(),
-                                       "module":mod}
-
+            try:  # If there is an error loading the script, skip it
+                mod = __import__(module[:-3], locals(), globals())
+
+                module_filename = plugins_dir+"/"+module
+                mtime = os.path.getmtime(module_filename)
+                if hasattr(mod,'register'):
+                    KICAD_PLUGINS[module]={"filename":module_filename,
+                                           "modification_time":mtime,
+                                           "object":mod.register(),
+                                           "module":mod}
+            except:
+                pass
 
 
 class KiCadPlugin:

--------------1.9.5.msysgit.0--



From 3d889906ac886304a76582d36a366ec60ed28d2a Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 9 Sep 2016 01:31:09 +1000
Subject: [PATCH 04/16] Added PutOnGridMM and PutOnGridMils functions to
 pcbnew.py (via units.i)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/scripting/units.i | 8 ++++++++
 1 file changed, 8 insertions(+)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0004-Added-PutOnGridMM-and-PutOnGridMils-functions-to-pcb.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0004-Added-PutOnGridMM-and-PutOnGridMils-functions-to-pcb.patch"

diff --git a/pcbnew/scripting/units.i b/pcbnew/scripting/units.i
index 56ea213..767969f 100644
--- a/pcbnew/scripting/units.i
+++ b/pcbnew/scripting/units.i
@@ -54,6 +54,14 @@
          return int(float(mils)*float(IU_PER_MILS))
       elif type(mils) in [wxPoint,wxSize]:
         return tuple(map(FromMils,mils))
+        
+   def PutOnGridMM(value, gridSizeMM):
+      thresh = FromMM(gridSizeMM)
+      return round(value/thresh)*thresh
+      
+   def PutOnGridMils(value, gridSizeMils):
+      thresh = FromMils(gridSizeMils)
+      return round(value/thresh)*thresh
 
    def wxSizeMM(mmx,mmy): return wxSize(FromMM(mmx),FromMM(mmy))
    def wxSizeMils(mmx,mmy): return wxSize(FromMils(mmx),FromMils(mmy))

--------------1.9.5.msysgit.0--



From 6a02cf876ac0fc931384bc7d4ea6167691bb5336 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 9 Sep 2016 08:40:59 +1000
Subject: [PATCH 05/16] Added "GetParameterHints" function to footprint wizard
 Fixed GetParameterTypes function (now actually gets the types rather than
 inferring from the name)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/class_footprint_wizard.h               |  7 +++++++
 pcbnew/scripting/pcbnew_footprint_wizards.cpp | 28 ++++++++++++---------------
 pcbnew/scripting/pcbnew_footprint_wizards.h   |  2 ++
 3 files changed, 21 insertions(+), 16 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0005-Added-GetParameterHints-function-to-footprint-wizard.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0005-Added-GetParameterHints-function-to-footprint-wizard.patch"

diff --git a/pcbnew/class_footprint_wizard.h b/pcbnew/class_footprint_wizard.h
index d2b86ab..a8137d7 100644
--- a/pcbnew/class_footprint_wizard.h
+++ b/pcbnew/class_footprint_wizard.h
@@ -116,6 +116,13 @@ 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 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
diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.cpp b/pcbnew/scripting/pcbnew_footprint_wizards.cpp
index e0f3854..8e4d9ed 100644
--- a/pcbnew/scripting/pcbnew_footprint_wizards.cpp
+++ b/pcbnew/scripting/pcbnew_footprint_wizards.cpp
@@ -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,17 @@ 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;
+}
 
 wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString& aValues )
 {
diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.h b/pcbnew/scripting/pcbnew_footprint_wizards.h
index ada111f..7d2c922 100644
--- a/pcbnew/scripting/pcbnew_footprint_wizards.h
+++ b/pcbnew/scripting/pcbnew_footprint_wizards.h
@@ -54,6 +54,8 @@ public:
     wxArrayString   GetParameterTypes( int aPage );
     wxArrayString   GetParameterValues( int aPage );
     wxArrayString   GetParameterErrors( int aPage );
+    wxArrayString   GetParameterHints( int aPage );
+
     // must return an empty string or an error description
     wxString        SetParameterValues( int aPage, wxArrayString& aValues );
     MODULE*         GetFootprint( wxString * aMessages );

--------------1.9.5.msysgit.0--



From 0ea580607291b7d0bb8583c101b966c080a959f3 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 9 Sep 2016 08:42:49 +1000
Subject: [PATCH 06/16] Consolidated HelpfulFootprintWizardPlugin.py and
 FootprintWizardDrawingAids.py into a single simplified file
 (FootprintWizardBase.py)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/scripting/plugins/FootprintWizardBase.py    | 635 +++++++++++++++++++++
 .../plugins/FootprintWizardDrawingAids.py          | 532 -----------------
 .../plugins/HelpfulFootprintWizardPlugin.py        | 348 -----------
 3 files changed, 635 insertions(+), 880 deletions(-)
 create mode 100644 pcbnew/scripting/plugins/FootprintWizardBase.py
 delete mode 100644 pcbnew/scripting/plugins/FootprintWizardDrawingAids.py
 delete mode 100644 pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0006-Consolidated-HelpfulFootprintWizardPlugin.py-and-Foo.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0006-Consolidated-HelpfulFootprintWizardPlugin.py-and-Foo.patch"

diff --git a/pcbnew/scripting/plugins/FootprintWizardBase.py b/pcbnew/scripting/plugins/FootprintWizardBase.py
new file mode 100644
index 0000000..0c96375
--- /dev/null
+++ b/pcbnew/scripting/plugins/FootprintWizardBase.py
@@ -0,0 +1,635 @@
+#  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):
+
+    """
+    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 GenerateParameterList(self):
+    	raise NotImplementedError
+        
+    def CheckParameters():
+        pass
+
+    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
+
+        if self.AnyErrors():  # Errors were detected!
+            
+            self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
+            
+            for p in self.params:
+                if len(p.errors) > 0:
+                    n_errors = len(p.errors)
+                    if n_errors == 1:
+                        error_string = "1 error"
+                    else:
+                        error_string = "{n} errors".format(n=n_errors)
+                    self.buildmessages +="['{page}']['{name}']: ({err})\n".format(page=p.page,name=p.name,err=error_string)
+                    
+                    for error in p.errors:
+                        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.FPID(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:
+            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)
\ No newline at end of file
diff --git a/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py b/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py
deleted file mode 100644
index 036f09a..0000000
--- a/pcbnew/scripting/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/scripting/plugins/HelpfulFootprintWizardPlugin.py b/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py
deleted file mode 100644
index a742067..0000000
--- a/pcbnew/scripting/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.FPID(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

--------------1.9.5.msysgit.0--



From b7438bd539b9e131e677d9a6e9d7a3efac3b8cd1 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 9 Sep 2016 08:54:46 +1000
Subject: [PATCH 07/16] Added more wizard parameter unit types Added
 FootprintWizardParameter Class to pcbnew.py (via kicadplugins.i) * Better
 management of data validation * Each parameter holds more values (such as
 "Units", "Default") * Automatically converts the string value passed back
 from pcbnew into correct internal units Merged many helper functions
 (originally in HelpfulFootprintWizardsPlugin) into the FootprintWizardPlugin
 class * Better management of AddParam * Allows "List" options (in future will
 present as a drop-down list in pcbnew) * Better string formatting for Show()
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 scripting/kicadplugins.i | 383 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 301 insertions(+), 82 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0007-Added-more-wizard-parameter-unit-types.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0007-Added-more-wizard-parameter-unit-types.patch"

diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index a1935bf..f0096a2 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -119,19 +119,23 @@ 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):
@@ -198,93 +202,290 @@ class FilePlugin(KiCadPlugin):
 
 from math import ceil, floor, sqrt
 
-class FootprintWizardPlugin(KiCadPlugin):
+class FootprintWizardParameter(object):
+
+    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
+
+    uUnits = [uMM, uMils, uFloat, uInteger, uBool, uDegrees, uRadians, uPercent, uString]  # List of allowable units
+
+    _true = ['true','t','y','yes','on','1']
+    _false = ['false','f','n','no','off','0']
+    
+    _bools = _true + _false
+
+    def __init__(self, page, name, units, default, **kwarg):
+        self.page = page        
+        self.name = name
+        self.units = units
+        self.default = str(default)
+        self.raw_value = str(default)
+        
+        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)
+        
+        self.multiple = int(kwarg.get('multiple',1))  # Check integer values are multiples of this number
+        self.min_value = kwarg.get('min_value',0) # 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
+        
+        if type(self.units) in [list, tuple]:  # Convert a list of options into a single string
+            self.units = ",".join([str(el) for el in self.units])
+            
+        if self.units.lower() == 'percent':
+            self.units = self.uPercent
+        
+        self.Check()  # Compare units to default values
+        
+    def Check(self):  # Check the value to ensure that the data type matches the units
+    
+        self.error_list = []  # Clear the error list
+        
+        if self.units.lower() in self.uUnits:
+            self.units = self.units.lower()
+    
+        if ',' in self.units:  # Select from a list of values
+            if self.raw_value not in self.units.split(','):
+                self.error_list.append("value '{v}' is not in list of options [{l}]".format(v=self.raw_value, l=str(self.units)))
+    
+        if self.units not in self.uUnits and ',' not in self.units:  # Allow either valid units or a list of strings
+            self.error_list.append("type '{t}' unknown".format(t=self.units))
+            self.error_list.append("Allowable types: " + str(uUnits))
+            
+        if self.units in [self.uMM, self.uMils, self.uFloat, self.uInteger, self.uRadians, self.uDegrees, self.uPercent]:  # Generic number test
+            try:
+                to_num = float(self.raw_value)
+                
+                if self.min_value is not None:  # Check minimum value if it is present
+                    if to_num < self.min_value:
+                        self.error_list.append("value '{v}' is below minimum ({m})".format(v=self.raw_value,m=self.min_value))
+                        
+                if self.max_value is not None:  # Check maximum value if it is present
+                    if to_num > self.max_value:
+                        self.error_list.append("value '{v}' is above maximum ({m})".format(v=self.raw_value,m=self.max_value))
+                        
+                if self.units == self.uPercent: 
+                    if to_num < 0 or to_num > 100:
+                        self.error_list.append("'{v}' is not a valid percentage (0% to 100%)".format(v=self.raw_value))
+                
+            except:
+                self.error_list.append("value '{v}' is not of type '{t}'".format(v = self.raw_value, t=self.units))
+
+        if self.units == self.uInteger:  # Perform integer specific checks
+            try:
+                to_int = int(self.raw_value)
+                
+                if self.multiple > 1:
+                    if (to_int % self.multiple) > 0:
+                        self.error_list.append("value '{v}' is not a multiple of {m}".format(v=self.raw_value,m=self.multiple))
+            except:
+                self.error_list.append("value {'v}' is not an integer".format(v=self.raw_value))
+                
+        if self.units == self.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.error_list.append("value '{v}' is not a boolean value".format(v = self.raw_value))
+        
+    @property
+    def errors(self):  # Return a (formatted) error string
+        self.Check()  # Automatically check errors
+        return self.error_list
+        
+    @property
+    def value(self):  # Return the current value, converted to internal units (from string representation) if required
+        if self.units == self.uMM:  # Convert from MM to internal units
+            return FromMM(float(self.raw_value))
+        elif self.units == self.uMils:  # Convert from Mils to internal units
+            return FromMils(float(self.raw_value))
+        elif self.units == self.uBool:
+            if self.raw_value == True or str(self.raw_value).lower() in self._true:
+                return True
+            else:
+                return False
+        elif self.units == self.uInteger:
+            return int(self.raw_value)
+        elif self.units == self.uPercent:
+            percent = float(self.raw_value)
+            if percent < 0:
+                percent = 0
+            elif percent > 100:
+                percent = 100
+            return percent
+        elif self.units == self.uDegrees:  # Angles should be expressed in radians
+            angle = float(self.raw_value)
+            while angle < -180: 
+                angle += 360
+            while angle > 180:
+                angle -= 360
+            return angle * math.pi / 180
+        elif self.units == self.uRadians:
+            angle = float(self.raw_value)
+            while angle < -math.pi:
+                angle += 2 * math.pi
+            while angle >  math.pi:
+                angle -= 2 * math.pi
+            return angle
+
+        else:
+            return self.raw_value
+            
+    def DefaultValue(self):
+        self.raw_value = str(self.default)
+            
+    def SetValue(self, new_value):  # Update the value
+        self.raw_value = str(new_value)
+
+class FootprintWizardPlugin(KiCadPlugin, object):
+    
+    uMM         = FootprintWizardParameter.uMM      # Millimeters
+    uMils       = FootprintWizardParameter.uMils    # Mils
+    uFloat      = FootprintWizardParameter.uFloat   # Natural number units (dimensionless)
+    uInteger    = FootprintWizardParameter.uInteger # Integer (no decimals, numeric, dimensionless)
+    uBool       = FootprintWizardParameter.uBool    # Boolean value
+    uRadians    = FootprintWizardParameter.uRadians # Angular units (radians)
+    uDegrees    = FootprintWizardParameter.uDegrees # Angular units (degrees)
+    uPercent    = FootprintWizardParameter.uPercent # Percent (0% -> 100%)
+    uString     = FootprintWizardParameter.uString  # Raw string
+
+    uUnits      = FootprintWizardParameter.uUnits   # List of allowable units
+    
     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 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 GetNumParameterPages(self):
-        return len(self.parameters)
-
-    def GetParameterPageName(self,page_n):
-        return self.page_order[page_n]
-
-    def GetParameterNames(self,page_n):
-        name = self.GetParameterPageName(page_n)
-        return self.parameter_order[name]
-
-    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 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 CheckParameters(self):
-        return ""
-
-    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
-
-
-    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 ClearErrors(self):
-        errs={}
-
-        for page in self.parameters.keys():
-            page_dict = self.parameters[page]
-            page_params = {}
-            for param in page_dict.keys():
-                page_params[param]=""
-
-            errs[page]=page_params
-
-        self.parameter_errors = errs
-
-
+        
+    def GetValue(self):
+        raise NotImplementedError
+        
+    def GetReferencePrefix(self):
+        return "REF"  # Default reference prefix for any footprint
+        
+    def GetParam(self, page, name):  # Grab a parameter
+        for p in self.params:
+            if p.page == page and p.name == name:
+                return p
+                
+        return None
+        
+    def AnyErrors(self):
+        return any([len(p.errors) > 0 for p in self.params])
+        
+    @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)
+                
+        return page_list
+
+    def GetNumParameterPages(self):  # Return the number of parameter pages
+        return len(self.pages)
+
+    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 = []
+        
+        for p in self.params:
+            if p.page == page_name:
+                params.append(p)
+            
+        return params
+        
+    def GetParametersByPageIndex(self, page_index):  # Return an ordered list of parameters on a given page    
+        return self.GetParametersByPageName(self.GetParameterPageName(page_index))
+        
+    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.errors)) 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()
         return self.module
-
+        
     def BuildFootprint(self):
         return
 
@@ -292,18 +493,36 @@ 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)
+            
+            values = self.GetParameterValues(i)
+            names =  self.GetParameterNames(i)
+            units =  self.GetParameterTypes(i)
+            errors = self.GetParameterErrors(i)
+            
+            text += "page {i}) {name}\n".format(i=i,name=name)
+            
+            for j in range(len(values)):
+                text += ("\t{name}: {val} ({units}){err}\n".format(
+                    name=names[j],
+                    val=values[j],
+                    units=units[j],
+                    err = ' *' if errors[j] else ''
+                    ))
+
+        if self.AnyErrors():
+            text += " * Errors exist for these parameters"
+        
+        return text
+        
 class ActionPlugin(KiCadPlugin):
     def __init__(self):
         KiCadPlugin.__init__(self)

--------------1.9.5.msysgit.0--



From 130ad0a5b0db7617565c30e98446bd7fa92540f8 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 10 Sep 2016 15:58:40 +1000
Subject: [PATCH 08/16] Added function to get parameter designators from the
 wizard (e.g. 'p' for lead pitch)  - If a param has a designator, this is used
 as the row column  - Else, the row column is left blank Value field is given
 a particular editor style based on the units of the parameter. e.g. "bool"
 values use a checkbox, and a list-box is used for multiple selectable items
 No conversion is done between unit types in pcbnew any more. The units are
 all treated as "human readable" and any conversion takes place in the
 FootprintWizardParameter class in pcbnew.py Added some minor tweaks to the
 python code in kicadplugins.i to match new features as above
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/class_footprint_wizard.h               |  9 ++-
 pcbnew/footprint_wizard.cpp                   | 27 --------
 pcbnew/footprint_wizard_frame.cpp             | 96 ++++++++++++++++-----------
 pcbnew/footprint_wizard_frame.h               |  2 +-
 pcbnew/scripting/pcbnew_footprint_wizards.cpp | 20 ++++--
 pcbnew/scripting/pcbnew_footprint_wizards.h   |  1 +
 scripting/kicadplugins.i                      |  4 +-
 7 files changed, 87 insertions(+), 72 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0008-Added-function-to-get-parameter-designators-from-the.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0008-Added-function-to-get-parameter-designators-from-the.patch"

diff --git a/pcbnew/class_footprint_wizard.h b/pcbnew/class_footprint_wizard.h
index a8137d7..933abaf 100644
--- a/pcbnew/class_footprint_wizard.h
+++ b/pcbnew/class_footprint_wizard.h
@@ -41,7 +41,7 @@ const wxString WIZARD_PARAM_UNITS_INTEGER = "integer";		// Integer (dimensionles
 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";		// Percent (0% -> 100%)
+const wxString WIZARD_PARAM_UNITS_PERCENT = "%";		// Percent (0% -> 100%)
 const wxString WIZARD_PARAM_UNITS_STRING = "string";		// String
 
 /**
@@ -123,6 +123,13 @@ public:
     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
diff --git a/pcbnew/footprint_wizard.cpp b/pcbnew/footprint_wizard.cpp
index 6e9b17e..013bd4f 100644
--- a/pcbnew/footprint_wizard.cpp
+++ b/pcbnew/footprint_wizard.cpp
@@ -230,33 +230,6 @@ void FOOTPRINT_WIZARD_FRAME::ParametersUpdated( wxGridEvent& event )
     {
         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();
-            }
-        }
-
         if( prmValues[prm_id] != value )
         {
             has_changed = true;
diff --git a/pcbnew/footprint_wizard_frame.cpp b/pcbnew/footprint_wizard_frame.cpp
index 6112fa6..ed75944 100644
--- a/pcbnew/footprint_wizard_frame.cpp
+++ b/pcbnew/footprint_wizard_frame.cpp
@@ -44,6 +44,7 @@
 #include "footprint_wizard_frame.h"
 #include <footprint_info.h>
 #include <wx/grid.h>
+#include <wx/tokenzr.h>
 
 #include <hotkeys.h>
 #include <wildcards_and_files_ext.h>
@@ -336,58 +337,79 @@ 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->SetRowLabelValue( i, designator );
 
-        m_parameterGrid->SetCellValue( i, m_columnPrmName, fpList[i] );
+    	// Set the 'Name'
+        m_parameterGrid->SetCellValue( i, m_columnPrmName, name );
         m_parameterGrid->SetReadOnly( i, m_columnPrmName );
 
-        if( ptList[i]==wxT( "IU" ) )
+        // Set the 'Value'
+        m_parameterGrid->SetCellValue( i, m_columnPrmValue, value );
+
+        // Set the editor type of the
+
+        // Boolean parameters can be displayed using a checkbox
+        if ( units == WIZARD_PARAM_UNITS_BOOL )
+        {
+        	m_parameterGrid->SetCellEditor( i, m_columnPrmValue, new wxGridCellBoolEditor );
+        	m_parameterGrid->SetCellRenderer( i, m_columnPrmValue, new wxGridCellBoolRenderer );
+        }
+        // Parameters that can be selected from a list of multiple options
+        else if ( units.Contains( "," ) )  // Indicates list of available options
         {
-            LOCALE_IO toggle;
-
-            // We are handling internal units, so convert them to the current
-            // system selected units and store into value.
-            double dValue;
-
-            value.ToDouble( &dValue );
-
-            dValue = To_User_Unit( g_UserUnit, dValue );
-
-            if( g_UserUnit==INCHES )    // we convert inches into mils for more detail
-            {
-                dValue  = dValue * 1000.0;
-                units   = wxT( "mils" );
-            }
-            else if( g_UserUnit==MILLIMETRES )
-            {
-                units = wxT( "mm" );
-            }
-
-            // 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() );
+        	wxStringTokenizer tokenizer( units, "," );
+        	wxArrayString options;
+
+        	while ( tokenizer.HasMoreTokens() )
+        	{
+        		options.Add( tokenizer.GetNextToken() );
+        	}
+
+        	m_parameterGrid->SetCellEditor( i, m_columnPrmValue, new wxGridCellChoiceEditor( options ) );
+
+        	units = wxT( "<list>" );
         }
-        else if( ptList[i]==wxT( "UNITS" ) )    // 1,2,3,4,5 ... N
+        // Integer parameters
+        else if ( units == WIZARD_PARAM_UNITS_INTEGER )
+		{
+			m_parameterGrid->SetCellEditor( i, m_columnPrmValue, 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 ) )
         {
-            units = wxT( "" );
+        	m_parameterGrid->SetCellEditor( i, m_columnPrmValue, new wxGridCellFloatEditor );
         }
 
-        m_parameterGrid->SetCellValue( i, m_columnPrmValue, value );
+
+        // Set the 'Units'
         m_parameterGrid->SetCellValue( i, m_columnPrmUnit, units );
         m_parameterGrid->SetReadOnly( i, m_columnPrmUnit );
     }
diff --git a/pcbnew/footprint_wizard_frame.h b/pcbnew/footprint_wizard_frame.h
index 65e6d0d..78a6e23 100644
--- a/pcbnew/footprint_wizard_frame.h
+++ b/pcbnew/footprint_wizard_frame.h
@@ -39,7 +39,7 @@ 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;
 
 /**
diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.cpp b/pcbnew/scripting/pcbnew_footprint_wizards.cpp
index 8e4d9ed..1208819 100644
--- a/pcbnew/scripting/pcbnew_footprint_wizards.cpp
+++ b/pcbnew/scripting/pcbnew_footprint_wizards.cpp
@@ -273,12 +273,24 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterHints( int aPage )
 {
 	PyLOCK          lock;
 
-	    PyObject*       arglist = Py_BuildValue( "(i)", aPage );
-	    wxArrayString   ret = CallRetArrayStrMethod( "GetParameterHints", arglist );
+	PyObject*       arglist = Py_BuildValue( "(i)", aPage );
+	wxArrayString   ret = CallRetArrayStrMethod( "GetParameterHints", arglist );
 
-	    Py_DECREF( arglist );
+	Py_DECREF( arglist );
 
-	    return ret;
+	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 )
diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.h b/pcbnew/scripting/pcbnew_footprint_wizards.h
index 7d2c922..6960085 100644
--- a/pcbnew/scripting/pcbnew_footprint_wizards.h
+++ b/pcbnew/scripting/pcbnew_footprint_wizards.h
@@ -55,6 +55,7 @@ public:
     wxArrayString   GetParameterValues( int aPage );
     wxArrayString   GetParameterErrors( int aPage );
     wxArrayString   GetParameterHints( int aPage );
+    wxArrayString	GetParameterDesignators( int aPage = 0);
 
     // must return an empty string or an error description
     wxString        SetParameterValues( int aPage, wxArrayString& aValues );
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index f0096a2..466ea5a 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -217,7 +217,7 @@ class FootprintWizardParameter(object):
     uUnits = [uMM, uMils, uFloat, uInteger, uBool, uDegrees, uRadians, uPercent, uString]  # List of allowable units
 
     _true = ['true','t','y','yes','on','1']
-    _false = ['false','f','n','no','off','0']
+    _false = ['false','f','n','no','off','0','',None]
     
     _bools = _true + _false
 
@@ -229,7 +229,7 @@ class FootprintWizardParameter(object):
         self.raw_value = str(default)
         
         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)
+        self.designator = kwarg.get('designator',' ')  # Parameter designator such as "e, D, p" (etc)
         
         self.multiple = int(kwarg.get('multiple',1))  # Check integer values are multiples of this number
         self.min_value = kwarg.get('min_value',0) # Check numeric values are above or equal to this number

--------------1.9.5.msysgit.0--



From c251d7b277acb2c3715b4c857b7baca2e6afc552 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 10 Sep 2016 16:55:50 +1000
Subject: [PATCH 09/16] Added button to reset wizard parameters to their
 default values
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/class_footprint_wizard.h             |  6 ++++++
 pcbnew/footprint_wizard.cpp                 | 16 ++++++++++++++++
 pcbnew/footprint_wizard_frame.cpp           | 10 ++++++++++
 pcbnew/footprint_wizard_frame.h             |  2 ++
 pcbnew/pcbnew_id.h                          |  1 +
 pcbnew/scripting/pcbnew_footprint_wizards.h |  3 +++
 scripting/kicadplugins.i                    |  6 +++++-
 7 files changed, 43 insertions(+), 1 deletion(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0009-Added-button-to-reset-wizard-parameters-to-their-def.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0009-Added-button-to-reset-wizard-parameters-to-their-def.patch"

diff --git a/pcbnew/class_footprint_wizard.h b/pcbnew/class_footprint_wizard.h
index 933abaf..49b4cac 100644
--- a/pcbnew/class_footprint_wizard.h
+++ b/pcbnew/class_footprint_wizard.h
@@ -138,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 013bd4f..a1b3280 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 )
 {
diff --git a/pcbnew/footprint_wizard_frame.cpp b/pcbnew/footprint_wizard_frame.cpp
index ed75944..5ec9474 100644
--- a/pcbnew/footprint_wizard_frame.cpp
+++ b/pcbnew/footprint_wizard_frame.cpp
@@ -62,6 +62,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 )
 
@@ -615,6 +618,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" ) );
diff --git a/pcbnew/footprint_wizard_frame.h b/pcbnew/footprint_wizard_frame.h
index 78a6e23..7b0a501 100644
--- a/pcbnew/footprint_wizard_frame.h
+++ b/pcbnew/footprint_wizard_frame.h
@@ -168,6 +168,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_id.h b/pcbnew/pcbnew_id.h
index 73802ee..205a6c3 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -389,6 +389,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/scripting/pcbnew_footprint_wizards.h b/pcbnew/scripting/pcbnew_footprint_wizards.h
index 6960085..9b6a25f 100644
--- a/pcbnew/scripting/pcbnew_footprint_wizards.h
+++ b/pcbnew/scripting/pcbnew_footprint_wizards.h
@@ -59,6 +59,9 @@ public:
 
     // must return an empty string or an error description
     wxString        SetParameterValues( int aPage, wxArrayString& aValues );
+
+    void			ResetParameters();
+
     MODULE*         GetFootprint( wxString * aMessages );
     void*           GetObject();
 };
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 466ea5a..e52a1fe 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -252,7 +252,7 @@ class FootprintWizardParameter(object):
     
         if ',' in self.units:  # Select from a list of values
             if self.raw_value not in self.units.split(','):
-                self.error_list.append("value '{v}' is not in list of options [{l}]".format(v=self.raw_value, l=str(self.units)))
+                self.raw_value = self.units.split(',')[0]
     
         if self.units not in self.uUnits and ',' not in self.units:  # Allow either valid units or a list of strings
             self.error_list.append("type '{t}' unknown".format(t=self.units))
@@ -393,6 +393,10 @@ class FootprintWizardPlugin(KiCadPlugin, object):
     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
 

--------------1.9.5.msysgit.0--



From 9eefa78d6618aa61667795bb7888ebd90bc16f56 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 10 Sep 2016 17:18:21 +1000
Subject: [PATCH 10/16] Moved parameter unit definitions to the base of
 pcbnew.py such that user scripts can refer to 'pcbnew.uMM' (etc)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 scripting/kicadplugins.i | 192 ++++++++++++++++++++++-------------------------
 1 file changed, 89 insertions(+), 103 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0010-Moved-parameter-unit-definitions-to-the-base-of-pcbn.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0010-Moved-parameter-unit-definitions-to-the-base-of-pcbn.patch"

diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index e52a1fe..2b5daba 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -202,23 +202,22 @@ class FilePlugin(KiCadPlugin):
 
 from math import ceil, floor, sqrt
 
-class FootprintWizardParameter(object):
-
-    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
-
-    uUnits = [uMM, uMils, uFloat, uInteger, uBool, uDegrees, uRadians, uPercent, uString]  # List of allowable units
+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
+
+uUnits = [uMM, uMils, uFloat, uInteger, uBool, uDegrees, uRadians, uPercent, uString]  # List of allowable units
 
+class FootprintWizardParameter(object):
     _true = ['true','t','y','yes','on','1']
     _false = ['false','f','n','no','off','0','',None]
-    
+
     _bools = _true + _false
 
     def __init__(self, page, name, units, default, **kwarg):
@@ -227,105 +226,105 @@ class FootprintWizardParameter(object):
         self.units = units
         self.default = str(default)
         self.raw_value = str(default)
-        
+
         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)
-        
+
         self.multiple = int(kwarg.get('multiple',1))  # Check integer values are multiples of this number
         self.min_value = kwarg.get('min_value',0) # 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
-        
+
         if type(self.units) in [list, tuple]:  # Convert a list of options into a single string
             self.units = ",".join([str(el) for el in self.units])
-            
+
         if self.units.lower() == 'percent':
-            self.units = self.uPercent
-        
+            self.units = uPercent
+
         self.Check()  # Compare units to default values
-        
+
     def Check(self):  # Check the value to ensure that the data type matches the units
-    
+
         self.error_list = []  # Clear the error list
-        
-        if self.units.lower() in self.uUnits:
+
+        if self.units.lower() in uUnits:
             self.units = self.units.lower()
-    
+
         if ',' in self.units:  # Select from a list of values
             if self.raw_value not in self.units.split(','):
                 self.raw_value = self.units.split(',')[0]
-    
-        if self.units not in self.uUnits and ',' not in self.units:  # Allow either valid units or a list of strings
+
+        if self.units not in uUnits and ',' not in self.units:  # Allow either valid units or a list of strings
             self.error_list.append("type '{t}' unknown".format(t=self.units))
-            self.error_list.append("Allowable types: " + str(uUnits))
-            
-        if self.units in [self.uMM, self.uMils, self.uFloat, self.uInteger, self.uRadians, self.uDegrees, self.uPercent]:  # Generic number test
+            self.error_list.append("Allowable types: " + str(self.units))
+
+        if self.units in [uMM, uMils, uFloat, uInteger, uRadians, uDegrees, uPercent]:  # Generic number test
             try:
                 to_num = float(self.raw_value)
-                
+
                 if self.min_value is not None:  # Check minimum value if it is present
                     if to_num < self.min_value:
                         self.error_list.append("value '{v}' is below minimum ({m})".format(v=self.raw_value,m=self.min_value))
-                        
+
                 if self.max_value is not None:  # Check maximum value if it is present
                     if to_num > self.max_value:
                         self.error_list.append("value '{v}' is above maximum ({m})".format(v=self.raw_value,m=self.max_value))
-                        
-                if self.units == self.uPercent: 
+
+                if self.units == uPercent: 
                     if to_num < 0 or to_num > 100:
                         self.error_list.append("'{v}' is not a valid percentage (0% to 100%)".format(v=self.raw_value))
-                
+
             except:
                 self.error_list.append("value '{v}' is not of type '{t}'".format(v = self.raw_value, t=self.units))
 
-        if self.units == self.uInteger:  # Perform integer specific checks
+        if self.units == uInteger:  # Perform integer specific checks
             try:
                 to_int = int(self.raw_value)
-                
+
                 if self.multiple > 1:
                     if (to_int % self.multiple) > 0:
                         self.error_list.append("value '{v}' is not a multiple of {m}".format(v=self.raw_value,m=self.multiple))
             except:
                 self.error_list.append("value {'v}' is not an integer".format(v=self.raw_value))
-                
-        if self.units == self.uBool:  # Check that the value is of a correct boolean format
+
+        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.error_list.append("value '{v}' is not a boolean value".format(v = self.raw_value))
-        
+
     @property
     def errors(self):  # Return a (formatted) error string
         self.Check()  # Automatically check errors
         return self.error_list
-        
+
     @property
     def value(self):  # Return the current value, converted to internal units (from string representation) if required
-        if self.units == self.uMM:  # Convert from MM to internal units
+        if self.units == uMM:  # Convert from MM to internal units
             return FromMM(float(self.raw_value))
-        elif self.units == self.uMils:  # Convert from Mils to internal units
+        elif self.units == uMils:  # Convert from Mils to internal units
             return FromMils(float(self.raw_value))
-        elif self.units == self.uBool:
+        elif self.units == uBool:
             if self.raw_value == True or str(self.raw_value).lower() in self._true:
                 return True
             else:
                 return False
-        elif self.units == self.uInteger:
+        elif self.units == uInteger:
             return int(self.raw_value)
-        elif self.units == self.uPercent:
+        elif self.units == uPercent:
             percent = float(self.raw_value)
             if percent < 0:
                 percent = 0
             elif percent > 100:
                 percent = 100
             return percent
-        elif self.units == self.uDegrees:  # Angles should be expressed in radians
+        elif self.units == uDegrees:  # Angles should be expressed in radians
             angle = float(self.raw_value)
             while angle < -180: 
                 angle += 360
             while angle > 180:
                 angle -= 360
             return angle * math.pi / 180
-        elif self.units == self.uRadians:
+        elif self.units == uRadians:
             angle = float(self.raw_value)
             while angle < -math.pi:
                 angle += 2 * math.pi
@@ -335,27 +334,14 @@ class FootprintWizardParameter(object):
 
         else:
             return self.raw_value
-            
+
     def DefaultValue(self):
         self.raw_value = str(self.default)
-            
+
     def SetValue(self, new_value):  # Update the value
         self.raw_value = str(new_value)
 
 class FootprintWizardPlugin(KiCadPlugin, object):
-    
-    uMM         = FootprintWizardParameter.uMM      # Millimeters
-    uMils       = FootprintWizardParameter.uMils    # Mils
-    uFloat      = FootprintWizardParameter.uFloat   # Natural number units (dimensionless)
-    uInteger    = FootprintWizardParameter.uInteger # Integer (no decimals, numeric, dimensionless)
-    uBool       = FootprintWizardParameter.uBool    # Boolean value
-    uRadians    = FootprintWizardParameter.uRadians # Angular units (radians)
-    uDegrees    = FootprintWizardParameter.uDegrees # Angular units (degrees)
-    uPercent    = FootprintWizardParameter.uPercent # Percent (0% -> 100%)
-    uString     = FootprintWizardParameter.uString  # Raw string
-
-    uUnits      = FootprintWizardParameter.uUnits   # List of allowable units
-    
     def __init__(self):
         KiCadPlugin.__init__(self)
         self.defaults()
@@ -363,40 +349,40 @@ class FootprintWizardPlugin(KiCadPlugin, object):
     def defaults(self):
         self.module = None
         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 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
 
@@ -405,30 +391,30 @@ class FootprintWizardPlugin(KiCadPlugin, object):
 
     def GetDescription(self):  # Return the description text 
         return self.description
-        
+
     def GetValue(self):
         raise NotImplementedError
-        
+
     def GetReferencePrefix(self):
         return "REF"  # Default reference prefix for any footprint
-        
+
     def GetParam(self, page, name):  # Grab a parameter
         for p in self.params:
             if p.page == page and p.name == name:
                 return p
-                
+
         return None
-        
+
     def AnyErrors(self):
         return any([len(p.errors) > 0 for p in self.params])
-        
+
     @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)
-                
+
         return page_list
 
     def GetNumParameterPages(self):  # Return the number of parameter pages
@@ -436,27 +422,27 @@ class FootprintWizardPlugin(KiCadPlugin, object):
 
     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 = []
-        
+
         for p in self.params:
             if p.page == page_name:
                 params.append(p)
-            
+
         return params
-        
+
     def GetParametersByPageIndex(self, page_index):  # Return an ordered list of parameters on a given page    
         return self.GetParametersByPageName(self.GetParameterPageName(page_index))
-        
+
     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]
@@ -464,32 +450,32 @@ class FootprintWizardPlugin(KiCadPlugin, object):
     def GetParameterErrors(self,page_index):  # Return list of errors associated with a given page
         params = self.GetParametersByPageIndex(page_index)
         return [str("\n".join(p.errors)) 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()
         return self.module
-        
+
     def BuildFootprint(self):
         return
 
@@ -499,21 +485,21 @@ class FootprintWizardPlugin(KiCadPlugin, object):
     def Show(self):
         text  = "Footprint Wizard Name:        {name}\n".format(name=self.GetName())
         text += "Footprint Wizard Description: {desc}\n".format(desc=self.GetDescription())
-        
+
         n_pages = self.GetNumParameterPages()
-        
+
         text += "Pages: {n}\n".format(n=n_pages)
-        
+
         for i in range(n_pages):
             name = self.GetParameterPageName(i)
-            
+
             values = self.GetParameterValues(i)
             names =  self.GetParameterNames(i)
             units =  self.GetParameterTypes(i)
             errors = self.GetParameterErrors(i)
-            
+
             text += "page {i}) {name}\n".format(i=i,name=name)
-            
+
             for j in range(len(values)):
                 text += ("\t{name}: {val} ({units}){err}\n".format(
                     name=names[j],
@@ -524,7 +510,7 @@ class FootprintWizardPlugin(KiCadPlugin, object):
 
         if self.AnyErrors():
             text += " * Errors exist for these parameters"
-        
+
         return text
         
 class ActionPlugin(KiCadPlugin):

--------------1.9.5.msysgit.0--



From 14685c183e88d3b3cfc2b2b45b102d08572207c5 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 10 Sep 2016 21:22:38 +1000
Subject: [PATCH 11/16] Reimplemented CheckParameters() functions Simplified
 error checking Set all default parameter checks to None Removed hard-coded
 angle checks (they can be user specified) Improved parameter printing
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/scripting/plugins/FootprintWizardBase.py |  62 +++++++++----
 scripting/kicadplugins.i                        | 115 +++++++++++++-----------
 2 files changed, 108 insertions(+), 69 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0011-Reimplemented-CheckParameters-functions.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0011-Reimplemented-CheckParameters-functions.patch"

diff --git a/pcbnew/scripting/plugins/FootprintWizardBase.py b/pcbnew/scripting/plugins/FootprintWizardBase.py
index 0c96375..0f2c3d0 100644
--- a/pcbnew/scripting/plugins/FootprintWizardBase.py
+++ b/pcbnew/scripting/plugins/FootprintWizardBase.py
@@ -39,11 +39,35 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         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():
-        pass
+    def CheckParameters(self):
+        """ 
+        Any custom parameter checking should be performed here
+        """
+        raise NotImplementedError
 
     def BuildThisFootprint(self):
         """
@@ -62,6 +86,13 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         """
 
         self.buildmessages = ""
+        
+        # Perform default checks on all params
+        for p in self.params:
+            p.ClearErrors()
+            p.Check() # use defaults
+            
+        self.CheckParameters() # User error checks
 
         self.module = pcbnew.MODULE(None)  # create a new module
 
@@ -70,15 +101,10 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
             self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
             
             for p in self.params:
-                if len(p.errors) > 0:
-                    n_errors = len(p.errors)
-                    if n_errors == 1:
-                        error_string = "1 error"
-                    else:
-                        error_string = "{n} errors".format(n=n_errors)
-                    self.buildmessages +="['{page}']['{name}']: ({err})\n".format(page=p.page,name=p.name,err=error_string)
+                if len(p.error_list) > 0:
+                    self.buildmessages +="['{page}']['{name}']:\n".format(page=p.page,name=p.name)
                     
-                    for error in p.errors:
+                    for error in p.error_list:
                         self.buildmessages += "\t" + error + "\n"
                     
             return
@@ -397,6 +423,7 @@ class FootprintWizardDrawingAids:
         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)
 
@@ -465,21 +492,22 @@ class FootprintWizardDrawingAids:
 
         _PolyLineInternal(pts)  # original
 
-        if mirrorX is not None:
-            self.TransformFlip(mirrorX, 0, self.flipX)
+        if mirrorX is not None and mirrorY is not None:
+            self.TransformFlip(mirrorX, mirrorY, self.flipBoth)  # both
             _PolyLineInternal(pts)
             self.PopTransform()
-
-        if mirrorY is not None:
-            self.TransformFlipOrigin(0, mirrorY, self.flipY)
+            
+        elif mirrorX is not None:
+            self.TransformFlip(mirrorX, 0, self.flipX)
             _PolyLineInternal(pts)
             self.PopTransform()
 
-        if mirrorX is not None and mirrorY is not None:
-            self.TransformFlip(mirrorX, mirrorY, self.flipBoth)  # both
+        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.
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 2b5daba..31c0ab4 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -231,7 +231,7 @@ class FootprintWizardParameter(object):
         self.designator = kwarg.get('designator',' ')  # Parameter designator such as "e, D, p" (etc)
 
         self.multiple = int(kwarg.get('multiple',1))  # Check integer values are multiples of this number
-        self.min_value = kwarg.get('min_value',0) # Check numeric values are above or equal to 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
 
         if type(self.units) in [list, tuple]:  # Convert a list of options into a single string
@@ -240,12 +240,30 @@ class FootprintWizardParameter(object):
         if self.units.lower() == 'percent':
             self.units = uPercent
 
-        self.Check()  # Compare units to default values
-
-    def Check(self):  # Check the value to ensure that the data type matches the units
-
-        self.error_list = []  # Clear the error list
-
+        self.ClearErrors()
+        self.Check()           
+        
+    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.lower() in uUnits:
             self.units = self.units.lower()
 
@@ -254,48 +272,43 @@ class FootprintWizardParameter(object):
                 self.raw_value = self.units.split(',')[0]
 
         if self.units not in uUnits and ',' not in self.units:  # Allow either valid units or a list of strings
-            self.error_list.append("type '{t}' unknown".format(t=self.units))
-            self.error_list.append("Allowable types: " + str(self.units))
+            self.AddError("type '{t}' unknown".format(t=self.units),info)
+            self.AddError("Allowable types: " + str(self.units),info)
 
         if self.units in [uMM, uMils, uFloat, uInteger, uRadians, uDegrees, uPercent]:  # Generic number test
             try:
                 to_num = float(self.raw_value)
 
-                if self.min_value is not None:  # Check minimum value if it is present
-                    if to_num < self.min_value:
-                        self.error_list.append("value '{v}' is below minimum ({m})".format(v=self.raw_value,m=self.min_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 self.max_value is not None:  # Check maximum value if it is present
-                    if to_num > self.max_value:
-                        self.error_list.append("value '{v}' is above maximum ({m})".format(v=self.raw_value,m=self.max_value))
+                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)
 
                 if self.units == uPercent: 
                     if to_num < 0 or to_num > 100:
-                        self.error_list.append("'{v}' is not a valid percentage (0% to 100%)".format(v=self.raw_value))
+                        self.AddError("'{v}' is not a valid percentage (0% to 100%)".format(v=self.raw_value),info)
 
             except:
-                self.error_list.append("value '{v}' is not of type '{t}'".format(v = self.raw_value, t=self.units))
+                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 self.multiple > 1:
-                    if (to_int % self.multiple) > 0:
-                        self.error_list.append("value '{v}' is not a multiple of {m}".format(v=self.raw_value,m=self.multiple))
+                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.error_list.append("value {'v}' is not an integer".format(v=self.raw_value))
+                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.error_list.append("value '{v}' is not a boolean value".format(v = self.raw_value))
-
-    @property
-    def errors(self):  # Return a (formatted) error string
-        self.Check()  # Automatically check errors
-        return self.error_list
+                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 internal units (from string representation) if required
@@ -317,19 +330,8 @@ class FootprintWizardParameter(object):
             elif percent > 100:
                 percent = 100
             return percent
-        elif self.units == uDegrees:  # Angles should be expressed in radians
-            angle = float(self.raw_value)
-            while angle < -180: 
-                angle += 360
-            while angle > 180:
-                angle -= 360
-            return angle * math.pi / 180
-        elif self.units == uRadians:
+        elif self.units in [uDegrees, uRadians]:  # Angles should be expressed in radians
             angle = float(self.raw_value)
-            while angle < -math.pi:
-                angle += 2 * math.pi
-            while angle >  math.pi:
-                angle -= 2 * math.pi
             return angle
 
         else:
@@ -340,6 +342,17 @@ class FootprintWizardParameter(object):
 
     def SetValue(self, new_value):  # Update the value
         self.raw_value = str(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
+            self.name + ": {b}".format(b = "True" if self.value else "False")
+            
+        return s
 
 class FootprintWizardPlugin(KiCadPlugin, object):
     def __init__(self):
@@ -405,8 +418,11 @@ class FootprintWizardPlugin(KiCadPlugin, object):
 
         return None
 
+    def CheckParam(self, page, name, **kwarg):
+        self.GetParam(page,name).Check(**kwarg)
+        
     def AnyErrors(self):
-        return any([len(p.errors) > 0 for p in self.params])
+        return any([len(p.error_list) > 0 for p in self.params])
 
     @property
     def pages(self):  # Return an (ordered) list of the available page names
@@ -449,7 +465,7 @@ class FootprintWizardPlugin(KiCadPlugin, object):
 
     def GetParameterErrors(self,page_index):  # Return list of errors associated with a given page
         params = self.GetParametersByPageIndex(page_index)
-        return [str("\n".join(p.errors)) for p in params]
+        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)
@@ -493,19 +509,14 @@ class FootprintWizardPlugin(KiCadPlugin, object):
         for i in range(n_pages):
             name = self.GetParameterPageName(i)
 
-            values = self.GetParameterValues(i)
-            names =  self.GetParameterNames(i)
-            units =  self.GetParameterTypes(i)
-            errors = self.GetParameterErrors(i)
+            params = self.GetParametersByPageName(name)
 
-            text += "page {i}) {name}\n".format(i=i,name=name)
+            text += "{name}\n".format(name=name)
 
-            for j in range(len(values)):
-                text += ("\t{name}: {val} ({units}){err}\n".format(
-                    name=names[j],
-                    val=values[j],
-                    units=units[j],
-                    err = ' *' if errors[j] else ''
+            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():

--------------1.9.5.msysgit.0--



From 314e090d282f72679755f3cfa6cf353649097cb6 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Mon, 12 Sep 2016 22:32:58 +1000
Subject: [PATCH 12/16] Updated wizard scripts to match new requirements
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 .../plugins/FPC_(SMD_type)_footprintwizard.py      | 159 ---------------
 pcbnew/scripting/plugins/FPC_wizard.py             | 157 +++++++++++++++
 pcbnew/scripting/plugins/FootprintWizardBase.py    |  13 +-
 pcbnew/scripting/plugins/bga_wizard.py             | 120 ++++++++---
 .../scripting/plugins/circular_pad_array_wizard.py |  86 +++++---
 pcbnew/scripting/plugins/qfn_wizard.py             | 222 ++++++++++++---------
 pcbnew/scripting/plugins/qfp_wizard.py             | 105 ++++++----
 pcbnew/scripting/plugins/sdip_wizard.py            |  38 ++--
 pcbnew/scripting/plugins/touch_slider_wizard.py    |  57 +++---
 pcbnew/scripting/plugins/uss39_barcode.py          |  11 +-
 pcbnew/scripting/plugins/zip_wizard.py             | 115 +++++------
 11 files changed, 618 insertions(+), 465 deletions(-)
 delete mode 100644 pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py
 create mode 100644 pcbnew/scripting/plugins/FPC_wizard.py


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0012-Updated-wizard-scripts-to-match-new-requirements.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0012-Updated-wizard-scripts-to-match-new-requirements.patch"

diff --git a/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py b/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py
deleted file mode 100644
index bf77e93..0000000
--- a/pcbnew/scripting/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/scripting/plugins/FPC_wizard.py b/pcbnew/scripting/plugins/FPC_wizard.py
new file mode 100644
index 0000000..7f10b0b
--- /dev/null
+++ b/pcbnew/scripting/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/scripting/plugins/FootprintWizardBase.py b/pcbnew/scripting/plugins/FootprintWizardBase.py
index 0f2c3d0..fa340d6 100644
--- a/pcbnew/scripting/plugins/FootprintWizardBase.py
+++ b/pcbnew/scripting/plugins/FootprintWizardBase.py
@@ -22,6 +22,17 @@ import math
 # 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
@@ -86,6 +97,7 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         """
 
         self.buildmessages = ""
+        self.module = pcbnew.MODULE(None)  # create a new module
         
         # Perform default checks on all params
         for p in self.params:
@@ -94,7 +106,6 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
             
         self.CheckParameters() # User error checks
 
-        self.module = pcbnew.MODULE(None)  # create a new module
 
         if self.AnyErrors():  # Errors were detected!
             
diff --git a/pcbnew/scripting/plugins/bga_wizard.py b/pcbnew/scripting/plugins/bga_wizard.py
index 8ce17e6..6529bca 100644
--- a/pcbnew/scripting/plugins/bga_wizard.py
+++ b/pcbnew/scripting/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,44 +29,57 @@ class BGAPadGridArray(PA.PadGridArray):
             n_x + 1)
 
 
-class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
+class BGAWizard(FootprintWizardBase.FootprintWizard):
 
     def GetName(self):
         return "BGA"
 
     def GetDescription(self):
         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"])
-
-        return "BGA_%d" % pins
+        pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"])
+        
+        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/scripting/plugins/circular_pad_array_wizard.py b/pcbnew/scripting/plugins/circular_pad_array_wizard.py
index 7a42c8f..6c3e995 100644
--- a/pcbnew/scripting/plugins/circular_pad_array_wizard.py
+++ b/pcbnew/scripting/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):
+        
+        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")
 
-        self.CheckParamInt("Pads", "*n")
-        self.CheckParamInt("Pads", "*first pad number")
-        self.CheckParamBool("Pads", "*number clockwise")
 
     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/scripting/plugins/qfn_wizard.py b/pcbnew/scripting/plugins/qfn_wizard.py
index d6c3a61..0e1bbc6 100644
--- a/pcbnew/scripting/plugins/qfn_wizard.py
+++ b/pcbnew/scripting/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/scripting/plugins/qfp_wizard.py b/pcbnew/scripting/plugins/qfp_wizard.py
index 04664aa..c50d86e 100644
--- a/pcbnew/scripting/plugins/qfp_wizard.py
+++ b/pcbnew/scripting/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/scripting/plugins/sdip_wizard.py b/pcbnew/scripting/plugins/sdip_wizard.py
index fad99f1..e69d52d 100644
--- a/pcbnew/scripting/plugins/sdip_wizard.py
+++ b/pcbnew/scripting/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/scripting/plugins/touch_slider_wizard.py b/pcbnew/scripting/plugins/touch_slider_wizard.py
index e27fffb..0970cfd 100644
--- a/pcbnew/scripting/plugins/touch_slider_wizard.py
+++ b/pcbnew/scripting/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,8 +194,12 @@ 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)
diff --git a/pcbnew/scripting/plugins/uss39_barcode.py b/pcbnew/scripting/plugins/uss39_barcode.py
index ee86775..7821153 100644
--- a/pcbnew/scripting/plugins/uss39_barcode.py
+++ b/pcbnew/scripting/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/scripting/plugins/zip_wizard.py b/pcbnew/scripting/plugins/zip_wizard.py
index 4ffa7f1..aeb45d3 100644
--- a/pcbnew/scripting/plugins/zip_wizard.py
+++ b/pcbnew/scripting/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)
+        
+        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']
+
+    @property
+    def body(self):
+        return self.parameters['Body']
+        
     def CheckParameters(self):
-        self.CheckParamInt("Pads", '*' + self.pad_count_key)
-        self.CheckParamInt("Pads", '*' + self.line_count_key)
-
-        # can do this internally to parameter manager?
-        self.CheckParamBool("Body", '*' + self.silkscreen_inside_key)
+        # 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
@@ -78,8 +67,8 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
         # 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)
 

--------------1.9.5.msysgit.0--



From a7ad452768b0320c63f0c1b5d489a8eac97ebda1 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Mon, 12 Sep 2016 22:46:54 +1000
Subject: [PATCH 13/16] Method call to reset script parameters to default
 values
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/scripting/pcbnew_footprint_wizards.cpp | 7 +++++++
 1 file changed, 7 insertions(+)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0013-Method-call-to-reset-script-parameters-to-default-va.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0013-Method-call-to-reset-script-parameters-to-default-va.patch"

diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.cpp b/pcbnew/scripting/pcbnew_footprint_wizards.cpp
index 1208819..6de4b06 100644
--- a/pcbnew/scripting/pcbnew_footprint_wizards.cpp
+++ b/pcbnew/scripting/pcbnew_footprint_wizards.cpp
@@ -317,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 );

--------------1.9.5.msysgit.0--



From 9e54e3cdb949f0bd8ceea05656c2533c1f4e6e9e Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 13 Sep 2016 19:02:33 +1000
Subject: [PATCH 14/16] Reject blank string values sent to the python wizard
 parameter(s)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 scripting/kicadplugins.i | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0014-Reject-blank-string-values-sent-to-the-python-wizard.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0014-Reject-blank-string-values-sent-to-the-python-wizard.patch"

diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 31c0ab4..8b92599 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -341,8 +341,13 @@ class FootprintWizardParameter(object):
         self.raw_value = str(self.default)
 
     def SetValue(self, new_value):  # Update the value
-        self.raw_value = str(new_value)
+        new_value = str(new_value).strip()
         
+        if len(new_value) == 0:
+            if not self.units in [uString, uBool]:
+                return  # Ignore empty values unless for string
+        self.raw_value = str(new_value)
+    
     def __str__(self):  # pretty-print the parameter
         
         s = self.name + ": " + str(self.raw_value)

--------------1.9.5.msysgit.0--



From dae66afdb93cfdffd5b3a52652da63dee1cd4f22 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 13 Sep 2016 20:45:54 +1000
Subject: [PATCH 15/16] Fixed boolean parameter rendering Added auto-resize of
 parameter grid columns
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/dialogs/dialog_footprint_wizard_list.cpp | 23 +++++--
 pcbnew/footprint_wizard.cpp                     |  4 +-
 pcbnew/footprint_wizard_frame.cpp               | 80 ++++++++++++++++++-------
 pcbnew/footprint_wizard_frame.h                 | 19 ++++--
 4 files changed, 94 insertions(+), 32 deletions(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0015-Fixed-boolean-parameter-rendering.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0015-Fixed-boolean-parameter-rendering.patch"

diff --git a/pcbnew/dialogs/dialog_footprint_wizard_list.cpp b/pcbnew/dialogs/dialog_footprint_wizard_list.cpp
index 8c49d6b..5bc650d 100644
--- a/pcbnew/dialogs/dialog_footprint_wizard_list.cpp
+++ b/pcbnew/dialogs/dialog_footprint_wizard_list.cpp
@@ -33,9 +33,14 @@
 #include <kiface_i.h>
 #include <dialog_footprint_wizard_list.h>
 #include <class_footprint_wizard.h>
+#include <python_scripting.h>
+
+enum FPGeneratorRowNames
+{
+	FP_GEN_ROW_NAME = 0,
+	FP_GEN_ROW_DESCR,
+};
 
-#define ROW_NAME 0
-#define ROW_DESCR 1
 #define FPWIZARTDLIST_HEIGHT_KEY wxT( "FpWizardListHeight" )
 #define FPWIZARTDLIST_WIDTH_KEY  wxT( "FpWizardListWidth" )
 
@@ -64,11 +69,21 @@ DIALOG_FOOTPRINT_WIZARD_LIST::DIALOG_FOOTPRINT_WIZARD_LIST( wxWindow* aParent )
         wxString description = wizard->GetDescription();
         wxString image = wizard->GetImage();
 
-        m_footprintGeneratorsGrid->SetCellValue( i, ROW_NAME, name );
-        m_footprintGeneratorsGrid->SetCellValue( i, ROW_DESCR, description );
+        m_footprintGeneratorsGrid->SetCellValue( i, FP_GEN_ROW_NAME, name );
+        m_footprintGeneratorsGrid->SetCellValue( i, FP_GEN_ROW_DESCR, description );
 
     }
 
+    m_footprintGeneratorsGrid->AutoSizeColumns();
+
+    // Auto-expand the description column
+    int width = m_footprintGeneratorsGrid->GetClientSize().GetWidth() -
+    			m_footprintGeneratorsGrid->GetRowLabelSize() -
+    			m_footprintGeneratorsGrid->GetColSize( FP_GEN_ROW_NAME );
+
+    if ( width > m_footprintGeneratorsGrid->GetColMinimalAcceptableWidth() )
+    	m_footprintGeneratorsGrid->SetColSize( FP_GEN_ROW_DESCR, width );
+
     // Select the first row
     m_footprintGeneratorsGrid->ClearSelection();
     m_footprintGeneratorsGrid->SelectRow( 0, false );
diff --git a/pcbnew/footprint_wizard.cpp b/pcbnew/footprint_wizard.cpp
index a1b3280..3227e47 100644
--- a/pcbnew/footprint_wizard.cpp
+++ b/pcbnew/footprint_wizard.cpp
@@ -239,12 +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 );
+        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 5ec9474..19ca019 100644
--- a/pcbnew/footprint_wizard_frame.cpp
+++ b/pcbnew/footprint_wizard_frame.cpp
@@ -78,6 +78,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 )
@@ -85,10 +86,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" )
 
@@ -259,6 +256,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 )
 {
@@ -282,9 +286,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();
 
@@ -292,6 +297,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 );
 }
 
 
@@ -366,19 +376,18 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateParameterList()
     	m_parameterGrid->SetRowLabelValue( i, designator );
 
     	// Set the 'Name'
-        m_parameterGrid->SetCellValue( i, m_columnPrmName, name );
-        m_parameterGrid->SetReadOnly( i, m_columnPrmName );
-
-        // Set the 'Value'
-        m_parameterGrid->SetCellValue( i, m_columnPrmValue, value );
+        m_parameterGrid->SetCellValue( i, WIZ_COL_NAME, name );
+        m_parameterGrid->SetReadOnly( i, WIZ_COL_NAME );
 
         // Set the editor type of the
 
         // Boolean parameters can be displayed using a checkbox
         if ( units == WIZARD_PARAM_UNITS_BOOL )
         {
-        	m_parameterGrid->SetCellEditor( i, m_columnPrmValue, new wxGridCellBoolEditor );
-        	m_parameterGrid->SetCellRenderer( i, m_columnPrmValue, new wxGridCellBoolRenderer );
+        	wxGridCellBoolEditor *boolEditor = new wxGridCellBoolEditor;
+        	boolEditor->UseStringValues("True","False");
+        	m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, boolEditor );
+        	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
@@ -391,14 +400,14 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateParameterList()
         		options.Add( tokenizer.GetNextToken() );
         	}
 
-        	m_parameterGrid->SetCellEditor( i, m_columnPrmValue, new wxGridCellChoiceEditor( options ) );
+        	m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellChoiceEditor( options ) );
 
-        	units = wxT( "<list>" );
+        	units = wxT( "" );
         }
         // Integer parameters
         else if ( units == WIZARD_PARAM_UNITS_INTEGER )
 		{
-			m_parameterGrid->SetCellEditor( i, m_columnPrmValue, new wxGridCellNumberEditor );
+			m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellNumberEditor );
 		}
         // Non-integer numerical parameters
         else if ( ( units == WIZARD_PARAM_UNITS_MM ) ||
@@ -408,16 +417,45 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateParameterList()
 				  ( units == WIZARD_PARAM_UNITS_DEGREES ) ||
 				  ( units == WIZARD_PARAM_UNITS_PERCENT ) )
         {
-        	m_parameterGrid->SetCellEditor( i, m_columnPrmValue, new wxGridCellFloatEditor );
+        	m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellFloatEditor );
         }
 
 
         // Set the 'Units'
-        m_parameterGrid->SetCellValue( i, m_columnPrmUnit, units );
-        m_parameterGrid->SetReadOnly( i, m_columnPrmUnit );
+        m_parameterGrid->SetCellValue( i, WIZ_COL_UNITS, units );
+        m_parameterGrid->SetReadOnly( i, WIZ_COL_UNITS );
+
+        // Set the 'Value'
+        m_parameterGrid->SetCellValue( i, WIZ_COL_VALUE, value );
+
+        // Center the value text
+        m_parameterGrid->SetCellAlignment( i, WIZ_COL_VALUE, wxALIGN_CENTRE, wxALIGN_CENTRE );
     }
 
-    m_parameterGrid->AutoSizeColumns();
+    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 );
+	}
 }
 
 
diff --git a/pcbnew/footprint_wizard_frame.h b/pcbnew/footprint_wizard_frame.h
index 7b0a501..7015767 100644
--- a/pcbnew/footprint_wizard_frame.h
+++ b/pcbnew/footprint_wizard_frame.h
@@ -42,6 +42,13 @@ class FOOTPRINT_EDIT_FRAME;
 // 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
@@ -74,6 +76,8 @@ public:
 
 private:
 
+    void				OnGridSize( wxSizeEvent& aSizeEvent );
+
     void                OnSize( wxSizeEvent& event );
 
     /**
@@ -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
      */

--------------1.9.5.msysgit.0--



From 690733e54ba4f41e6e1f13124ce04be69784b6c7 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Wed, 14 Sep 2016 18:12:20 +1000
Subject: [PATCH 16/16] Removed wxGridCellBoolRenderer (actually works better
 without it)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------1.9.5.msysgit.0"

This is a multi-part message in MIME format.
--------------1.9.5.msysgit.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit

---
 pcbnew/footprint_wizard_frame.cpp | 1 -
 1 file changed, 1 deletion(-)


--------------1.9.5.msysgit.0
Content-Type: text/x-patch; name="0016-Removed-wxGridCellBoolRenderer-actually-works-better.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0016-Removed-wxGridCellBoolRenderer-actually-works-better.patch"

diff --git a/pcbnew/footprint_wizard_frame.cpp b/pcbnew/footprint_wizard_frame.cpp
index 19ca019..fb99d7a 100644
--- a/pcbnew/footprint_wizard_frame.cpp
+++ b/pcbnew/footprint_wizard_frame.cpp
@@ -387,7 +387,6 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateParameterList()
         	wxGridCellBoolEditor *boolEditor = new wxGridCellBoolEditor;
         	boolEditor->UseStringValues("True","False");
         	m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, boolEditor );
-        	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

--------------1.9.5.msysgit.0--



Follow ups