← Back to team overview

kicad-developers team mailing list archive

[FEATURE] Add keepout areas in footprints

 

Attached is a patch set that allows addition of keepout zones to footprints
(modules).

When embedded inside a module, a keepout can be specified on a combination
of:

a) F.Cu
b) All internal copper layers
c) B.Cu

The patch set is working pretty well, save / load is working and you can
add / edit / delete keepout areas within the module editor.

I have also added a new layer shortcut string *.In.Cu to specify all
internal layers within a saved file.

There are a few issues that I need some help sorting out.

1. Displaying zones only on internal layers.

If you create a keepout zone but edit the layers such that is only on the
internal copper layers, then it will disappear in the module editor. This
is because only F.Cu and B.Cu layers are available in the module editor.
I'm not sure how to display the zone in this case, and allow the user to
edit it once it has disappeared.

2. Editing zone in pcbnew

Once a module is loaded into pcbnew, I want to be able to modify the zone
properties. e.g. disable for a certain internal layer, or adjust the
outline of the zone. In the same way that you can individually edit a pad
once a module is added to a PCB.

Strangely I had this working at one point but I can't work out what I
changed to break it...


As far as I can tell, the DRC and zone cutouts are working well for keepout
areas that are added to a board via a footprint.

Let me know if you see any crashes or mistakes, and if you can provide
pointers for the two outstanding items above.

Thanks,

Oliver
From 5cb4e5cda87421f4dad13e476487d45bd89d235e Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Mon, 9 Oct 2017 08:42:18 +1100
Subject: [PATCH 1/7] Add menu and toolbar icons

---
 pcbnew/menubar_modedit.cpp | 8 ++++++++
 pcbnew/pcbnew_id.h         | 1 +
 pcbnew/tool_modedit.cpp    | 6 ++++++
 3 files changed, 15 insertions(+)

diff --git a/pcbnew/menubar_modedit.cpp b/pcbnew/menubar_modedit.cpp
index 9b6be27..c48065a 100644
--- a/pcbnew/menubar_modedit.cpp
+++ b/pcbnew/menubar_modedit.cpp
@@ -311,6 +311,14 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar()
 
     placeMenu->AppendSeparator();
 
+    // Keepout zone
+    AddMenuItem( placeMenu, ID_MODEDIT_KEEPOUT_TOOL,
+                _( "&Keepout" ), _( "Add keepout zone" ),
+                KiBitmap( add_keepout_area_xpm ) );
+
+
+    placeMenu->AppendSeparator();
+
     // Anchor
     AddMenuItem( placeMenu, ID_MODEDIT_ANCHOR_TOOL,
                  _( "A&nchor" ),
diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h
index 8b8e51c..5e1d18f 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -343,6 +343,7 @@ enum pcbnew_ids
     ID_MODEDIT_CIRCLE_TOOL,
     ID_MODEDIT_ARC_TOOL,
     ID_MODEDIT_TEXT_TOOL,
+    ID_MODEDIT_KEEPOUT_TOOL,
     ID_MODEDIT_ANCHOR_TOOL,
     ID_MODEDIT_DELETE_TOOL,
     ID_MODEDIT_PLACE_GRID_COORD,
diff --git a/pcbnew/tool_modedit.cpp b/pcbnew/tool_modedit.cpp
index 97f1a93..44fb173 100644
--- a/pcbnew/tool_modedit.cpp
+++ b/pcbnew/tool_modedit.cpp
@@ -183,6 +183,12 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
                             _( "Add Text" ), wxITEM_CHECK );
 
     m_drawToolBar->AddSeparator();
+
+    m_drawToolBar->AddTool( ID_MODEDIT_KEEPOUT_TOOL, wxEmptyString, KiBitmap( add_keepout_area_xpm ),
+                            _( "Add keepout" ), wxITEM_CHECK );
+
+    m_drawToolBar->AddSeparator();
+
     m_drawToolBar->AddTool( ID_MODEDIT_ANCHOR_TOOL, wxEmptyString, KiBitmap( anchor_xpm ),
                             _( "Place footprint reference anchor" ),
                             wxITEM_CHECK );
-- 
2.7.4

From facbd0fce2396a2e42221677f762284eeb08e28e Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 10 Oct 2017 22:58:31 +1100
Subject: [PATCH 2/7] Add zones to footprint

- Can draw keepout zone on F/B/Inner
- Invalid tool msg in Legacy view
- Save / Load zones in footprint (module) files
- Can add a footprint with a zone to a board
---
 pcbnew/board_item_container.h                     |   7 ++
 pcbnew/class_board.cpp                            |   5 -
 pcbnew/class_board.h                              |   5 +-
 pcbnew/class_module.cpp                           |  97 ++++++++++++++++++-
 pcbnew/class_module.h                             |  10 +-
 pcbnew/class_zone.cpp                             |   7 +-
 pcbnew/class_zone.h                               |   4 +-
 pcbnew/class_zone_settings.cpp                    |   1 +
 pcbnew/class_zone_settings.h                      |  10 +-
 pcbnew/dialogs/dialog_keepout_area_properties.cpp | 108 ++++++++++++++++++----
 pcbnew/kicad_plugin.cpp                           |  10 ++
 pcbnew/kicad_plugin.h                             |   3 +-
 pcbnew/modedit.cpp                                |   1 +
 pcbnew/modedit_onclick.cpp                        |   1 +
 pcbnew/pcb_parser.cpp                             |  17 +++-
 pcbnew/pcb_parser.h                               |   3 +-
 pcbnew/tools/drawing_tool.cpp                     |   2 +-
 pcbnew/tools/pcb_actions.cpp                      |   1 +
 pcbnew/tools/zone_create_helper.cpp               |  17 +++-
 19 files changed, 265 insertions(+), 44 deletions(-)

diff --git a/pcbnew/board_item_container.h b/pcbnew/board_item_container.h
index 011df4b..ef933de 100644
--- a/pcbnew/board_item_container.h
+++ b/pcbnew/board_item_container.h
@@ -28,6 +28,7 @@
 #define BOARD_ITEM_CONTAINER_H
 
 #include <class_board_item.h>
+#include <class_zone_settings.h>
 
 enum ADD_MODE { ADD_INSERT, ADD_APPEND };
 
@@ -70,6 +71,12 @@ public:
         Remove( aItem );
         delete aItem;
     }
+
+    const ZONE_SETTINGS& GetZoneSettings() const            { return m_zoneSettings; }
+    void SetZoneSettings( const ZONE_SETTINGS& aSettings )  { m_zoneSettings = aSettings; }
+
+protected:
+    ZONE_SETTINGS m_zoneSettings;
 };
 
 #endif /* BOARD_ITEM_CONTAINER_H */
diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp
index 1b3d155..2c9d76d 100644
--- a/pcbnew/class_board.cpp
+++ b/pcbnew/class_board.cpp
@@ -1,8 +1,3 @@
-/**
- * @file class_board.cpp
- * @brief  BOARD class functions.
- */
-
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h
index 8160747..248f3bd 100644
--- a/pcbnew/class_board.h
+++ b/pcbnew/class_board.h
@@ -191,7 +191,7 @@ private:
     std::shared_ptr<CONNECTIVITY_DATA>      m_connectivity;
 
     BOARD_DESIGN_SETTINGS   m_designSettings;
-    ZONE_SETTINGS           m_zoneSettings;
+    //ZONE_SETTINGS           m_zoneSettings;
     COLORS_DESIGN_SETTINGS* m_colorsSettings;
     PAGE_INFO               m_paper;
     TITLE_BLOCK             m_titles;               ///< text in lower right of screen and plots
@@ -553,9 +553,6 @@ public:
     TITLE_BLOCK& GetTitleBlock()                            { return m_titles; }
     void SetTitleBlock( const TITLE_BLOCK& aTitleBlock )    { m_titles = aTitleBlock; }
 
