← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Fix layer color swatches in Linux

 

Hi Wayne,

I rebased the patches onto the COLOR4D work.

I've also added a further refactor than generalises the row indicator
icons, which removes a bit more logic from the layer widgets, and can
be reused as a generic switchable icon for other purposes if needed (I
had an Inkscape-style "eye" for the visibility checkbox in mind, but
it's not limited to two states, or to just the layer widget).

I've put both the COLOR_SWATCH and INDICATOR_ICONS in common/widgets.

There's a lot more scope for tidying this widget, and other UI ideas
abound. This should help a little to tidying up, and at least makes it
usable and consistent across platforms for v5.

Cheers,

John

On Tue, Feb 21, 2017 at 11:05 PM, John Beard <john.j.beard@xxxxxxxxx> wrote:
> On Tue, Feb 21, 2017 at 10:41 PM, Wayne Stambaugh <stambaughw@xxxxxxxxx> wrote:
>> I forgot to mention that this will likely clash with the COLOR4D work
>> that is already in progress so we will have to coordinate the changes.
>
> Happy to rebase as needed.
>
>> On 2/21/2017 9:35 AM, Wayne Stambaugh wrote:
>>> My only issue with this change is that the tooltip letting the user know
>>> that a left button double click or a middle button click would allow
>>> them to change the color is gone.
>
> The tooltip is still there for me (Linux). I specifically checked it,
> as I think it's a useful thing, since there's no other affordance on
> the swatches to tell you what to do. I hope it's not platform specfic
> as to whether the swatch wxStaticBitmap or the COLOR_SWATCH panel
> provide the tooltip.
>
> Cheers,
>
> John
From 8dc00c8c102b644768c8fda411daa41b6af075e5 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 22 Feb 2017 20:34:23 +0800
Subject: [PATCH 4/4] Break row indicators out to own class

The introduces INDICATOR_ICON, which is a very simple class holding a
bitmap that can toggle on or off.

The ICON_PROVIDER class then provides icons to INDICATOR_ICONS, which
means the class can be used for more than just row indicators.

A default row icon provider is also provided for use in the standard row
selector.
---
 common/CMakeLists.txt                    |   1 +
 common/widgets/indicator_icon.cpp        | 191 +++++++++++++++++++++++++++++++
 gerbview/class_gerbview_layer_widget.cpp |  21 ----
 gerbview/class_gerbview_layer_widget.h   |   7 --
 include/widgets/indicator_icon.h         | 140 ++++++++++++++++++++++
 pcbnew/layer_widget.cpp                  | 152 ++++++------------------
 pcbnew/layer_widget.h                    |  13 ++-
 7 files changed, 375 insertions(+), 150 deletions(-)
 create mode 100644 common/widgets/indicator_icon.cpp
 create mode 100644 include/widgets/indicator_icon.h

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index f2c6f04b3..349f6bc01 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -166,6 +166,7 @@ set( COMMON_WIDGET_SRCS
     widgets/widget_hotkey_list.cpp
     widgets/two_column_tree_list.cpp
     widgets/footprint_preview_panel.cpp
+    widgets/indicator_icon.cpp
     )
 
 set( COMMON_PAGE_LAYOUT_SRCS
diff --git a/common/widgets/indicator_icon.cpp b/common/widgets/indicator_icon.cpp
new file mode 100644
index 000000000..c365c390c
--- /dev/null
+++ b/common/widgets/indicator_icon.cpp
@@ -0,0 +1,191 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include <widgets/indicator_icon.h>
+
+
+INDICATOR_ICON::INDICATOR_ICON( wxWindow* aParent,
+                              ICON_PROVIDER& aIconProvider,
+                              ICON_ID aInitialIcon, int aID ):
+        wxPanel( aParent, aID ),
+        m_iconProvider( aIconProvider ),
+        m_currentId( aInitialIcon )
+{
+    auto sizer = new wxBoxSizer( wxHORIZONTAL );
+    SetSizer( sizer );
+
+    const wxBitmap& initBitmap = m_iconProvider.GetIndicatorIcon( m_currentId );
+
+    m_bitmap = new wxStaticBitmap( this, aID,
+                                   initBitmap, wxDefaultPosition,
+                                   initBitmap.GetSize() );
+
+    sizer->Add( m_bitmap, 0, 0 );
+
+    auto evtSkipper = [this] ( wxEvent& aEvent ) {
+        wxPostEvent( this, aEvent );
+    };
+
+    m_bitmap->Bind( wxEVT_LEFT_DOWN, evtSkipper );
+}
+
+
+void INDICATOR_ICON::SetIndicatorState( ICON_ID aIconId )
+{
+    if( aIconId == m_currentId )
+        return;
+
+    m_currentId = aIconId;
+
+    m_bitmap->SetBitmap( m_iconProvider.GetIndicatorIcon( m_currentId ) );
+}
+
+
+INDICATOR_ICON::ICON_ID INDICATOR_ICON::GetIndicatorState() const
+{
+    return m_currentId;
+}
+
+// ====================================================================
+// Common icon providers
+
+/* XPM
+ * This bitmap is used for not selected layers
+ */
+static const char * clear_xpm[] = {
+"10 14 1 1",
+" 	c None",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          ",
+"          "};
+
+/* XPM
+ * This bitmap can be used to show a not selected layer
+ * with special property (mainly not selected layers not in use in GerbView)
+ */
+static const char * clear_alternate_xpm[] = {
+"10 14 4 1",
+"       c None",
+"X      c #008080",
+"o      c GREEN",
+"O      c #00B080",
+"          ",
+"          ",
+"          ",
+"          ",
+"    X     ",
+"   XXX    ",
+"  XXXXX   ",
+" OOOOOOO  ",
+"  ooooo   ",
+"   ooo    ",
+"    o     ",
+"          ",
+"          ",
+"          "};
+
+
+/* XPM
+ * This bitmap  is used for a normale selected layer
+ */
+static const char * rightarrow_xpm[] = {
+"10 14 4 1",
+"       c None",
+"X      c #8080ff",
+"o      c BLUE",
+"O      c gray56",
+"  X       ",
+"  XX      ",
+"  XXX     ",
+"  XXXX    ",
+"  XXXXX   ",
+"  XXXXXX  ",
+"  XXXXXXX ",
+"  oooooooO",
+"  ooooooO ",
+"  oooooO  ",
+"  ooooO   ",
+"  oooO    ",
+"  ooO     ",
+"  oO      "};
+
+/* XPM
+ * This bitmap can be used to show the selected layer
+ * with special property (mainly a layer in use in GerbView)
+ */
+static const char * rightarrow_alternate_xpm[] = {
+"10 14 5 1",
+"       c None",
+".      c #00B000",
+"X      c #8080ff",
+"o      c BLUE",
+"O      c gray56",
+"..X       ",
+"..XX      ",
+"..XXX     ",
+"..XXXX    ",
+"..XXXXX   ",
+"..XXXXXX  ",
+"..XXXXXXX ",
+"..oooooooO",
+"..ooooooO ",
+"..oooooO  ",
+"..ooooO   ",
+"..oooO    ",
+"..ooO     ",
+"..oO      "};
+
+
+
+static wxBitmap rightArrowBitmap( rightarrow_xpm );
+static wxBitmap rightArrowAlternateBitmap( rightarrow_alternate_xpm );
+static wxBitmap blankBitmap( clear_xpm );
+static wxBitmap blankAlternateBitmap( clear_alternate_xpm );
+
+
+ROW_ICON_PROVIDER::ROW_ICON_PROVIDER( bool aAlt ):
+        m_alt( aAlt )
+{}
+
+
+const wxBitmap& ROW_ICON_PROVIDER::GetIndicatorIcon(
+            INDICATOR_ICON::ICON_ID aIconId ) const
+{
+    const bool on = ( aIconId == STATE::ON );
+
+    if( m_alt )
+        return ( on ? rightArrowAlternateBitmap : blankAlternateBitmap );
+
+    return ( on ? rightArrowBitmap : blankBitmap );
+}
diff --git a/gerbview/class_gerbview_layer_widget.cpp b/gerbview/class_gerbview_layer_widget.cpp
index f6c6ca9bb..1470417d4 100644
--- a/gerbview/class_gerbview_layer_widget.cpp
+++ b/gerbview/class_gerbview_layer_widget.cpp
@@ -314,24 +314,3 @@ bool GERBER_LAYER_WIDGET::useAlternateBitmap(int aRow)
 {
     return GetImagesList()->GetGbrImage( aRow ) != NULL;
 }
-
-/*
- * Update the layer manager icons (layers only)
- * Useful when loading a file or clearing a layer because they change
- */
-void GERBER_LAYER_WIDGET::UpdateLayerIcons()
-{
-    int row_count = GetLayerRowCount();
-    for( int row = 0; row < row_count ; row++ )
-    {
-        wxStaticBitmap* bm = (wxStaticBitmap*) getLayerComp( row, COLUMN_ICON_ACTIVE );
-        if( bm == NULL)
-            continue;
-
-        if( row == m_CurrentRow )
-            bm->SetBitmap( useAlternateBitmap(row) ? *m_RightArrowAlternateBitmap :
-                           *m_RightArrowBitmap );
-        else
-            bm->SetBitmap( useAlternateBitmap(row) ? *m_BlankAlternateBitmap : *m_BlankBitmap );
-    }
-}
diff --git a/gerbview/class_gerbview_layer_widget.h b/gerbview/class_gerbview_layer_widget.h
index 8e5c6ef18..ad40533af 100644
--- a/gerbview/class_gerbview_layer_widget.h
+++ b/gerbview/class_gerbview_layer_widget.h
@@ -125,13 +125,6 @@ public:
     bool OnLayerSelected();     // postprocess after an active layer selection
                                 // ensure active layer visible if
                                 // m_alwaysShowActiveCopperLayer is true;
-
-    /**
-     * Function UpdateLayerIcons
-     * Update the layer manager icons (layers only)
-     * Useful when loading a file or clearing a layer because they change
-     */
-    void UpdateLayerIcons();
 };
 
 #endif  // _CLASS_GERBER_LAYER_WIDGET_H_
diff --git a/include/widgets/indicator_icon.h b/include/widgets/indicator_icon.h
new file mode 100644
index 000000000..fc3436ab9
--- /dev/null
+++ b/include/widgets/indicator_icon.h
@@ -0,0 +1,140 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef ROW_INDICATOR__H_
+#define ROW_INDICATOR__H_
+
+#include <wx/wx.h>
+
+/**
+ * Class representing a row indicator icon for use in
+ * places like the layer widget
+ */
+class INDICATOR_ICON: public wxPanel
+{
+public:
+
+    /**
+     * An id that refers to a certain icon state.
+     *
+     * Exactly what that state might mean in terms of icons is up
+     * to the icon provider.
+     */
+    using ICON_ID = int;
+
+    /**
+     * A simple object that can provide fixed bitmaps for use as row
+     * indicators
+     */
+    class ICON_PROVIDER
+    {
+    public:
+
+        virtual ~ICON_PROVIDER() {};
+
+        /**
+         * Gets a reference to the row icon in the given mode
+         *
+         * @param aIconId the id of the icon to get (depends on the
+         * provider).
+         */
+        virtual const wxBitmap& GetIndicatorIcon( ICON_ID aIconId ) const = 0;
+    };
+
+    /**
+     * Accessor for the default icon providers, which take
+     * true and false for IDs, meaining on/off.
+     *
+     * @param aAlternative false for blue arrow/blank, true for the
+     * green diamond
+     */
+    static ICON_PROVIDER& GetDefaultRowIconProvider( bool aAlternative );
+
+    /**
+     * @param aParent the owning window
+     * @param aIconProvider the icon provider to get icons from
+     * @param aID the ID to use for the widgets - events will have
+     * this ID.
+     */
+    INDICATOR_ICON( wxWindow* aParent,
+                   ICON_PROVIDER& aIconProvider,
+                   ICON_ID aInitialIcon, int aID );
+
+    /**
+     * Sets the row indiciator to the given state
+     *
+     * @param aIconId the icon ID to pass to the provider.
+     */
+    void SetIndicatorState( ICON_ID aIconId );
+
+
+    /**
+     * @return the current state of the indicator
+     */
+    ICON_ID GetIndicatorState() const;
+
+private:
+
+    ///> An class that delivers icons for the indictor (currently just
+    ///> uses a default implementation).
+    ICON_PROVIDER& m_iconProvider;
+
+    ///> Handle on the bitmap widget
+    wxStaticBitmap* m_bitmap;
+
+    ///> Is the icon currently "on"
+    ICON_ID m_currentId;
+};
+
+
+/**
+ * Icon provider for the "standard" row indicators, for example in
+ * layer selection lists
+ */
+class ROW_ICON_PROVIDER: public INDICATOR_ICON::ICON_PROVIDER
+{
+public:
+
+    ///> State constants to select the right icons
+    enum STATE
+    {
+        OFF,    ///> Row "off" or "deselected"
+        ON,     ///> Row "on" or "selected"
+    };
+
+    /**
+     * @param aAlt false: normal icons (blue arrow/blank), true:
+     * alternative icons (blue arrow/green diamond)
+     */
+    ROW_ICON_PROVIDER( bool aAlt );
+
+    ///> @copydoc INDICATOR_ICON::ICON_PROVIDER::GetIndicatorIcon()
+    const wxBitmap& GetIndicatorIcon( INDICATOR_ICON::ICON_ID aIconId ) const override;
+
+private:
+    bool m_alt;
+};
+
+
+
+#endif // ROW_INDICATOR__H_
diff --git a/pcbnew/layer_widget.cpp b/pcbnew/layer_widget.cpp
index 2f03f8356..7ceb0ab0d 100644
--- a/pcbnew/layer_widget.cpp
+++ b/pcbnew/layer_widget.cpp
@@ -38,6 +38,7 @@
 #include <macros.h>
 #include <common.h>
 #include <widgets/color_swatch.h>
+#include <widgets/indicator_icon.h>
 #include <wx/colour.h>
 #include <wx/colordlg.h>
 
@@ -46,103 +47,11 @@
 
 const wxEventType LAYER_WIDGET::EVT_LAYER_COLOR_CHANGE = wxNewEventType();
 
-/* XPM
- * This bitmap is used for not selected layers
- */
-static const char * clear_xpm[] = {
-"10 14 1 1",
-" 	c None",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          ",
-"          "};
-
-/* XPM
- * This bitmap can be used to show a not selected layer
- * with special property (mainly not selected layers not in use in GerbView)
- */
-static const char * clear_alternate_xpm[] = {
-"10 14 4 1",
-"       c None",
-"X      c #008080",
-"o      c GREEN",
-"O      c #00B080",
-"          ",
-"          ",
-"          ",
-"          ",
-"    X     ",
-"   XXX    ",
-"  XXXXX   ",
-" OOOOOOO  ",
-"  ooooo   ",
-"   ooo    ",
-"    o     ",
-"          ",
-"          ",
-"          "};
-
-
-/* XPM
- * This bitmap  is used for a normale selected layer
- */
-static const char * rightarrow_xpm[] = {
-"10 14 4 1",
-"       c None",
-"X      c #8080ff",
-"o      c BLUE",
-"O      c gray56",
-"  X       ",
-"  XX      ",
-"  XXX     ",
-"  XXXX    ",
-"  XXXXX   ",
-"  XXXXXX  ",
-"  XXXXXXX ",
-"  oooooooO",
-"  ooooooO ",
-"  oooooO  ",
-"  ooooO   ",
-"  oooO    ",
-"  ooO     ",
-"  oO      "};
-
-/* XPM
- * This bitmap can be used to show the selected layer
- * with special property (mainly a layer in use in GerbView)
+/*
+ * Icon providers for the row icons
  */
-static const char * rightarrow_alternate_xpm[] = {
-"10 14 5 1",
-"       c None",
-".      c #00B000",
-"X      c #8080ff",
-"o      c BLUE",
-"O      c gray56",
-"..X       ",
-"..XX      ",
-"..XXX     ",
-"..XXXX    ",
-"..XXXXX   ",
-"..XXXXXX  ",
-"..XXXXXXX ",
-"..oooooooO",
-"..ooooooO ",
-"..oooooO  ",
-"..ooooO   ",
-"..oooO    ",
-"..ooO     ",
-"..oO      "};
-
+static ROW_ICON_PROVIDER defaultRowIcons( false );
+static ROW_ICON_PROVIDER alternativeRowIcons( true );
 
 /**
  * Function shrinkFont
@@ -337,11 +246,13 @@ void LAYER_WIDGET::insertLayerRow( int aRow, const ROW& aSpec )
     int         index = aRow * LYR_COLUMN_COUNT;
     const int   flags = wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT;
 
+    auto& iconProvider = useAlternateBitmap(aRow) ? alternativeRowIcons : defaultRowIcons;
+
     // column 0
-    col = 0;
-    wxStaticBitmap* sbm = new wxStaticBitmap( m_LayerScrolledWindow, encodeId( col, aSpec.id ),
-                            useAlternateBitmap(aRow) ? *m_BlankAlternateBitmap : *m_BlankBitmap,
-                            wxDefaultPosition, m_BitmapSize );
+    col = COLUMN_ICON_ACTIVE;
+    auto sbm = new INDICATOR_ICON( m_LayerScrolledWindow, iconProvider,
+                                   ROW_ICON_PROVIDER::STATE::OFF,
+                                   encodeId( col, aSpec.id ) );
     sbm->Bind( wxEVT_LEFT_DOWN, &LAYER_WIDGET::OnLeftDownLayers, this );
     m_LayersFlexGridSizer->wxSizer::Insert( index+col, sbm, 0, flags );
 
@@ -493,13 +404,6 @@ LAYER_WIDGET::LAYER_WIDGET( wxWindow* aParent, wxWindow* aFocusOwner, int aPoint
 
     m_CurrentRow = -1;  // hide the arrow initially
 
-    m_RightArrowBitmap = new wxBitmap( rightarrow_xpm );
-    m_RightArrowAlternateBitmap = new wxBitmap( rightarrow_alternate_xpm );
-
-    m_BlankBitmap = new wxBitmap( clear_xpm );     // translucent
-    m_BlankAlternateBitmap = new wxBitmap( clear_alternate_xpm );
-    m_BitmapSize = wxSize(m_BlankBitmap->GetWidth(), m_BlankBitmap->GetHeight());
-
     // trap the tab changes so that we can call passOnFocus().
     m_notebook->Bind( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, &LAYER_WIDGET::OnTabChange, this );
 
@@ -509,10 +413,6 @@ LAYER_WIDGET::LAYER_WIDGET( wxWindow* aParent, wxWindow* aFocusOwner, int aPoint
 
 LAYER_WIDGET::~LAYER_WIDGET()
 {
-    delete m_BlankBitmap;
-    delete m_BlankAlternateBitmap;
-    delete m_RightArrowBitmap;
-    delete m_RightArrowAlternateBitmap;
 }
 
 
@@ -615,14 +515,14 @@ void LAYER_WIDGET::SelectLayerRow( int aRow )
     // enable the layer tab at index 0
     m_notebook->SetSelection( 0 );
 
-    wxStaticBitmap* oldbm = (wxStaticBitmap*) getLayerComp( m_CurrentRow, 0 );
-    if( oldbm )
-        oldbm->SetBitmap( useAlternateBitmap(m_CurrentRow) ? *m_BlankAlternateBitmap : *m_BlankBitmap );
+    INDICATOR_ICON* oldIndicator = (INDICATOR_ICON*) getLayerComp( m_CurrentRow, 0 );
+    if( oldIndicator )
+        oldIndicator->SetIndicatorState( ROW_ICON_PROVIDER::STATE::OFF );
 
-    wxStaticBitmap* newbm = (wxStaticBitmap*) getLayerComp( aRow, 0 );
-    if( newbm )
+    INDICATOR_ICON* newIndicator = (INDICATOR_ICON*) getLayerComp( aRow, 0 );
+    if( newIndicator )
     {
-        newbm->SetBitmap( useAlternateBitmap(aRow) ? *m_RightArrowAlternateBitmap : *m_RightArrowBitmap );
+        newIndicator->SetIndicatorState( ROW_ICON_PROVIDER::STATE::ON );
 
         // Make sure the desired layer row is visible.
         // It seems that as of 2.8.2, setting the focus does this.
@@ -748,6 +648,24 @@ void LAYER_WIDGET::UpdateLayouts()
     FitInside();
 }
 
+
+void LAYER_WIDGET::UpdateLayerIcons()
+{
+    int rowCount = GetLayerRowCount();
+    for( int row = 0; row < rowCount ; row++ )
+    {
+        INDICATOR_ICON* indicator = (INDICATOR_ICON*) getLayerComp( row, COLUMN_ICON_ACTIVE );
+
+        if( indicator )
+        {
+            auto state = ( row == m_CurrentRow ) ? ROW_ICON_PROVIDER::STATE::ON
+                                                 : ROW_ICON_PROVIDER::STATE::OFF;
+            indicator->SetIndicatorState( state );
+        }
+    }
+}
+
+
 #if defined(STAND_ALONE)
 
 #include <wx/aui/aui.h>
diff --git a/pcbnew/layer_widget.h b/pcbnew/layer_widget.h
index 635ee18a2..99949b59d 100644
--- a/pcbnew/layer_widget.h
+++ b/pcbnew/layer_widget.h
@@ -113,11 +113,6 @@ protected:
     wxFlexGridSizer*    m_RenderFlexGridSizer;
 
     wxWindow*           m_FocusOwner;
-    wxBitmap*           m_BlankBitmap;
-    wxBitmap*           m_BlankAlternateBitmap;
-    wxBitmap*           m_RightArrowBitmap;
-    wxBitmap*           m_RightArrowAlternateBitmap;
-    wxSize              m_BitmapSize;
     int                 m_CurrentRow;           ///< selected row of layer list
     int                 m_PointSize;
 
@@ -363,6 +358,14 @@ public:
 
     void UpdateLayouts();
 
+    /**
+     * Function UpdateLayerIcons
+     * Update all layer manager icons (layers only)
+     * Useful when loading a file or clearing a layer because they change,
+     * and the indicator arrow icon needs to be updated
+     */
+    void UpdateLayerIcons();
+
 /*  did not help:
     void Freeze()
     {
-- 
2.11.0

From 990653e3b97a6db5baba5ddab9f77d9c84f39596 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Tue, 21 Feb 2017 20:43:02 +0800
Subject: [PATCH 3/4] Use Bind rather than Connect in LAYER_WIDGET

Bind is safer than Connect. Bind is more flexible and has a more concise
signature.
---
 pcbnew/layer_widget.cpp | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/pcbnew/layer_widget.cpp b/pcbnew/layer_widget.cpp
index 5d3a53d52..2f03f8356 100644
--- a/pcbnew/layer_widget.cpp
+++ b/pcbnew/layer_widget.cpp
@@ -342,7 +342,7 @@ void LAYER_WIDGET::insertLayerRow( int aRow, const ROW& aSpec )
     wxStaticBitmap* sbm = new wxStaticBitmap( m_LayerScrolledWindow, encodeId( col, aSpec.id ),
                             useAlternateBitmap(aRow) ? *m_BlankAlternateBitmap : *m_BlankBitmap,
                             wxDefaultPosition, m_BitmapSize );
-    sbm->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnLeftDownLayers ), NULL, this );
+    sbm->Bind( wxEVT_LEFT_DOWN, &LAYER_WIDGET::OnLeftDownLayers, this );
     m_LayersFlexGridSizer->wxSizer::Insert( index+col, sbm, 0, flags );
 
     // column 1 (COLUMN_COLORBM)
@@ -358,7 +358,7 @@ void LAYER_WIDGET::insertLayerRow( int aRow, const ROW& aSpec )
     col = COLUMN_COLOR_LYR_CB;
     wxCheckBox* cb = new wxCheckBox( m_LayerScrolledWindow, encodeId( col, aSpec.id ), wxEmptyString );
     cb->SetValue( aSpec.state );
-    cb->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( LAYER_WIDGET::OnLayerCheckBox ), NULL, this );
+    cb->Bind( wxEVT_COMMAND_CHECKBOX_CLICKED, &LAYER_WIDGET::OnLayerCheckBox, this );
     cb->SetToolTip( _( "Enable this for visibility" ) );
     m_LayersFlexGridSizer->wxSizer::Insert( index+col, cb, 0, flags );
 
@@ -366,7 +366,7 @@ void LAYER_WIDGET::insertLayerRow( int aRow, const ROW& aSpec )
     col = COLUMN_COLOR_LYRNAME;
     wxStaticText* st = new wxStaticText( m_LayerScrolledWindow, encodeId( col, aSpec.id ), aSpec.rowName );
     shrinkFont( st, m_PointSize );
-    st->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnLeftDownLayers ), NULL, this );
+    st->Bind( wxEVT_LEFT_DOWN, &LAYER_WIDGET::OnLeftDownLayers, this );
     st->SetToolTip( aSpec.tooltip );
     m_LayersFlexGridSizer->wxSizer::Insert( index+col, st, 0, flags );
 }
@@ -404,8 +404,7 @@ void LAYER_WIDGET::insertRenderRow( int aRow, const ROW& aSpec )
                         aSpec.rowName, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );
     shrinkFont( cb, m_PointSize );
     cb->SetValue( aSpec.state );
-    cb->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED,
-        wxCommandEventHandler( LAYER_WIDGET::OnRenderCheckBox ), NULL, this );
+    cb->Bind( wxEVT_COMMAND_CHECKBOX_CLICKED, &LAYER_WIDGET::OnRenderCheckBox, this );
     cb->SetToolTip( aSpec.tooltip );
     m_RenderFlexGridSizer->wxSizer::Insert( index+col, cb, 0, flags );
 }
@@ -502,8 +501,7 @@ LAYER_WIDGET::LAYER_WIDGET( wxWindow* aParent, wxWindow* aFocusOwner, int aPoint
     m_BitmapSize = wxSize(m_BlankBitmap->GetWidth(), m_BlankBitmap->GetHeight());
 
     // trap the tab changes so that we can call passOnFocus().
-    m_notebook->Connect( -1, wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED,
-        wxNotebookEventHandler( LAYER_WIDGET::OnTabChange ), NULL, this );
+    m_notebook->Bind( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, &LAYER_WIDGET::OnTabChange, this );
 
     Layout();
 }
-- 
2.11.0

From 003d5f08804a747dcff5f32c97c5ffe05cc56c8b Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Tue, 21 Feb 2017 20:31:19 +0800
Subject: [PATCH 2/4] Move layer/render swatches to own class

This introduces COLOR_SWATCH, which is a reusable
widget that shows a color swatch and can invoke the colour picker
when duble/middle clicked.

It uses it's own wxCommandEvent to signal the change.

This makes the layer widget simpler internally, and also allows other
code to show identical swatches if needed.
---
 common/CMakeLists.txt           |   1 +
 common/widgets/color_swatch.cpp | 153 ++++++++++++++++++++++++++++++++++++
 include/widgets/color_swatch.h  |  88 +++++++++++++++++++++
 pcbnew/layer_widget.cpp         | 167 ++++++----------------------------------
 pcbnew/layer_widget.h           |  12 ++-
 5 files changed, 275 insertions(+), 146 deletions(-)
 create mode 100644 common/widgets/color_swatch.cpp
 create mode 100644 include/widgets/color_swatch.h

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index dba4e7f61..f2c6f04b3 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -161,6 +161,7 @@ set( COMMON_DLG_SRCS
     )
 
 set( COMMON_WIDGET_SRCS
+    widgets/color_swatch.cpp
     widgets/mathplot.cpp
     widgets/widget_hotkey_list.cpp
     widgets/two_column_tree_list.cpp
diff --git a/common/widgets/color_swatch.cpp b/common/widgets/color_swatch.cpp
new file mode 100644
index 000000000..85a5c990b
--- /dev/null
+++ b/common/widgets/color_swatch.cpp
@@ -0,0 +1,153 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include <widgets/color_swatch.h>
+
+wxDEFINE_EVENT(COLOR_SWATCH_CHANGED, wxCommandEvent);
+
+using KIGFX::COLOR4D;
+
+
+const static int SWATCH_SIZE_X = 14;
+const static int SWATCH_SIZE_Y = 12;
+
+// See selcolor.cpp:
+extern COLOR4D DisplayColorFrame( wxWindow* aParent, COLOR4D aOldColor );
+
+
+/**
+ * Make a simple color swatch bitmap
+ */
+static wxBitmap makeBitmap( COLOR4D aColor )
+{
+    wxBitmap    bitmap( SWATCH_SIZE_X, SWATCH_SIZE_Y );
+    wxBrush     brush;
+    wxMemoryDC  iconDC;
+
+    iconDC.SelectObject( bitmap );
+
+    brush.SetColour( aColor.ToColour() );
+    brush.SetStyle( wxBRUSHSTYLE_SOLID );
+
+    iconDC.SetBrush( brush );
+
+    iconDC.DrawRectangle( 0, 0, SWATCH_SIZE_X, SWATCH_SIZE_Y );
+
+    return bitmap;
+}
+
+
+/**
+ * Function makeColorButton
+ * creates a wxStaticBitmap and assigns it a solid color and a control ID
+ */
+static std::unique_ptr<wxStaticBitmap> makeColorSwatch(
+        wxWindow* aParent, COLOR4D aColor, int aID )
+{
+    // construct a bitmap of the right color and make the swatch from it
+    wxBitmap bitmap = makeBitmap( aColor );
+    auto ret = std::make_unique<wxStaticBitmap>( aParent, aID, bitmap );
+
+    return ret;
+}
+
+
+COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, COLOR4D aColor, int aID ):
+        wxPanel( aParent, aID ),
+        m_color( aColor )
+{
+    auto sizer = new wxBoxSizer( wxHORIZONTAL );
+    SetSizer( sizer );
+
+    auto swatch = makeColorSwatch( this, m_color, aID );
+    m_swatch = swatch.get(); // hold a handle
+
+    sizer->Add( swatch.release(), 0, 0 );
+
+    // forward click to any other listeners, since we don't want them
+    m_swatch->Bind( wxEVT_LEFT_DOWN, &COLOR_SWATCH::rePostEvent, this );
+    m_swatch->Bind( wxEVT_RIGHT_DOWN, &COLOR_SWATCH::rePostEvent, this );
+
+    // bind the events that trigger the dialog
+    m_swatch->Bind( wxEVT_LEFT_DCLICK, [this] ( wxMouseEvent& aEvt ) {
+        GetNewSwatchColor();
+    } );
+
+    m_swatch->Bind( wxEVT_MIDDLE_DOWN, [this] ( wxMouseEvent& aEvt ) {
+        GetNewSwatchColor();
+    } );
+}
+
+
+void COLOR_SWATCH::rePostEvent( wxEvent& aEvt )
+{
+    wxPostEvent( this, aEvt );
+}
+
+
+static void sendSwatchChangeEvent( COLOR_SWATCH& aSender )
+{
+    wxCommandEvent changeEvt( COLOR_SWATCH_CHANGED );
+
+    // use this class as the object (alternative might be to
+    // set a custom event class but that's more work)
+    changeEvt.SetEventObject( &aSender );
+
+    wxPostEvent( &aSender, changeEvt );
+}
+
+
+void COLOR_SWATCH::SetSwatchColor( COLOR4D aColor, bool sendEvent )
+{
+    m_color = aColor;
+
+    wxBitmap bm = makeBitmap( aColor );
+    m_swatch->SetBitmap( bm );
+
+    if( sendEvent )
+    {
+        sendSwatchChangeEvent( *this );
+    }
+}
+
+
+COLOR4D COLOR_SWATCH::GetSwatchColor() const
+{
+    return m_color;
+}
+
+
+void COLOR_SWATCH::GetNewSwatchColor()
+{
+    COLOR4D newColor = DisplayColorFrame( this, m_color );
+
+    if( newColor != COLOR4D::UNSPECIFIED )
+    {
+        m_color = newColor;
+
+        wxBitmap bm = makeBitmap( newColor );
+        m_swatch->SetBitmap( bm );
+
+        sendSwatchChangeEvent( *this );
+    }
+}
diff --git a/include/widgets/color_swatch.h b/include/widgets/color_swatch.h
new file mode 100644
index 000000000..d634ef3bf
--- /dev/null
+++ b/include/widgets/color_swatch.h
@@ -0,0 +1,88 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef COLOR_SWATCH__H_
+#define COLOR_SWATCH__H_
+
+#include <wx/wx.h>
+
+#include <common.h>
+
+#include <gal/color4d.h>
+
+
+/**
+ * Class representing a simple color swatch, of the kind used to
+ * set layer colors
+ */
+class COLOR_SWATCH: public wxPanel
+{
+public:
+
+    /**
+     * Construct a COLOR_SWATCH
+     *
+     * @param aParent parent window
+     * @param aColor initial swatch color
+     * @param aID id to use when sending swatch events
+     */
+    COLOR_SWATCH( wxWindow* aParent, KIGFX::COLOR4D aColor, int aID );
+
+    /**
+     * Set the current swatch color directly.
+     */
+    void SetSwatchColor( KIGFX::COLOR4D aColor, bool sendEvent );
+
+    /**
+     * @return the current swatch color
+     */
+    KIGFX::COLOR4D GetSwatchColor() const;
+
+    /**
+     * Prompt for a new colour, using the colour picker dialog.
+     *
+     * A colour change event will be sent if it's set.
+     */
+    void GetNewSwatchColor();
+
+private:
+
+    /**
+     * Pass unwanted events on to listeners of this object
+     */
+    void rePostEvent( wxEvent& aEvt );
+
+    ///> The current colour of the swatch
+    KIGFX::COLOR4D m_color;
+
+    ///> Handle of the actual swatch shown
+    wxStaticBitmap* m_swatch;
+};
+
+
+/**
+ * Event signalling a swatch has changed color
+ */
+wxDECLARE_EVENT(COLOR_SWATCH_CHANGED, wxCommandEvent);
+
+#endif // COLOR_SWATCH__H_
diff --git a/pcbnew/layer_widget.cpp b/pcbnew/layer_widget.cpp
index d63c3f914..5d3a53d52 100644
--- a/pcbnew/layer_widget.cpp
+++ b/pcbnew/layer_widget.cpp
@@ -37,18 +37,13 @@
 
 #include <macros.h>
 #include <common.h>
