From 0e28164b7a23dedf1cf7f115fe25134d8c1aea38 Mon Sep 17 00:00:00 2001
From: Jon Evans <jon@craftyjon.com>
Date: Sat, 3 Mar 2018 17:38:46 -0500
Subject: [PATCH] Rework footprint selection filtering to improve behavior

---
 pcbnew/class_module.cpp         | 31 +++++++++++++++++++++++++++++++
 pcbnew/class_module.h           |  9 +++++++++
 pcbnew/tools/selection_tool.cpp | 34 +++++++++++++++++++++++++++++-----
 3 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp
index 9968991b4..07882aa3a 100644
--- a/pcbnew/class_module.cpp
+++ b/pcbnew/class_module.cpp
@@ -42,6 +42,7 @@
 #include <macros.h>
 #include <msgpanel.h>
 #include <bitmaps.h>
+#include <unordered_set>
 
 #include <pcb_edit_frame.h>
 #include <class_board.h>
@@ -924,6 +925,36 @@ void MODULE::RunOnChildren( const std::function<void (BOARD_ITEM*)>& aFunction )
     }
 }
 
+
+void MODULE::GetAllDrawingLayers( int aLayers[], int& aCount, bool aIncludePads ) const
+{
+    std::unordered_set<int> layers;
+
+    for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() )
+    {
+        layers.insert( static_cast<int>( item->GetLayer() ) );
+    }
+
+    if( aIncludePads )
+    {
+        for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+        {
+            int pad_layers[KIGFX::VIEW::VIEW_MAX_LAYERS], pad_layers_count;
+            pad->ViewGetLayers( pad_layers, pad_layers_count );
+
+            for( int i = 0; i < pad_layers_count; i++ )
+                layers.insert( pad_layers[i] );
+        }
+    }
+
+    aCount = layers.size();
+    int i = 0;
+
+    for( auto layer : layers )
+        aLayers[i++] = layer;
+}
+
+
 void MODULE::ViewGetLayers( int aLayers[], int& aCount ) const
 {
     aCount = 2;
diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h
index 0af325301..8b82b6927 100644
--- a/pcbnew/class_module.h
+++ b/pcbnew/class_module.h
@@ -610,6 +610,15 @@ public:
      */
     void RunOnChildren( const std::function<void (BOARD_ITEM*)>& aFunction );
 
+    /**
+     * Returns a set of all layers that this module has drawings on
+     * similar to ViewGetLayers()
+     *
+     * @param aLayers is an array to store layer ids
+     * @param aCount is the number of layers stored in the array
+     * @param aIncludePads controls whether to also include pad layers
+     */
+    void GetAllDrawingLayers( int aLayers[], int& aCount, bool aIncludePads = true ) const;
 
     virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
 
diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
index aae688207..25d40f155 100644
--- a/pcbnew/tools/selection_tool.cpp
+++ b/pcbnew/tools/selection_tool.cpp
@@ -1599,13 +1599,37 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
         if( viewArea > 0.0 && modArea > viewArea )
             return false;
 
-        if( aItem->IsOnLayer( F_Cu ) && board()->IsElementVisible( LAYER_MOD_FR ) )
-            return !m_editModules;
+        // Allow selection of footprints if at least one draw layer is on and
+        // the appropriate LAYER_MOD is on
 
-        if( aItem->IsOnLayer( B_Cu ) && board()->IsElementVisible( LAYER_MOD_BK ) )
-            return !m_editModules;
+        bool layer_mod = ( ( aItem->IsOnLayer( F_Cu ) && board()->IsElementVisible( LAYER_MOD_FR ) ) ||
+                           ( aItem->IsOnLayer( B_Cu ) && board()->IsElementVisible( LAYER_MOD_BK ) ) );
 
-        return false;
+        bool draw_layer_visible = false;
+        bool pads_visible = ( ( aItem->IsOnLayer( F_Cu ) && board()->IsElementVisible( LAYER_PAD_FR ) ) ||
+                              ( aItem->IsOnLayer( B_Cu ) && board()->IsElementVisible( LAYER_PAD_BK ) ) );
+        int draw_layers[KIGFX::VIEW::VIEW_MAX_LAYERS], draw_layers_count;
+
+        static_cast<const MODULE*>( aItem )->GetAllDrawingLayers( draw_layers,
+                                                                  draw_layers_count,
+                                                                  pads_visible );
+
+        for( int i = 0; i < draw_layers_count; ++i )
+        {
+            if( board()->IsLayerVisible( static_cast<PCB_LAYER_ID>( draw_layers[i] ) ) )
+                draw_layer_visible = true;
+        }
+
+        // And finally, the pads layers count as draw layers too, if the copper layer is on
+        if( ( aItem->IsOnLayer( F_Cu ) &&
+              board()->IsElementVisible( LAYER_PAD_FR ) && board()->IsLayerVisible( F_Cu ) ) ||
+            ( aItem->IsOnLayer( B_Cu ) &&
+              board()->IsElementVisible( LAYER_PAD_BK ) && board()->IsLayerVisible( B_Cu ) ) )
+        {
+            draw_layer_visible = true;
+        }
+
+        return ( draw_layer_visible && layer_mod );
 
         break;
     }
-- 
2.13.1