-    const ZONE_SETTINGS& GetZoneSettings() const            { return m_zoneSettings; }
-    void SetZoneSettings( const ZONE_SETTINGS& aSettings )  { m_zoneSettings = aSettings; }
-
     /**
      * Function GetColorSettings
      * @return the current COLORS_DESIGN_SETTINGS in use
diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp
index a23ef28..cc22b2c 100644
--- a/pcbnew/class_module.cpp
+++ b/pcbnew/class_module.cpp
@@ -132,6 +132,12 @@ MODULE::MODULE( const MODULE& aModule ) :
         }
     }
 
+    // Copy auxiliary data: zones
+    for( ZONE_CONTAINER* zone = aModule.m_Zones; zone; zone = zone->Next() )
+    {
+        Add( static_cast<ZONE_CONTAINER*>( zone->Clone() ) );
+    }
+
     // Copy auxiliary data: 3D_Drawings info
     m_3D_Drawings = aModule.m_3D_Drawings;
 
@@ -274,6 +280,10 @@ void MODULE::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
             m_Pads.PushFront( static_cast<D_PAD*>( aBoardItem ) );
         break;
 
+    case PCB_ZONE_AREA_T:
+        m_Zones.PushBack( static_cast<ZONE_CONTAINER*>( aBoardItem ) );
+        break;
+
     default:
     {
         wxString msg;
@@ -302,11 +312,21 @@ void MODULE::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
         static_cast<D_PAD*>( aBoardItem )->SetLocalCoord();
         break;
 
+    case PCB_ZONE_AREA_T:
+        //TODO
+        //static_cast<ZONE_CONTAINER*>( aBoardItem )->SetLocalCoord();
+        break;
+
     default:
+    {
         // Huh? It should have been filtered out by the previous switch
-        assert(false);
+        wxString msg;
+                msg.Printf( wxT( "MODULE::Add() needs work: BOARD_ITEM type (%d) not handled" ),
+                            aBoardItem->Type() );
+        wxFAIL_MSG( msg );
         break;
     }
+    }
 }
 
 
@@ -328,6 +348,10 @@ void MODULE::Remove( BOARD_ITEM* aBoardItem )
         m_Pads.Remove( static_cast<D_PAD*>( aBoardItem ) );
         break;
 
+    case PCB_ZONE_AREA_T:
+        m_Zones.Remove( static_cast<ZONE_CONTAINER*>( aBoardItem ) );
+        break;
+
     default:
     {
         wxString msg;
@@ -435,6 +459,21 @@ void MODULE::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode,
         }
     }
 
+    for( BOARD_ITEM* item = m_Zones; item; item = item->Next() )
+    {
+        if( item->IsMoving() )
+            continue;
+
+        switch (item->Type() )
+        {
+        case PCB_ZONE_AREA_T:
+            item->Draw( aPanel, aDC, aDrawMode, aOffset );
+            break;
+        default:
+            break;
+        }
+    }
+
     // Enable these line to draw m_BoundaryBox (debug tests purposes only)
 #if 0
     GRRect( aPanel->GetClipBox(), aDC, m_BoundaryBox, 0, BROWN );
@@ -485,7 +524,14 @@ EDA_RECT MODULE::GetFootprintRect() const
     }
 
     for( D_PAD* pad = m_Pads;  pad;  pad = pad->Next() )
+    {
         area.Merge( pad->GetBoundingBox() );
+    }
+
+    for( ZONE_CONTAINER* zone = m_Zones; zone; zone=zone->Next() )
+    {
+        area.Merge( zone->GetBoundingBox() );
+    }
 
     return area;
 }
@@ -634,6 +680,12 @@ bool MODULE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) co
                 return true;
         }
 
+        for( ZONE_CONTAINER* zone = m_Zones; zone; zone = zone->Next() )
+        {
+            if( zone->HitTest( arect, false, 0 ) )
+                return true;
+        }
+
         // No items were hit
         return false;
     }
@@ -780,6 +832,11 @@ SEARCH_RESULT MODULE::Visit( INSPECTOR inspector, void* testData, const KICAD_T
             ++p;
             break;
 
+        case PCB_ZONE_T:
+            result = IterateForward( m_Zones, inspector, testData, p );
+            ++p;
+            break;
+
         case PCB_MODULE_TEXT_T:
             result = inspector( m_Reference, testData );
 
@@ -860,6 +917,9 @@ void MODULE::RunOnChildren( std::function<void (BOARD_ITEM*)> aFunction )
         for( BOARD_ITEM* drawing = m_Drawings; drawing; drawing = drawing->Next() )
             aFunction( drawing );
 
+        for( ZONE_CONTAINER* zone = m_Zones; zone; zone = zone->Next() )
+            aFunction( zone );
+
         aFunction( static_cast<BOARD_ITEM*>( m_Reference ) );
         aFunction( static_cast<BOARD_ITEM*>( m_Value ) );
     }
@@ -992,6 +1052,11 @@ void MODULE::Flip( const wxPoint& aCentre )
         }
     }
 
+    for( ZONE_CONTAINER* zone = m_Zones; zone; zone = zone->Next() )
+    {
+        zone->Flip( m_Pos );
+    }
+
     CalculateBoundingBox();
 }
 
@@ -1034,6 +1099,11 @@ void MODULE::SetPosition( const wxPoint& newpos )
         }
     }
 
+    for( ZONE_CONTAINER* zone = m_Zones; zone; zone = zone->Next() )
+    {
+        zone->Move( delta );
+    }
+
     CalculateBoundingBox();
 }
 
@@ -1093,6 +1163,11 @@ void MODULE::MoveAnchorPosition( const wxPoint& aMoveVector )
         }
     }
 
+    for( ZONE_CONTAINER* zone = m_Zones; zone; zone = zone->Next() )
+    {
+        zone->Move( moveVector );
+    }
+
     CalculateBoundingBox();
 }
 
@@ -1128,6 +1203,11 @@ void MODULE::SetOrientation( double newangle )
         }
     }
 
+    for( ZONE_CONTAINER* zone = m_Zones; zone; zone = zone->Next() )
+    {
+        zone->Rotate( zone->GetCenter(), angleChange );
+    }
+
     CalculateBoundingBox();
 }
 
@@ -1172,7 +1252,7 @@ BOARD_ITEM* MODULE::Duplicate( const BOARD_ITEM* aItem,
     case PCB_MODULE_EDGE_T:
     {
         EDGE_MODULE* new_edge = new EDGE_MODULE(
-                *static_cast<const EDGE_MODULE*>(aItem) );
+                *static_cast<const EDGE_MODULE*>( aItem ) );
 
         if( aAddToModule )
             GraphicalItemsList().PushBack( new_edge );
@@ -1181,6 +1261,19 @@ BOARD_ITEM* MODULE::Duplicate( const BOARD_ITEM* aItem,
         break;
     }
 
+    case PCB_ZONE_AREA_T:
+    {
+        ZONE_CONTAINER* new_zone = new ZONE_CONTAINER(
+                *static_cast<const ZONE_CONTAINER*>( aItem ) );
+
+        if( aAddToModule )
+        {
+            m_Zones.PushBack( new_zone );
+        }
+
+        new_item = new_zone;
+        break;
+    }
     case PCB_MODULE_T:
         // Ignore the module itself
         break;
diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h
index 79431d4..286e953 100644
--- a/pcbnew/class_module.h
+++ b/pcbnew/class_module.h
@@ -36,6 +36,7 @@
 #include <dlist.h>
 #include <layers_id_colors_and_visibility.h>       // ALL_LAYERS definition.
 #include <class_board_item.h>
+#include <class_zone.h>
 #include <board_item_container.h>
 #include <lib_id.h>
 
@@ -131,12 +132,15 @@ public:
     // Virtual function
     const EDA_RECT GetBoundingBox() const override;
 
-    DLIST<D_PAD>& PadsList()                        { return m_Pads; }
+    DLIST<D_PAD>& PadsList() { return m_Pads; }
     const DLIST<D_PAD>& PadsList() const { return m_Pads; }
 
-    DLIST<BOARD_ITEM>& GraphicalItemsList()         { return m_Drawings; }
+    DLIST<BOARD_ITEM>& GraphicalItemsList() { return m_Drawings; }
     const DLIST<BOARD_ITEM>& GraphicalItemsList() const { return m_Drawings; }
 
+    DLIST<ZONE_CONTAINER>& ZonesList() { return m_Zones; }
+    const DLIST<ZONE_CONTAINER>& ZonesList() const { return m_Zones; }
+
     DLIST_ITERATOR_WRAPPER<D_PAD> Pads()
     {
          return DLIST_ITERATOR_WRAPPER<D_PAD>( m_Pads );
@@ -679,6 +683,8 @@ private:
     int m_ModuleStatus;                 ///< For autoplace: flags (LOCKED, AUTOPLACED)
     EDA_RECT m_BoundaryBox;             ///< Bounding box : coordinates on board, real orientation.
 
+    DLIST<ZONE_CONTAINER> m_Zones;      ///< Linked list of zones
+
     // The final margin is the sum of these 2 values
     int m_ThermalWidth;
     int m_ThermalGap;
diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp
index 6029d93..644fdc8 100644
--- a/pcbnew/class_zone.cpp
+++ b/pcbnew/class_zone.cpp
@@ -50,8 +50,8 @@
 #include <polygon_test_point_inside.h>
 
 
-ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) :
-    BOARD_CONNECTED_ITEM( aBoard, PCB_ZONE_AREA_T )
+ZONE_CONTAINER::ZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent ) :
+    BOARD_CONNECTED_ITEM( aParent, PCB_ZONE_AREA_T )
 {
     m_CornerSelection = nullptr;                // no corner is selected
     m_IsFilled = false;                         // fill status : true when the zone is filled
@@ -66,7 +66,8 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) :
     m_cornerRadius = 0;
     SetLocalFlags( 0 );                         // flags tempoarry used in zone calculations
     m_Poly = new SHAPE_POLY_SET();              // Outlines
-    aBoard->GetZoneSettings().ExportSetting( *this );
+
+    aParent->GetZoneSettings().ExportSetting( *this );
 }
 
 
diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h
index a99359c..7c7fb82 100644
--- a/pcbnew/class_zone.h
+++ b/pcbnew/class_zone.h
@@ -84,13 +84,15 @@ public:
      */
     typedef enum HATCH_STYLE { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE } HATCH_STYLE;
 