+#include <widgets/color_swatch.h>
 #include <wx/colour.h>
 #include <wx/colordlg.h>
 
 #include <algorithm>
 
 
-const static int SWATCH_SIZE_X = 14;
-const static int SWATCH_SIZE_Y = 12;
-
-// See selcolor.cpp:
-extern COLOR4D DisplayColorFrame( wxWindow* aParent, COLOR4D aOldColor );
-
 const wxEventType LAYER_WIDGET::EVT_LAYER_COLOR_CHANGE = wxNewEventType();
 
 /* XPM
@@ -150,56 +145,6 @@ static const char * rightarrow_alternate_xpm[] = {
 
 
 /**
- * Function makeColorTxt
- * returns a string representing the color in CSS format
- * For example: "rgba(255, 0, 0, 255)"
- */
-static wxString makeColorTxt( COLOR4D aColor )
-{
-    return aColor.ToWxString( wxC2S_CSS_SYNTAX );
-}
-
-
-static wxBitmap makeBitmap( COLOR4D aColor )
-{
-    wxBitmap    bitmap( SWATCH_SIZE_X, SWATCH_SIZE_Y );
-    wxBrush     brush;
-    wxMemoryDC  iconDC;
-
-    iconDC.SelectObject( bitmap );
-
-    brush.SetColour( aColor.ToColour() );
-    brush.SetStyle( wxBRUSHSTYLE_SOLID );
-
-    iconDC.SetBrush( brush );
-
-    iconDC.DrawRectangle( 0, 0, SWATCH_SIZE_X, SWATCH_SIZE_Y );
-
-    return bitmap;
-}
-
-
-/**
- * Function makeColorButton
- * creates a wxStaticBitmap and assigns it a solid color and a control ID
- */
-static std::unique_ptr<wxStaticBitmap> makeColorSwatch( wxWindow* aParent,
-                                                        COLOR4D aColor, int aID )
-{
-    // dynamically make a wxBitMap and brush it with the appropriate color,
-    // then create a wxBitmapButton from it.
-    wxBitmap bitmap = makeBitmap( aColor );
-
-    // save the color value in the name, no where else to put it.
-    auto ret = std::make_unique<wxStaticBitmap>( aParent, aID, bitmap );
-
-    ret->SetName( makeColorTxt( aColor ) );
-
-    return ret;
-}
-
-
-/**
  * Function shrinkFont
  * reduces the size of the wxFont associated with \a aControl
  */