-    ZONE_CONTAINER( BOARD* parent );
+    ZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent );
 
     ZONE_CONTAINER( const ZONE_CONTAINER& aZone );
     ZONE_CONTAINER& operator=( const ZONE_CONTAINER &aOther );
 
     ~ZONE_CONTAINER();
 
+    ZONE_CONTAINER* Next() const { return static_cast<ZONE_CONTAINER*>( Pnext ); }
+
     /**
      * Function GetPosition
      *
diff --git a/pcbnew/class_zone_settings.cpp b/pcbnew/class_zone_settings.cpp
index d9a3f6b..4b00bf7 100644
--- a/pcbnew/class_zone_settings.cpp
+++ b/pcbnew/class_zone_settings.cpp
@@ -64,6 +64,7 @@ ZONE_SETTINGS::ZONE_SETTINGS()
     m_cornerRadius = 0;
 
     SetIsKeepout( false );
+    SetIsInFootprint( false );
     SetDoNotAllowCopperPour( false );
     SetDoNotAllowVias( true );
     SetDoNotAllowTracks( true );
diff --git a/pcbnew/class_zone_settings.h b/pcbnew/class_zone_settings.h
index 73d6452..821f127 100644
--- a/pcbnew/class_zone_settings.h
+++ b/pcbnew/class_zone_settings.h
@@ -64,7 +64,7 @@ public:
     int  m_ZoneMinThickness;            ///< Min thickness value in filled areas
     int  m_NetcodeSelection;            ///< Net code selection for the current zone
 
-    LSET m_Layers;
+    LSET m_Layers;                      ///< Set of layers on which this zone exists
 
     PCB_LAYER_ID    m_CurrentZone_Layer;    ///< Layer used to create the current zone
 
@@ -87,7 +87,11 @@ private:
     /* A zone outline can be a keepout zone.
      * It will be never filled, and DRC should test for pads, tracks and vias
      */
-    bool                  m_isKeepout;
+    bool    m_isKeepout;
+
+    /* A zone outline can be drawn inside a footprint
+     */
+    bool    m_isInFootprint;
 
     /* For keepout zones only:
      * what is not allowed inside the keepout ( pads, tracks and vias )
@@ -134,11 +138,13 @@ public:
      * Accessors to parameters used in Keepout zones:
      */
     const bool GetIsKeepout() const { return m_isKeepout; }
+    const bool GetIsInFootprint() const { return m_isInFootprint; }
     const bool GetDoNotAllowCopperPour() const { return m_keepoutDoNotAllowCopperPour; }
     const bool GetDoNotAllowVias() const { return m_keepoutDoNotAllowVias; }
     const bool GetDoNotAllowTracks() const { return m_keepoutDoNotAllowTracks; }
 
     void SetIsKeepout( bool aEnable ) { m_isKeepout = aEnable; }
+    void SetIsInFootprint( bool aIsInFootprint ) { m_isInFootprint = aIsInFootprint; }
     void SetDoNotAllowCopperPour( bool aEnable ) { m_keepoutDoNotAllowCopperPour = aEnable; }
     void SetDoNotAllowVias( bool aEnable ) { m_keepoutDoNotAllowVias = aEnable; }
     void SetDoNotAllowTracks( bool aEnable ) { m_keepoutDoNotAllowTracks = aEnable; }
diff --git a/pcbnew/dialogs/dialog_keepout_area_properties.cpp b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
index 4b3e163..5c393dc 100644
--- a/pcbnew/dialogs/dialog_keepout_area_properties.cpp
+++ b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
@@ -87,7 +87,7 @@ private:
      * creates the colored rectangle icons used in the layer selection widget.
      * @param aColor is the color to fill the rectangle with.
      */
-    wxIcon makeLayerIcon( COLOR4D aColor );
+    wxDataViewIconText makeLayerIcon( const wxString aTitle, COLOR4D aColor );
 };
 
 
@@ -154,26 +154,70 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::initDialog()
     auto* checkColumn = m_layers->AppendToggleColumn( wxEmptyString );
     auto* layerColumn = m_layers->AppendIconTextColumn( wxEmptyString );
 
+    COLOR4D layerColor;
     wxVector<wxVariant> row;
 
     int imgIdx = 0;
 