@@ -273,48 +218,20 @@ void LAYER_WIDGET::OnLeftDownLayers( wxMouseEvent& event )
 }
 
 
-void LAYER_WIDGET::OnMiddleDownLayerColor( wxMouseEvent& aEvent )
+void LAYER_WIDGET::OnLayerSwatchChanged( wxCommandEvent& aEvent )
 {
-    auto eventSource = static_cast<wxStaticBitmap*>( aEvent.GetEventObject() );
-
-    wxString colorTxt = eventSource->GetName();
-
-    COLOR4D oldColor;
-    wxASSERT( oldColor.SetFromWxString( colorTxt ) );
-    COLOR4D newColor = COLOR4D::UNSPECIFIED;
-
-    if( AreArbitraryColorsAllowed() )
-    {
-        wxColourData colourData;
-        colourData.SetColour( oldColor.ToColour() );
-        wxColourDialog* dialog = new wxColourDialog( m_LayerScrolledWindow, &colourData );
+    auto eventSource = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
 
-        if( dialog->ShowModal() == wxID_OK )
-        {
-            newColor = COLOR4D( dialog->GetColourData().GetColour() );
-        }
-    }
-    else
-    {
-        newColor = DisplayColorFrame( this, oldColor );
-    }
+    COLOR4D newColor = eventSource->GetSwatchColor();
 
-    if( newColor != COLOR4D::UNSPECIFIED )
-    {
-        eventSource->SetName( makeColorTxt( newColor ) );
-
-        wxBitmap bm = makeBitmap( newColor.ToColour() );
-        eventSource->SetBitmap( bm );
-
-        LAYER_NUM layer = getDecodedId( eventSource->GetId() );
+    LAYER_NUM layer = getDecodedId( eventSource->GetId() );
 
-        // tell the client code.
-        OnLayerColorChange( layer, newColor );
+    // tell the client code.
+    OnLayerColorChange( layer, newColor );
 
-        // notify others
-        wxCommandEvent event( EVT_LAYER_COLOR_CHANGE );
-        wxPostEvent( this, event );
-    }
+    // notify others
+    wxCommandEvent event( EVT_LAYER_COLOR_CHANGE );
+    wxPostEvent( this, event );
 
     passOnFocus();
 }