-    for( LSEQ cu_stack = show.UIOrder();  cu_stack;  ++cu_stack, imgIdx++ )
-    {
-        PCB_LAYER_ID layer = *cu_stack;
+    /*
+     * Layer considerations for zones drawn in footprint or in PCB
+     *
+     * Zones drawn in footprint cannot select individual internal layers.
+     * Layer selection is limited to:
+     * - F.Cu
+     * - B.Cu
+     * - Inner.Cu
+     *
+     * If Inner.Cu is selected, then the zone is on ALL internal layers when
+     * the footprint is placed on the PCB
+     */
 
-        msg = board->GetLayerName( layer );
+    if( m_zonesettings.GetIsInFootprint() )
+    {
+        // Add F.Cu layer entry
+        layerColor = m_parent->Settings().Colors().GetLayerColor( F_Cu );
 
-        COLOR4D layerColor = m_parent->Settings().Colors().GetLayerColor( layer );
+        row.clear();
+        row.push_back( m_zonesettings.m_Layers.test( F_Cu ) );
+        row.push_back( wxVariant( makeLayerIcon( board->GetLayerName( F_Cu ), layerColor ) ) );
+        m_layers->AppendItem( row );
 
+        // Add Inner.Cu layer entry
         row.clear();
-        row.push_back( m_zonesettings.m_Layers.test( layer ) );
-        auto iconItem = wxDataViewIconText( msg, makeLayerIcon( layerColor ) );
-        row.push_back( wxVariant( iconItem ) );
 
+        // Test if any layers are set
+        LSET internal = m_zonesettings.m_Layers & LSET::InternalCuMask();
+        row.push_back( m_zonesettings.m_Layers.test( internal.count() > 0 ) );
+        layerColor = m_parent->Settings().Colors().GetLayerColor( In1_Cu );
+        row.push_back( wxVariant( makeLayerIcon( _( "Internal layers" ), layerColor ) ) );
+        m_layers->AppendItem( row );
+
+        // Add B.Cu layer entry
+        row.clear();
+        layerColor = m_parent->Settings().Colors().GetLayerColor( B_Cu );
+        row.push_back( m_zonesettings.m_Layers.test( B_Cu ) );
+        layerColor = m_parent->Settings().Colors().GetLayerColor( B_Cu );
+        row.push_back( wxVariant( makeLayerIcon( board->GetLayerName( B_Cu ), layerColor ) ) );
         m_layers->AppendItem( row );
 
     }
+    else
+    {
+        for( LSEQ cu_stack = show.UIOrder();  cu_stack;  ++cu_stack, imgIdx++ )
+        {
+            PCB_LAYER_ID layer = *cu_stack;
+
+            msg = board->GetLayerName( layer );
+
+            layerColor = m_parent->Settings().Colors().GetLayerColor( layer );
+
+            row.clear();
+            row.push_back( m_zonesettings.m_Layers.test( layer ) );
+            row.push_back( wxVariant( makeLayerIcon( msg, layerColor ) ) );
+
+            m_layers->AppendItem( row );
+        }
+    }
 
     // Init keepout parameters:
     m_cbTracksCtrl->SetValue( m_zonesettings.GetDoNotAllowTracks() );
@@ -218,14 +262,43 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::OnLayerSelection( wxDataViewEvent& event )
     int row = m_layers->ItemToRow( item );
     bool selected = m_layers->GetToggleValue( row, 0 );
 
-    BOARD* board = m_parent->GetBoard();
-    LSEQ cu_stack = LSET::AllCuMask( board->GetCopperLayerCount() ).UIOrder();
-
-    if( row < (int)cu_stack.size() )
+    // If the zone is defined in a footprint
+    if( m_zonesettings.GetIsInFootprint() )
     {
-        m_zonesettings.m_Layers.set( cu_stack[ row ], selected );
+        switch( row )
+        {
+        case 0: // F.Cu
+            m_zonesettings.m_Layers.set( F_Cu, selected );
+            break;
+        case 1: // Inner layers
+            if( selected )
+            {
+                m_zonesettings.m_Layers |= LSET::InternalCuMask();
+            }
+            else
+            {
+                m_zonesettings.m_Layers &= ~( LSET::InternalCuMask() );
+            }
+            break;
+        case 2: // B.Cu
+            m_zonesettings.m_Layers.set( B_Cu, selected );
+            break;
+        default:
+            break;
+        }
+    }
+    else
+    {
+        BOARD* board = m_parent->GetBoard();
+        LSEQ cu_stack = LSET::AllCuMask( board->GetCopperLayerCount() ).UIOrder();
+
+        if( row < (int)cu_stack.size() )
+        {
+            m_zonesettings.m_Layers.set( cu_stack[ row ], selected );
+        }
     }
 
+    // Disable 'OK' button if there are no copper layers selected
     m_sdbSizerButtonsOK->Enable( m_zonesettings.m_Layers.count() > 0 );
 }
 
@@ -286,7 +359,7 @@ bool DIALOG_KEEPOUT_AREA_PROPERTIES::AcceptOptionsForKeepOut()
 }
 
 
-wxIcon DIALOG_KEEPOUT_AREA_PROPERTIES::makeLayerIcon( COLOR4D aColor )
+wxDataViewIconText DIALOG_KEEPOUT_AREA_PROPERTIES::makeLayerIcon( const wxString aTitle, COLOR4D aColor )
 {
     wxBitmap    bitmap( LAYER_BITMAP_SIZE_X, LAYER_BITMAP_SIZE_Y );
     wxBrush     brush;
@@ -301,5 +374,8 @@ wxIcon DIALOG_KEEPOUT_AREA_PROPERTIES::makeLayerIcon( COLOR4D aColor )
 
     wxIcon icon;
     icon.CopyFromBitmap( bitmap );
-    return icon;
+
+    wxDataViewIconText iconText( aTitle, icon );
+
+    return iconText;
 }
diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp
index a38082e..a23cb5c 100644
--- a/pcbnew/kicad_plugin.cpp
+++ b/pcbnew/kicad_plugin.cpp
@@ -1200,11 +1200,21 @@ void PCB_IO::format( MODULE* aModule, int aNestLevel ) const
 
     // Save drawing elements.
     for( BOARD_ITEM* gr = aModule->GraphicalItemsList();  gr;  gr = gr->Next() )
+    {
         Format( gr, aNestLevel+1 );
+    }
 
     // Save pads.
     for( D_PAD* pad = aModule->PadsList();  pad;  pad = pad->Next() )
+    {
         format( pad, aNestLevel+1 );
+    }
+
+    // Save zones
+    for( ZONE_CONTAINER* zone = aModule->ZonesList(); zone; zone = zone->Next() )
+    {
+        format( zone, aNestLevel + 1 );
+    }
 
     // Save 3D info.
     std::list<S3D_INFO>::const_iterator bs3D = aModule->Models().begin();
diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h
index eff7590..fc48ae3 100644
--- a/pcbnew/kicad_plugin.h
+++ b/pcbnew/kicad_plugin.h
@@ -44,7 +44,8 @@ class NETINFO_MAPPING;
 //#define SEXPR_BOARD_FILE_VERSION    20160815  // differential pair settings per net class
 //#define SEXPR_BOARD_FILE_VERSION    20170123  // EDA_TEXT refactor, moved 'hide'
 //#define SEXPR_BOARD_FILE_VERSION    20170920  // long pad names and custom pad shape
-#define SEXPR_BOARD_FILE_VERSION      20170922  // Keepout zones can exist on multiple layers
+//#define SEXPR_BOARD_FILE_VERSION    20170922  // Keepout zones can exist on multiple layers
+#define SEXPR_BOARD_FILE_VERSION      20171010  // Keepzones can be defined inside footprints
 
 #define CTL_STD_LAYER_NAMES         (1 << 0)    ///< Use English Standard layer names
 #define CTL_OMIT_NETS               (1 << 1)    ///< Omit pads net names (useless in library)
diff --git a/pcbnew/modedit.cpp b/pcbnew/modedit.cpp
index 207d303..54d9292 100644
--- a/pcbnew/modedit.cpp
+++ b/pcbnew/modedit.cpp
@@ -977,6 +977,7 @@ void FOOTPRINT_EDIT_FRAME::OnVerticalToolbar( wxCommandEvent& aEvent )
         SetToolID( id, wxCURSOR_BULLSEYE, _( "Delete item" ) );
         break;
 
+    case ID_MODEDIT_KEEPOUT_TOOL:
     case ID_MODEDIT_MEASUREMENT_TOOL:
         DisplayError( this, wxT( "Unsupported tool in legacy canvas" ) );
         SetNoToolSelected();
diff --git a/pcbnew/modedit_onclick.cpp b/pcbnew/modedit_onclick.cpp
index 5a98642..d5bf357 100644
--- a/pcbnew/modedit_onclick.cpp
+++ b/pcbnew/modedit_onclick.cpp
@@ -200,6 +200,7 @@ void FOOTPRINT_EDIT_FRAME::OnLeftClick( wxDC* DC, const wxPoint& MousePos )
 
         break;
 
+    case ID_MODEDIT_KEEPOUT_TOOL:
     case ID_MODEDIT_MEASUREMENT_TOOL:
         DisplayError( this, wxT( "Unsupported tool in legacy canvas" ) );
         SetNoToolSelected();
diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp
index dfcc790..170c150 100644
--- a/pcbnew/pcb_parser.cpp
+++ b/pcbnew/pcb_parser.cpp
@@ -71,7 +71,9 @@ void PCB_PARSER::init()
         m_layerMasks[ untranslated ]   = LSET( PCB_LAYER_ID( layer ) );
     }
 