@@ -329,44 +246,17 @@ void LAYER_WIDGET::OnLayerCheckBox( wxCommandEvent& event )
 }
 
 
-void LAYER_WIDGET::OnMiddleDownRenderColor( wxMouseEvent& aEvent )
+void LAYER_WIDGET::OnRenderSwatchChanged( wxCommandEvent& aEvent )
 {
-    auto eventSource = static_cast<wxStaticBitmap*>( aEvent.GetEventObject() );
+    auto eventSource = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
 
-    wxString colorTxt = eventSource->GetName();
-
-    COLOR4D oldColor;
-    wxASSERT( oldColor.SetFromWxString( colorTxt ) );
-    COLOR4D newColor = COLOR4D::UNSPECIFIED;
-
-    if( AreArbitraryColorsAllowed() )
-    {
-        wxColourData colourData;
-        colourData.SetColour( oldColor.ToColour() );
-        wxColourDialog *dialog = new wxColourDialog( m_LayerScrolledWindow, &colourData );
+    COLOR4D newColor = eventSource->GetSwatchColor();
 
-        if( dialog->ShowModal() == wxID_OK )
-        {
-            newColor = COLOR4D( dialog->GetColourData().GetColour() );
-        }
-    }
-    else
-    {
-        newColor = DisplayColorFrame( this, oldColor );
-    }
-
-    if( newColor != COLOR4D::UNSPECIFIED )
-    {
-        eventSource->SetName( makeColorTxt( newColor ) );
-
-        wxBitmap bm = makeBitmap( newColor );
-        eventSource->SetBitmap( bm );
+    LAYER_NUM id = getDecodedId( eventSource->GetId() );
 
-        LAYER_NUM id = getDecodedId( eventSource->GetId() );
+    // tell the client code.
+    OnRenderColorChange( id, newColor );
 
-        // tell the client code.
-        OnRenderColorChange( id, newColor );
-    }
     passOnFocus();
 }
 