+    // Shortcuts for layer combinations
     m_layerMasks[ "*.Cu" ]      = LSET::AllCuMask();
+    m_layerMasks[ "*.In.Cu" ]   = LSET::InternalCuMask();
     m_layerMasks[ "F&B.Cu" ]    = LSET( 2, F_Cu, B_Cu );
     m_layerMasks[ "*.Adhes" ]   = LSET( 2, B_Adhes, F_Adhes );
     m_layerMasks[ "*.Paste" ]   = LSET( 2, B_Paste, F_Paste );
@@ -529,7 +531,7 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
             break;
 
         case T_zone:
-            m_board->Add( parseZONE_CONTAINER(), ADD_APPEND );
+            m_board->Add( parseZONE_CONTAINER( m_board ), ADD_APPEND );
             break;
 
         case T_target:
@@ -1963,6 +1965,13 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
             }
             break;
 
+        case T_zone:
+            {
+                ZONE_CONTAINER* zone = parseZONE_CONTAINER( module.get() );
+                module->Add( zone, ADD_APPEND );
+            }
+            break;
+
         case T_model:
             module->Add3DModel( parse3DModel() );
             break;
@@ -1972,7 +1981,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
                        "autoplace_cost90, autoplace_cost180, solder_mask_margin, "
                        "solder_paste_margin, solder_paste_ratio, clearance, "
                        "zone_connect, thermal_width, thermal_gap, attr, fp_text, "
-                       "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, pad, or model" );
+                       "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, pad, zone, or model" );
         }
     }
 
@@ -2768,7 +2777,7 @@ VIA* PCB_PARSER::parseVIA()
 }
 
 
-ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER()
+ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent )
 {
     wxCHECK_MSG( CurTok() == T_zone, NULL,
                  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
@@ -2785,7 +2794,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER()
     // bigger scope since each filled_polygon is concatenated in here
     SHAPE_POLY_SET pts;
 
-    std::unique_ptr< ZONE_CONTAINER > zone( new ZONE_CONTAINER( m_board ) );
+    std::unique_ptr< ZONE_CONTAINER > zone( new ZONE_CONTAINER( aParent ) );
 
     zone->SetPriority( 0 );
 
diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h
index a92f2ab..3f14721 100644
--- a/pcbnew/pcb_parser.h
+++ b/pcbnew/pcb_parser.h
@@ -43,6 +43,7 @@
 
 class BOARD;
 class BOARD_ITEM;
+class BOARD_ITEM_CONTAINER;
 class D_PAD;
 class DIMENSION;
 class DRAWSEGMENT;
@@ -130,7 +131,7 @@ class PCB_PARSER : public PCB_LEXER
     bool            parseD_PAD_option( D_PAD* aPad );
     TRACK*          parseTRACK();
     VIA*            parseVIA();
-    ZONE_CONTAINER* parseZONE_CONTAINER();
+    ZONE_CONTAINER* parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent );
     PCB_TARGET*     parsePCB_TARGET();
     BOARD*          parseBOARD();
 
diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp
index abdcd46..6b3fd8a 100644
--- a/pcbnew/tools/drawing_tool.cpp
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -653,7 +653,7 @@ int DRAWING_TOOL::DrawZoneKeepout( const TOOL_EVENT& aEvent )
 {
     SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::KEEPOUT );
 
-    m_frame->SetToolID( ID_PCB_KEEPOUT_AREA_BUTT, wxCURSOR_PENCIL, _( "Add keepout" ) );
+    m_frame->SetToolID( m_editModules ? ID_MODEDIT_KEEPOUT_TOOL : ID_PCB_KEEPOUT_AREA_BUTT, wxCURSOR_PENCIL, _( "Add keepout" ) );
 
     return drawZone( true, ZONE_MODE::ADD );
 }
diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp
index 8142884..419b024 100644
--- a/pcbnew/tools/pcb_actions.cpp
+++ b/pcbnew/tools/pcb_actions.cpp
@@ -62,6 +62,7 @@ boost::optional<TOOL_EVENT> PCB_ACTIONS::TranslateLegacyId( int aId )
         return PCB_ACTIONS::drawVia.MakeEvent();
 
     case ID_PCB_KEEPOUT_AREA_BUTT:
+    case ID_MODEDIT_KEEPOUT_TOOL:
         return PCB_ACTIONS::drawZoneKeepout.MakeEvent();
 
     case ID_PCB_ADD_LINE_BUTT:
diff --git a/pcbnew/tools/zone_create_helper.cpp b/pcbnew/tools/zone_create_helper.cpp
index 1b297bd..27fc4b1 100644
--- a/pcbnew/tools/zone_create_helper.cpp
+++ b/pcbnew/tools/zone_create_helper.cpp
@@ -27,6 +27,7 @@
 #include <class_zone.h>
 #include <board_commit.h>
 #include <pcb_painter.h>
+#include <pcbnew_id.h>
 
 #include <tools/pcb_actions.h>
 #include <tools/selection_tool.h>
@@ -53,25 +54,36 @@ ZONE_CREATE_HELPER::~ZONE_CREATE_HELPER()
 std::unique_ptr<ZONE_CONTAINER> ZONE_CREATE_HELPER::createNewZone( bool aKeepout )
 {
     auto& frame = *m_tool.getEditFrame<PCB_BASE_EDIT_FRAME>();
+
     auto& board = *m_tool.getModel<BOARD>();
 
+    BOARD_ITEM_CONTAINER* parent = frame.GetModel();
+
     // Get the current default settings for zones
     ZONE_SETTINGS zoneInfo = frame.GetZoneSettings();
     zoneInfo.m_CurrentZone_Layer = frame.GetScreen()->m_Active_Layer;
     zoneInfo.m_NetcodeSelection = board.GetHighLightNetCode();
     zoneInfo.SetIsKeepout( m_params.m_keepout );
 
+    zoneInfo.SetIsInFootprint( aKeepout && ( frame.GetToolId() == ID_MODEDIT_KEEPOUT_TOOL ) );
+
     // Show options dialog
     ZONE_EDIT_T dialogResult;
 
     if( m_params.m_keepout )
+    {
         dialogResult = InvokeKeepoutAreaEditor( &frame, &zoneInfo );
+    }
     else
     {
         if( IsCopperLayer( zoneInfo.m_CurrentZone_Layer ) )
+        {
             dialogResult = InvokeCopperZonesEditor( &frame, &zoneInfo );
+        }
         else
+        {
             dialogResult = InvokeNonCopperZonesEditor( &frame, nullptr, &zoneInfo );
+        }
     }
 
     if( dialogResult == ZONE_ABORT )
@@ -79,7 +91,8 @@ std::unique_ptr<ZONE_CONTAINER> ZONE_CREATE_HELPER::createNewZone( bool aKeepout
         return nullptr;
     }
 
-    auto newZone = std::make_unique<ZONE_CONTAINER>( &board );
+    auto newZone = std::make_unique<ZONE_CONTAINER>( parent );
+
 
     // Apply the selected settings
     zoneInfo.ExportSetting( *newZone );
@@ -173,7 +186,7 @@ bool ZONE_CREATE_HELPER::OnFirstPoint()
 
         if( m_zone )
         {
-            // set up poperties from zone
+            // Set up properties from zone
             const auto& settings = *m_parentView.GetPainter()->GetSettings();
             COLOR4D color = settings.GetColor( nullptr, m_zone->GetLayer() );
 
-- 
2.7.4

From edd2da58c125583b25ed65af57746a415df2906f Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Wed, 11 Oct 2017 08:53:56 +1100
Subject: [PATCH 3/7] Allow editing of keepout zone in modedit

- Also checks for DRC in keepout zones defined in a footprint
---
 pcbnew/board_commit.cpp                           |  5 ++-
 pcbnew/class_module.cpp                           |  9 +++++-
 pcbnew/class_zone.cpp                             |  2 +-
 pcbnew/collectors.cpp                             |  2 ++
 pcbnew/dialogs/dialog_keepout_area_properties.cpp |  7 ++++-
 pcbnew/drc.cpp                                    | 29 +++++++++++++++--
 pcbnew/hotkeys_module_editor.cpp                  |  6 ++++
 pcbnew/modedit_onclick.cpp                        | 38 +++++++++++++++++++++++
 8 files changed, 92 insertions(+), 6 deletions(-)

diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp
index 2ef7e32..85033ca 100644
--- a/pcbnew/board_commit.cpp
+++ b/pcbnew/board_commit.cpp
@@ -82,6 +82,9 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry )
             if( ent.m_item->Type() != PCB_MODULE_T )
                 ent.m_item = ent.m_item->GetParent();
 
+            //if( ent.m_copy->Type() != PCB_MODULE_T )
+            //    ent.m_copy = ent.m_copy->GetParent();
+
             // We have not saved the module yet, so let's create an entry
             if( savedModules.count( ent.m_item ) == 0 )
             {
@@ -92,7 +95,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry )
                 }
 
                 assert( ent.m_item->Type() == PCB_MODULE_T );
-                assert( ent.m_copy->Type() == PCB_MODULE_T );
+                //assert( ent.m_copy->Type() == PCB_MODULE_T );
 
                 if( aCreateUndoEntry )
                 {
diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp
index cc22b2c..684cc51 100644
--- a/pcbnew/class_module.cpp
+++ b/pcbnew/class_module.cpp
@@ -218,6 +218,11 @@ MODULE& MODULE::operator=( const MODULE& aOther )
         }
     }
 
+    for( ZONE_CONTAINER* zone = aOther.m_Zones; zone; zone = zone->Next() )
+    {
+        Add( new ZONE_CONTAINER( *zone ) );
+    }
+
     // Copy auxiliary data: 3D_Drawings info
     m_3D_Drawings.clear();
     m_3D_Drawings = aOther.m_3D_Drawings;
@@ -236,7 +241,9 @@ void MODULE::ClearAllNets()
     // Force the ORPHANED dummy net info for all pads.
     // ORPHANED dummy net does not depend on a board
     for( D_PAD* pad = PadsList(); pad; pad = pad->Next() )
+    {
         pad->SetNetCode( NETINFO_LIST::ORPHANED );
+    }
 }
 
 
@@ -832,7 +839,7 @@ SEARCH_RESULT MODULE::Visit( INSPECTOR inspector, void* testData, const KICAD_T
             ++p;
             break;
 
-        case PCB_ZONE_T:
+        case PCB_ZONE_AREA_T:
             result = IterateForward( m_Zones, inspector, testData, p );
             ++p;
             break;
diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp
index 644fdc8..f184826 100644
--- a/pcbnew/class_zone.cpp
+++ b/pcbnew/class_zone.cpp
@@ -72,7 +72,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent ) :
 
 
 ZONE_CONTAINER::ZONE_CONTAINER( const ZONE_CONTAINER& aZone ) :
-    BOARD_CONNECTED_ITEM( aZone )
+    BOARD_CONNECTED_ITEM( aZone.GetParent(), PCB_ZONE_AREA_T )
 {
     m_smoothedPoly = NULL;
 
diff --git a/pcbnew/collectors.cpp b/pcbnew/collectors.cpp
index e3dcb54..176d2ab 100644
--- a/pcbnew/collectors.cpp
+++ b/pcbnew/collectors.cpp
@@ -115,6 +115,7 @@ const KICAD_T GENERAL_COLLECTOR::ModulesAndTheirItems[] = {
     PCB_MODULE_EDGE_T,
     PCB_PAD_T,
     PCB_MODULE_T,
+    PCB_ZONE_AREA_T,
     EOT
 };
 
@@ -123,6 +124,7 @@ const KICAD_T GENERAL_COLLECTOR::ModuleItems[] = {
     PCB_MODULE_TEXT_T,
     PCB_MODULE_EDGE_T,
     PCB_PAD_T,
+    PCB_ZONE_AREA_T,
     EOT
 };
 
diff --git a/pcbnew/dialogs/dialog_keepout_area_properties.cpp b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
index 5c393dc..4391f8d 100644
--- a/pcbnew/dialogs/dialog_keepout_area_properties.cpp
+++ b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
@@ -182,6 +182,7 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::initDialog()
         row.push_back( wxVariant( makeLayerIcon( board->GetLayerName( F_Cu ), layerColor ) ) );
         m_layers->AppendItem( row );
 
+        /*
         // Add Inner.Cu layer entry
         row.clear();
 
@@ -192,6 +193,8 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::initDialog()
         row.push_back( wxVariant( makeLayerIcon( _( "Internal layers" ), layerColor ) ) );
         m_layers->AppendItem( row );
 
+        */
+
         // Add B.Cu layer entry
         row.clear();
         layerColor = m_parent->Settings().Colors().GetLayerColor( B_Cu );
@@ -270,6 +273,7 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::OnLayerSelection( wxDataViewEvent& event )
         case 0: // F.Cu
             m_zonesettings.m_Layers.set( F_Cu, selected );
             break;
+        /*
         case 1: // Inner layers
             if( selected )
             {
@@ -280,7 +284,8 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::OnLayerSelection( wxDataViewEvent& event )
                 m_zonesettings.m_Layers &= ~( LSET::InternalCuMask() );
             }
             break;
-        case 2: // B.Cu
+        */
+        case 1: // B.Cu
             m_zonesettings.m_Layers.set( B_Cu, selected );
             break;
         default:
diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp
index 6abcbd0..e34b5a1 100644
--- a/pcbnew/drc.cpp
+++ b/pcbnew/drc.cpp
@@ -615,12 +615,36 @@ void DRC::testZones()
 
 void DRC::testKeepoutAreas()
 {
-    // Test keepout areas for vias, tracks and pads inside keepout areas
+    // Construct a list of areas to inspect
+    std::list<ZONE_CONTAINER*> areasToTest;
+
+    // Add board zones
     for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
     {
         ZONE_CONTAINER* area = m_pcb->GetArea( ii );
 
-        if( !area->GetIsKeepout() )
+        if( area && area->GetIsKeepout() )
+        {
+            areasToTest.push_back( area );
+        }
+    }
+
+    // Add zones in footprints
+    for( auto mod : m_pcb->Modules() )
+    {
+        for( ZONE_CONTAINER* zone = mod->ZonesList(); zone; zone = zone->Next() )
+        {
+            if( zone->GetIsKeepout() )
+            {
+                areasToTest.push_back( zone );
+            }
+        }
+    }
+
+
+    for( auto* area : areasToTest )
+    {
+        if( !area || !area->GetIsKeepout() )
         {
             continue;
         }
@@ -662,6 +686,7 @@ void DRC::testKeepoutAreas()
                 }
             }
         }
+
         // Test pads: TODO
     }
 }
diff --git a/pcbnew/hotkeys_module_editor.cpp b/pcbnew/hotkeys_module_editor.cpp
index a7a7305..9e42bcd 100644
--- a/pcbnew/hotkeys_module_editor.cpp
+++ b/pcbnew/hotkeys_module_editor.cpp
@@ -261,6 +261,12 @@ bool FOOTPRINT_EDIT_FRAME::OnHotkeyEditItem( int aIdCommand )
 
         break;
 
+    case PCB_ZONE_AREA_T:
+        if( aIdCommand == HK_EDIT_ITEM )
+            evt_type = ID_POPUP_PCB_EDIT_ZONE_PARAMS;
+
+        break;
+
     default:
         break;
     }
diff --git a/pcbnew/modedit_onclick.cpp b/pcbnew/modedit_onclick.cpp
index d5bf357..b3f9a4f 100644
--- a/pcbnew/modedit_onclick.cpp
+++ b/pcbnew/modedit_onclick.cpp
@@ -550,6 +550,44 @@ void FOOTPRINT_EDIT_FRAME::OnEditItemRequest( wxDC* aDC, BOARD_ITEM* aItem )
         m_canvas->Refresh();
         break;
 