@@ -458,12 +348,11 @@ void LAYER_WIDGET::insertLayerRow( int aRow, const ROW& aSpec )
     // column 1 (COLUMN_COLORBM)
     col = COLUMN_COLORBM;
 
-    auto bmb = makeColorSwatch( m_LayerScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
-    bmb->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnLeftDownLayers ), NULL, this );
-    bmb->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownLayerColor ), NULL, this );
-    bmb->Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownLayerColor ), NULL, this );
+    auto bmb = new COLOR_SWATCH( m_LayerScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
+    bmb->Bind( wxEVT_LEFT_DOWN, &LAYER_WIDGET::OnLeftDownLayers, this );
+    bmb->Bind( COLOR_SWATCH_CHANGED, &LAYER_WIDGET::OnLayerSwatchChanged, this );
     bmb->SetToolTip( _("Left double click or middle click for color change, right click for menu" ) );
-    m_LayersFlexGridSizer->wxSizer::Insert( index+col, bmb.release(), 0, flags );
+    m_LayersFlexGridSizer->wxSizer::Insert( index+col, bmb, 0, flags );
 
     // column 2 (COLUMN_COLOR_LYR_CB)
     col = COLUMN_COLOR_LYR_CB;
@@ -495,13 +384,10 @@ void LAYER_WIDGET::insertRenderRow( int aRow, const ROW& aSpec )
     col = 0;
     if( aSpec.color != COLOR4D::UNSPECIFIED )
     {
-        auto bmb = makeColorSwatch(m_RenderScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
-
-        //makeColorSwatch( m_RenderScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
-        bmb->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownRenderColor ), NULL, this );
-        bmb->Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownRenderColor ), NULL, this );
+        auto bmb = new COLOR_SWATCH(m_RenderScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
+        bmb->Bind( COLOR_SWATCH_CHANGED, &LAYER_WIDGET::OnRenderSwatchChanged, this );
         bmb->SetToolTip( _( "Left double click or middle click for color change" ) );
-        m_RenderFlexGridSizer->wxSizer::Insert( index+col, bmb.release(), 0, flags );
+        m_RenderFlexGridSizer->wxSizer::Insert( index+col, bmb, 0, flags );
 
         // could add a left click handler on the color button that toggles checkbox.
     }