+    case PCB_ZONE_AREA_T:
+    {
+        ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( aItem );
+
+        ZONE_SETTINGS zoneSettings;
+
+        zoneSettings << *zone;
+
+        bool success = false;
+
+        if( zone )
+        {
+            if( zone->GetIsKeepout() )
+            {
+                success = InvokeKeepoutAreaEditor( this, &zoneSettings );
+            }
+            else if( zone->IsOnCopperLayer() )
+            {
+                success = InvokeCopperZonesEditor( this, &zoneSettings );
+            }
+            else
+            {
+                success = InvokeNonCopperZonesEditor( this, zone, &zoneSettings );
+            }
+
+            if( success )
+            {
+                //BOARD_COMMIT commit( this );
+                zoneSettings.ExportSetting( *zone );
+
+                //commit.Add( zone.release() );
+                //commit.Push( _( "Edit zone" ) );
+            }
+        }
+
+        break;
+    }
+
     default:
         break;
     }
-- 
2.7.4

From 7d797cfed18047366be30d2938c2a5e4e47d2d8b Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Wed, 11 Oct 2017 19:55:08 +1100
Subject: [PATCH 4/7] Zone cutouts now work for keepouts defined in footprints

- Refactored code to extract all zones
---
 pcbnew/class_board.cpp                             | 24 +++++++++++++
 pcbnew/class_board.h                               | 11 +++++-
 pcbnew/drc.cpp                                     | 39 ++++------------------
 ...es_convert_brd_items_to_polygons_with_Boost.cpp |  9 +++--
 4 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp
index 2c9d76d..a3b928b 100644
--- a/pcbnew/class_board.cpp
+++ b/pcbnew/class_board.cpp
@@ -2273,6 +2273,30 @@ TRACK* BOARD::CreateLockPoint( wxPoint& aPosition, TRACK* aSegment, PICKED_ITEMS
 }
 
 