@@ -803,13 +689,10 @@ void LAYER_WIDGET::SetLayerColor( LAYER_NUM aLayer, COLOR4D aColor )
     if( row >= 0 )
     {
         int col = 1;    // bitmap button is column 1
-        wxBitmapButton* bmb = (wxBitmapButton*) getLayerComp( row, col );
+        auto bmb = static_cast<COLOR_SWATCH*>( getLayerComp( row, col ) );
         wxASSERT( bmb );
 
-        wxBitmap bm = makeBitmap( aColor );
-
-        bmb->SetBitmapLabel( bm );
-        bmb->SetName( makeColorTxt( aColor ) ); // save color value in name as string
+        bmb->SetSwatchColor( aColor, false );
     }
 }
 
diff --git a/pcbnew/layer_widget.h b/pcbnew/layer_widget.h
index 892de638d..635ee18a2 100644
--- a/pcbnew/layer_widget.h
+++ b/pcbnew/layer_widget.h
@@ -157,10 +157,10 @@ protected:
     void OnLeftDownLayers( wxMouseEvent& event );
 
     /**
-     * Function OnMiddleDownLayerColor
-     * is called only from a color button when user right clicks.
+     * Function OnSwatchChanged()
+     * is called when a user changes a swatch color
      */
-    void OnMiddleDownLayerColor( wxMouseEvent& event );
+    void OnLayerSwatchChanged( wxCommandEvent& aEvent );
 
     /**
      * Function OnLayerCheckBox
@@ -169,7 +169,11 @@ protected:
      */
     void OnLayerCheckBox( wxCommandEvent& event );
 
-    void OnMiddleDownRenderColor( wxMouseEvent& event );
+    /**
+     * Function OnRenderSwatchChanged
+     * Called when user has changed the swatch color of a render entry
+     */
+    void OnRenderSwatchChanged( wxCommandEvent& aEvent );
 
     void OnRenderCheckBox( wxCommandEvent& event );
 
-- 
2.11.0

From 1d7553d5f4ed56b753f62d5b4d32368960fcd092 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Tue, 21 Feb 2017 19:09:10 +0800
Subject: [PATCH 1/4] Use wxStaticBitmaps for layer/render swatches

On Linux with recent GTK+ toolkits, these swatches had very wide
borders, resulting in colour swatches of only a few pixels. On OSX, all
borders were disabled, leaving only the swatch.

This commit changes the wxBitmapButton to wxStaticBitmap, which fixes
the issue on Linux GTK+ and also removes the platform-specific element,
as it will now be the same on all systems.

These widgets aren't used as buttons anyway, a single click is handled
across the whole row, not just the swatch. So using a button was not the
most intuitive affordance.

Fixes: lp:1605411
* https://bugs.launchpad.net/kicad/+bug/1605411
---
 pcbnew/layer_widget.cpp | 104 ++++++++++++++++++++++++------------------------
 pcbnew/layer_widget.h   |   8 ----
 2 files changed, 52 insertions(+), 60 deletions(-)

diff --git a/pcbnew/layer_widget.cpp b/pcbnew/layer_widget.cpp
index 5eb11b047..d63c3f914 100644
--- a/pcbnew/layer_widget.cpp
+++ b/pcbnew/layer_widget.cpp
@@ -26,7 +26,7 @@
 
 
 /*  This source module implements the layer visibility and selection widget
-    @todo make the bitmapbutton a staticbitmap, and make dependent on the point size.
+    @todo make bitmap size dependent on the point size.
 */
 
 
@@ -43,9 +43,8 @@
 #include <algorithm>
 
 
-#define BUTT_SIZE_X             20
-#define BUTT_SIZE_Y             18
-#define BUTT_VOID               2
+const static int SWATCH_SIZE_X = 14;
+const static int SWATCH_SIZE_Y = 12;
 
 // See selcolor.cpp:
 extern COLOR4D DisplayColorFrame( wxWindow* aParent, COLOR4D aOldColor );
@@ -161,37 +160,9 @@ static wxString makeColorTxt( COLOR4D aColor )
 }
 
 
-/**
- * Function shrinkFont
- * reduces the size of the wxFont associated with \a aControl
- */
-static void shrinkFont( wxWindow* aControl, int aPointSize )
+static wxBitmap makeBitmap( COLOR4D aColor )
 {
-    wxFont font = aControl->GetFont();
-    font.SetPointSize( aPointSize );
-    aControl->SetFont( font );              // need this?
-}
-
-
-int LAYER_WIDGET::encodeId( int aColumn, int aId )
-{
-    int id = aId * LYR_COLUMN_COUNT + aColumn;
-    return id;
-}
-
-
-LAYER_NUM LAYER_WIDGET::getDecodedId( int aControlId )
-{
-    int id = aControlId / LYR_COLUMN_COUNT;    // rounding is OK.
-    return id;
-}
-
-
-wxBitmap LAYER_WIDGET::makeBitmap( COLOR4D aColor )
-{
-    // the bitmap will be BUTT_VOID*2 pixels smaller than the button, leaving a
-    // border of BUTT_VOID pixels on each side.
-    wxBitmap    bitmap( BUTT_SIZE_X - 2 * BUTT_VOID, BUTT_SIZE_Y - 2 * BUTT_VOID );
+    wxBitmap    bitmap( SWATCH_SIZE_X, SWATCH_SIZE_Y );
     wxBrush     brush;
     wxMemoryDC  iconDC;
 
@@ -202,31 +173,58 @@ wxBitmap LAYER_WIDGET::makeBitmap( COLOR4D aColor )
 
     iconDC.SetBrush( brush );
 
-    iconDC.DrawRectangle( 0, 0, BUTT_SIZE_X - 2 * BUTT_VOID, BUTT_SIZE_Y - 2 * BUTT_VOID );
+    iconDC.DrawRectangle( 0, 0, SWATCH_SIZE_X, SWATCH_SIZE_Y );
 
     return bitmap;
 }
 
 