+std::list<ZONE_CONTAINER*> BOARD::GetZoneList( bool aIncludeZonesInFootprints )
+{
+    std::list<ZONE_CONTAINER*> zones;
+
+    for( int ii = 0; ii < GetAreaCount(); ii++ )
+    {
+        zones.push_back( GetArea( ii ) );
+    }
+
+    if( aIncludeZonesInFootprints )
+    {
+        for( MODULE* mod = m_Modules; mod; mod = mod->Next() )
+        {
+            for( ZONE_CONTAINER* zone = mod->ZonesList(); zone; zone = zone->Next() )
+            {
+                zones.push_back( zone );
+            }
+        }
+    }
+
+    return zones;
+}
+
+
 ZONE_CONTAINER* BOARD::AddArea( PICKED_ITEMS_LIST* aNewZonesList, int aNetcode,
                                 PCB_LAYER_ID aLayer, wxPoint aStartPointPosition, int aHatch )
 {
diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h
index 248f3bd..f2fd10a 100644
--- a/pcbnew/class_board.h
+++ b/pcbnew/class_board.h
@@ -958,6 +958,7 @@ public:
     void RedrawFilledAreas( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode,
                             PCB_LAYER_ID aLayer );
 
+
     /**
      * Function SetAreasNetCodesFromNetNames
      * Set the .m_NetCode member of all copper areas, according to the area Net Name
@@ -1001,11 +1002,19 @@ public:
         return -1;
     }
 
+
+    /**
+     * Function GetZoneList
+     * @return a std::list of pointers to all board zones (possibly including zones in footprints)
+     */
+    std::list<ZONE_CONTAINER*> GetZoneList( bool aIncludeZonesInFootprints = false );
+
+
     /**
      * Function GetAreaCount
      * @return int - The number of Areas or ZONE_CONTAINER.
      */
-    int GetAreaCount() const
+    int GetAreaCount( bool aIncludeAreasInFootprints = false ) const
     {
         return (int) m_ZoneDescriptorList.size();
     }
diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp
index e34b5a1..00a09e1 100644
--- a/pcbnew/drc.cpp
+++ b/pcbnew/drc.cpp
@@ -616,31 +616,8 @@ void DRC::testZones()
 void DRC::testKeepoutAreas()
 {
     // Construct a list of areas to inspect
-    std::list<ZONE_CONTAINER*> areasToTest;
-
-    // Add board zones
-    for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
-    {
-        ZONE_CONTAINER* area = m_pcb->GetArea( ii );
-
-        if( area && area->GetIsKeepout() )
-        {
-            areasToTest.push_back( area );
-        }
-    }
-
-    // Add zones in footprints
-    for( auto mod : m_pcb->Modules() )
-    {
-        for( ZONE_CONTAINER* zone = mod->ZonesList(); zone; zone = zone->Next() )
-        {
-            if( zone->GetIsKeepout() )
-            {
-                areasToTest.push_back( zone );
-            }
-        }
-    }
-
+    // (include zones found in footprints)
+    auto areasToTest = m_pcb->GetZoneList( true );
 
     for( auto* area : areasToTest )
     {
@@ -818,14 +795,12 @@ void DRC::testTexts()
 
 bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg )
 {
-    // Test keepout areas for vias, tracks and pads inside keepout areas
-    for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
-    {
-        ZONE_CONTAINER* area = m_pcb->GetArea( ii );
-
-        if( !area->GetIsKeepout() )
-            continue;
+    // Construct list of areas to inspect
+    // (include areas found in footprints)
+    auto areasToTest = m_pcb->GetZoneList( true );
 
+    for( auto* area : areasToTest )
+    {
         if( aRefSeg->Type() == PCB_TRACE_T )
         {
             if( ! area->GetDoNotAllowTracks()  )
diff --git a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp
index 3c0ea70..0b19c12 100644
--- a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp
+++ b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp
@@ -358,11 +358,14 @@ void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeature
         }
     }
 
+    // Build a list of zones to test
+
+    // Zones drawn on the board, and in footprints
+    auto areasToTest = GetBoard()->GetZoneList( true );
+
     // Add zones outlines having an higher priority and keepout
-    for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ )
+    for( auto* zone : areasToTest )
     {
-        ZONE_CONTAINER* zone = GetBoard()->GetArea( ii );
-
         // If the zones share no common layers
         if( !CommonLayerExists( zone->GetLayerSet() ) )
             continue;
-- 
2.7.4

From b95b4d109e5b71fc099e5f38e8dcae1a6c399c59 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Wed, 11 Oct 2017 21:01:58 +1100
Subject: [PATCH 5/7] Added layer-set shortcut for internal layers

- If all internal layers are set, write *.In.Cu
---
 pcbnew/kicad_plugin.cpp | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp
index a23cb5c..ca5cf3d 100644
--- a/pcbnew/kicad_plugin.cpp
+++ b/pcbnew/kicad_plugin.cpp
@@ -1272,7 +1272,11 @@ void PCB_IO::formatLayers( LSET aLayerMask, int aNestLevel ) const
     LSET cu_mask = cu_all;
 
     if( m_board )
+    {
         cu_mask &= m_board->GetEnabledLayers();
+    }
+
+    LSET cu_internal = cu_mask & LSET::InternalCuMask();
 
     // output copper layers first, then non copper
 
@@ -1287,6 +1291,13 @@ void PCB_IO::formatLayers( LSET aLayerMask, int aNestLevel ) const
         aLayerMask &= ~fr_bk;
     }
 
+    // All inner copper layers are selected
+    if( ( aLayerMask & cu_internal) == cu_internal )
+    {
+        output += " *.In.Cu";
+        aLayerMask &= ~cu_internal;
+    }
+
     if( ( aLayerMask & adhes ) == adhes )
     {
         output += " *.Adhes";
-- 
2.7.4

From 8850ed6fd31e7dd8b4af29d6f45d03f9d9f21628 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Wed, 11 Oct 2017 23:21:46 +1100
Subject: [PATCH 6/7] Fixed keepout zone editor in modedit

- Bug fix
- Added internal copper layers
---
 pcbnew/class_drawsegment.cpp                      |  2 +-
 pcbnew/dialogs/dialog_keepout_area_properties.cpp | 16 +++++-----------
 pcbnew/modedit_onclick.cpp                        |  2 ++
 pcbnew/tool_pcb.cpp                               |  2 +-
 4 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp
index 38bef2b..194aec4 100644
--- a/pcbnew/class_drawsegment.cpp
+++ b/pcbnew/class_drawsegment.cpp
@@ -611,7 +611,7 @@ wxString DRAWSEGMENT::GetSelectMenuText() const
 
 BITMAP_DEF DRAWSEGMENT::GetMenuImage() const
 {
-    return add_dashed_line_xpm;
+    return add_polygon_xpm;
 }
 
 
diff --git a/pcbnew/dialogs/dialog_keepout_area_properties.cpp b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
index 4391f8d..539c85c 100644
--- a/pcbnew/dialogs/dialog_keepout_area_properties.cpp
+++ b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
@@ -179,28 +179,24 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::initDialog()
 
         row.clear();
         row.push_back( m_zonesettings.m_Layers.test( F_Cu ) );
-        row.push_back( wxVariant( makeLayerIcon( board->GetLayerName( F_Cu ), layerColor ) ) );
+        row.push_back( wxVariant( makeLayerIcon( _( "Front copper layer" ), layerColor ) ) );
         m_layers->AppendItem( row );
 
-        /*
         // Add Inner.Cu layer entry
         row.clear();
-
         // Test if any layers are set
         LSET internal = m_zonesettings.m_Layers & LSET::InternalCuMask();
-        row.push_back( m_zonesettings.m_Layers.test( internal.count() > 0 ) );
+        row.push_back( internal.count() > 0 );
         layerColor = m_parent->Settings().Colors().GetLayerColor( In1_Cu );
-        row.push_back( wxVariant( makeLayerIcon( _( "Internal layers" ), layerColor ) ) );
+        row.push_back( wxVariant( makeLayerIcon( _( "Internal copper layers" ), layerColor ) ) );
         m_layers->AppendItem( row );
 
-        */
-
         // Add B.Cu layer entry
         row.clear();
         layerColor = m_parent->Settings().Colors().GetLayerColor( B_Cu );
         row.push_back( m_zonesettings.m_Layers.test( B_Cu ) );
         layerColor = m_parent->Settings().Colors().GetLayerColor( B_Cu );
-        row.push_back( wxVariant( makeLayerIcon( board->GetLayerName( B_Cu ), layerColor ) ) );
+        row.push_back( wxVariant( makeLayerIcon( _( "Back copper layer" ), layerColor ) ) );
         m_layers->AppendItem( row );
 
     }
@@ -273,7 +269,6 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::OnLayerSelection( wxDataViewEvent& event )
         case 0: // F.Cu
             m_zonesettings.m_Layers.set( F_Cu, selected );
             break;
-        /*
         case 1: // Inner layers
             if( selected )
             {
@@ -284,8 +279,7 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::OnLayerSelection( wxDataViewEvent& event )
                 m_zonesettings.m_Layers &= ~( LSET::InternalCuMask() );
             }
             break;
-        */
-        case 1: // B.Cu
+        case 2: // B.Cu
             m_zonesettings.m_Layers.set( B_Cu, selected );
             break;
         default:
diff --git a/pcbnew/modedit_onclick.cpp b/pcbnew/modedit_onclick.cpp
index b3f9a4f..521075f 100644
--- a/pcbnew/modedit_onclick.cpp
+++ b/pcbnew/modedit_onclick.cpp
@@ -558,6 +558,8 @@ void FOOTPRINT_EDIT_FRAME::OnEditItemRequest( wxDC* aDC, BOARD_ITEM* aItem )
 
         zoneSettings << *zone;
 
+        zoneSettings.SetIsInFootprint( true );
+
         bool success = false;
 
         if( zone )
diff --git a/pcbnew/tool_pcb.cpp b/pcbnew/tool_pcb.cpp
index 193efce..a32b346 100644
--- a/pcbnew/tool_pcb.cpp
+++ b/pcbnew/tool_pcb.cpp
@@ -452,7 +452,7 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
                             _( "Add keepout areas" ), wxITEM_CHECK );
 
     m_drawToolBar->AddSeparator();
-    m_drawToolBar->AddTool( ID_PCB_ADD_LINE_BUTT, wxEmptyString, KiBitmap( add_dashed_line_xpm ),
+    m_drawToolBar->AddTool( ID_PCB_ADD_LINE_BUTT, wxEmptyString, KiBitmap( add_polygon_xpm ),
                             _( "Add graphic line or polygon" ), wxITEM_CHECK );
 
     m_drawToolBar->AddTool( ID_PCB_CIRCLE_BUTT, wxEmptyString, KiBitmap( add_circle_xpm ),
-- 
2.7.4

From 0cd4ad948c5022d1b7149f1d8675f85ae19b0bf5 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Thu, 12 Oct 2017 00:19:11 +1100
Subject: [PATCH 7/7] Selection fixes for ZONE_CONTAINER

---
 pcbnew/tools/selection_tool.cpp | 44 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 40 insertions(+), 4 deletions(-)

diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
index 6912e54..b2ad302 100644
--- a/pcbnew/tools/selection_tool.cpp
+++ b/pcbnew/tools/selection_tool.cpp
@@ -1427,6 +1427,17 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
     case PCB_ZONE_AREA_T:
         // Keepout zones can exist on multiple layers!
         {
+            // In PCB editor, don't allow zone selection if parent is locked
+            if( !m_editModules )
+            {
+                MODULE* mod = static_cast<MODULE*>( static_cast<const ZONE_CONTAINER*>( aItem )->GetParent() );
+
+                if( mod && mod->IsLocked() )
+                {
+                    return false;
+                }
+            }
+
             auto* zone = static_cast<const ZONE_CONTAINER*>( aItem );
 
             if( zone && zone->GetIsKeepout() )
@@ -1500,7 +1511,9 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
         {
             MODULE* mod = static_cast<const D_PAD*>( aItem )->GetParent();
             if( mod && mod->IsLocked() )
+            {
                 return false;
+            }
         }
 
         break;
@@ -1953,10 +1966,14 @@ bool SELECTION_TOOL::SanitizeSelection()
         for( auto i : m_selection )
         {
             auto item = static_cast<BOARD_ITEM*>( i );
-            if( item->Type() == PCB_PAD_T )
-            {
-                MODULE* mod = static_cast<MODULE*>( item->GetParent() );
 
+            MODULE* mod = static_cast<MODULE*>( item->GetParent() );
+
+            bool parentSelected = mod && m_selection.Contains( mod );
+
+            switch( item->Type() )
+            {
+            case PCB_PAD_T:
                 // case 1: module (or its pads) are locked
                 if( mod && ( mod->PadsLocked() || mod->IsLocked() ) )
                 {
@@ -1967,8 +1984,23 @@ bool SELECTION_TOOL::SanitizeSelection()
                 }
 
                 // case 2: multi-item selection contains both the module and its pads - remove the pads
-                if( mod && m_selection.Contains( mod ) )
+                if( parentSelected )
+                {
                     rejected.insert( item );
+                }
+
+                break;
+
+            case PCB_ZONE_AREA_T:
+                // Don't select a zone if its parent is selected
+                if( !m_editModules && parentSelected )
+                {
+                    rejected.insert( item );
+                }
+                break;
+
+            default:
+                break;
             }
         }
     }
@@ -1976,7 +2008,9 @@ bool SELECTION_TOOL::SanitizeSelection()
     if( !rejected.empty() )
     {
         for( BOARD_ITEM* item : rejected )
+        {
             unselect( item );
+        }
 
         // Inform other potentially interested tools
         m_toolMgr->ProcessEvent( UnselectedEvent );
@@ -1985,7 +2019,9 @@ bool SELECTION_TOOL::SanitizeSelection()
     if( !added.empty() )
     {
         for( BOARD_ITEM* item : added )
+        {
             select( item );
+        }
 
         // Inform other potentially interested tools
         m_toolMgr->ProcessEvent( SelectedEvent );
-- 
2.7.4


Follow ups