-wxBitmapButton* LAYER_WIDGET::makeColorButton( wxWindow* aParent, COLOR4D aColor, int aID )
+/**
+ * Function makeColorButton
+ * creates a wxStaticBitmap and assigns it a solid color and a control ID
+ */
+static std::unique_ptr<wxStaticBitmap> makeColorSwatch( wxWindow* aParent,
+                                                        COLOR4D aColor, int aID )
 {
     // dynamically make a wxBitMap and brush it with the appropriate color,
     // then create a wxBitmapButton from it.
     wxBitmap bitmap = makeBitmap( aColor );
 
-#ifdef __WXMAC__
-    wxBitmapButton* ret = new wxBitmapButton( aParent, aID, bitmap,
-        wxDefaultPosition, wxSize(BUTT_SIZE_X, BUTT_SIZE_Y), wxBORDER_NONE );
-#else
-    wxBitmapButton* ret = new wxBitmapButton( aParent, aID, bitmap,
-        wxDefaultPosition, wxSize(BUTT_SIZE_X, BUTT_SIZE_Y), wxBORDER_RAISED );
-#endif
     // save the color value in the name, no where else to put it.
+    auto ret = std::make_unique<wxStaticBitmap>( aParent, aID, bitmap );
+
     ret->SetName( makeColorTxt( aColor ) );
+
     return ret;
 }
 
 
+/**
+ * Function shrinkFont
+ * reduces the size of the wxFont associated with \a aControl
+ */
+static void shrinkFont( wxWindow* aControl, int aPointSize )
+{
+    wxFont font = aControl->GetFont();
+    font.SetPointSize( aPointSize );
+    aControl->SetFont( font );              // need this?
+}
+
+
+int LAYER_WIDGET::encodeId( int aColumn, int aId )
+{
+    int id = aId * LYR_COLUMN_COUNT + aColumn;
+    return id;
+}
+
+
+LAYER_NUM LAYER_WIDGET::getDecodedId( int aControlId )
+{
+    int id = aControlId / LYR_COLUMN_COUNT;    // rounding is OK.
+    return id;
+}
+
+
 void LAYER_WIDGET::OnLeftDownLayers( wxMouseEvent& event )
 {
     int row;
@@ -277,7 +275,7 @@ void LAYER_WIDGET::OnLeftDownLayers( wxMouseEvent& event )
 
 void LAYER_WIDGET::OnMiddleDownLayerColor( wxMouseEvent& aEvent )
 {
-    wxBitmapButton* eventSource = (wxBitmapButton*) aEvent.GetEventObject();
+    auto eventSource = static_cast<wxStaticBitmap*>( aEvent.GetEventObject() );
 
     wxString colorTxt = eventSource->GetName();
 
@@ -306,7 +304,7 @@ void LAYER_WIDGET::OnMiddleDownLayerColor( wxMouseEvent& aEvent )
         eventSource->SetName( makeColorTxt( newColor ) );
 
         wxBitmap bm = makeBitmap( newColor.ToColour() );
-        eventSource->SetBitmapLabel( bm );
+        eventSource->SetBitmap( bm );
 
         LAYER_NUM layer = getDecodedId( eventSource->GetId() );
 
@@ -331,9 +329,9 @@ void LAYER_WIDGET::OnLayerCheckBox( wxCommandEvent& event )
 }
 
 
-void LAYER_WIDGET::OnMiddleDownRenderColor( wxMouseEvent& event )
+void LAYER_WIDGET::OnMiddleDownRenderColor( wxMouseEvent& aEvent )
 {
-    wxBitmapButton* eventSource = (wxBitmapButton*) event.GetEventObject();
+    auto eventSource = static_cast<wxStaticBitmap*>( aEvent.GetEventObject() );
 
     wxString colorTxt = eventSource->GetName();
 
@@ -362,7 +360,7 @@ void LAYER_WIDGET::OnMiddleDownRenderColor( wxMouseEvent& event )
         eventSource->SetName( makeColorTxt( newColor ) );
 
         wxBitmap bm = makeBitmap( newColor );
-        eventSource->SetBitmapLabel( bm );
+        eventSource->SetBitmap( bm );
 
         LAYER_NUM id = getDecodedId( eventSource->GetId() );
 
@@ -460,12 +458,12 @@ void LAYER_WIDGET::insertLayerRow( int aRow, const ROW& aSpec )
     // column 1 (COLUMN_COLORBM)
     col = COLUMN_COLORBM;
 
-    wxBitmapButton* bmb = makeColorButton( m_LayerScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
+    auto bmb = makeColorSwatch( m_LayerScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
     bmb->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnLeftDownLayers ), NULL, this );
     bmb->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownLayerColor ), NULL, this );
     bmb->Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownLayerColor ), NULL, this );
     bmb->SetToolTip( _("Left double click or middle click for color change, right click for menu" ) );
-    m_LayersFlexGridSizer->wxSizer::Insert( index+col, bmb, 0, flags );
+    m_LayersFlexGridSizer->wxSizer::Insert( index+col, bmb.release(), 0, flags );
 
     // column 2 (COLUMN_COLOR_LYR_CB)
     col = COLUMN_COLOR_LYR_CB;
@@ -497,11 +495,13 @@ void LAYER_WIDGET::insertRenderRow( int aRow, const ROW& aSpec )
     col = 0;
     if( aSpec.color != COLOR4D::UNSPECIFIED )
     {
-        wxBitmapButton* bmb = makeColorButton( m_RenderScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
+        auto bmb = makeColorSwatch(m_RenderScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
+
+        //makeColorSwatch( m_RenderScrolledWindow, aSpec.color, encodeId( col, aSpec.id ) );
         bmb->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownRenderColor ), NULL, this );
         bmb->Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( LAYER_WIDGET::OnMiddleDownRenderColor ), NULL, this );
         bmb->SetToolTip( _( "Left double click or middle click for color change" ) );
-        m_RenderFlexGridSizer->wxSizer::Insert( index+col, bmb, 0, flags );
+        m_RenderFlexGridSizer->wxSizer::Insert( index+col, bmb.release(), 0, flags );
 
         // could add a left click handler on the color button that toggles checkbox.
     }
diff --git a/pcbnew/layer_widget.h b/pcbnew/layer_widget.h
index 23624e340..892de638d 100644
--- a/pcbnew/layer_widget.h
+++ b/pcbnew/layer_widget.h
@@ -121,8 +121,6 @@ protected:
     int                 m_CurrentRow;           ///< selected row of layer list
     int                 m_PointSize;
 
-    static wxBitmap makeBitmap( COLOR4D aColor );
-
     /**
      * Virtual Function useAlternateBitmap
      * @return true if bitmaps shown in Render layer list
@@ -156,12 +154,6 @@ protected:
      */
     static LAYER_NUM getDecodedId( int aControlId );
 
-    /**
-     * Function makeColorButton
-     * creates a wxBitmapButton and assigns it a solid color and a control ID
-     */
-    wxBitmapButton* makeColorButton( wxWindow* aParent, COLOR4D aColor, int aID );
-
     void OnLeftDownLayers( wxMouseEvent& event );
 
     /**
-- 
2.11.0


Follow ups

References