← Back to team overview

kicad-developers team mailing list archive

[PATCH] GerbView GAL support

 

Hi all,

The day has finally come!  I have distilled my GerbView GAL branch into a
patchset attached to this email.  Hopefully with this merged into master we
can identify any remaining bugs and clean it up for 5.0.

Note that this set is split into 5 patches to make review easier, but they
are not intended to compile and work independently.

Best,
Jon
From 8a93c2d1ac32dcf4cc49f55ce49945d2317f4154 Mon Sep 17 00:00:00 2001
From: Jon Evans <jon@xxxxxxxxxxxxx>
Date: Wed, 13 Sep 2017 20:07:29 -0400
Subject: [PATCH] Support scrollbar controls in GAL canvas

---
 common/view/wx_view_controls.cpp | 59 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 7 deletions(-)

diff --git a/common/view/wx_view_controls.cpp b/common/view/wx_view_controls.cpp
index 9f9dcdc8b..d403e6581 100644
--- a/common/view/wx_view_controls.cpp
+++ b/common/view/wx_view_controls.cpp
@@ -63,6 +63,14 @@ WX_VIEW_CONTROLS::WX_VIEW_CONTROLS( VIEW* aView, wxScrolledCanvas* aParentPanel
                             wxMouseEventHandler( WX_VIEW_CONTROLS::onLeave ), NULL, this );
     m_parentPanel->Connect( wxEVT_SCROLLWIN_THUMBTRACK,
                             wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
+    m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEUP,
+                            wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
+    m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEDOWN,
+                            wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
+    m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEUP,
+                            wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
+    m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEDOWN,
+                            wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
 
     m_panTimer.SetOwner( this );
     this->Connect( wxEVT_TIMER,
@@ -335,15 +343,52 @@ void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent )
 
 void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
 {
-    VECTOR2D center = m_view->GetCenter();
-    const BOX2I& boundary = m_view->GetBoundary();
+    const double linePanDelta = 0.05;
+    const double pagePanDelta = 0.5;
+
+    int type = aEvent.GetEventType();
+    int dir = aEvent.GetOrientation();
 
-    if( aEvent.GetOrientation() == wxHORIZONTAL )
-        center.x = boundary.GetLeft() + aEvent.GetPosition() / m_scrollScale.x;
-    else if( aEvent.GetOrientation() == wxVERTICAL )
-        center.y = boundary.GetTop() + aEvent.GetPosition() / m_scrollScale.y;
+    if( type == wxEVT_SCROLLWIN_THUMBTRACK )
+    {
+        VECTOR2D center = m_view->GetCenter();
+        const BOX2I& boundary = m_view->GetBoundary();
+
+        if( dir == wxHORIZONTAL )
+            center.x = boundary.GetLeft() + aEvent.GetPosition() / m_scrollScale.x;
+        else
+            center.y = boundary.GetTop() + aEvent.GetPosition() / m_scrollScale.y;
+
+        m_view->SetCenter( center );
+    }
+    else
+    {
+        double dist = 0;
+
+        if( type == wxEVT_SCROLLWIN_PAGEUP )
+            dist = pagePanDelta;
+        else if( type == wxEVT_SCROLLWIN_PAGEDOWN )
+            dist = -pagePanDelta;
+        else if( type == wxEVT_SCROLLWIN_LINEUP )
+            dist = linePanDelta;
+        else if( type == wxEVT_SCROLLWIN_LINEDOWN )
+            dist = -linePanDelta;
+
+        VECTOR2D scroll = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) * dist;
+
+        double scrollX = 0.0;
+        double scrollY = 0.0;
+
+        if ( dir == wxHORIZONTAL )
+            scrollX = -scroll.x;
+        else
+            scrollY = -scroll.y;
+
+        VECTOR2D delta( scrollX, scrollY );
+
+        m_view->SetCenter( m_view->GetCenter() + delta );
+    }
 
-    m_view->SetCenter( center );
     m_parentPanel->Refresh();
 }
 
-- 
2.11.0

From be3060e2982c20a24b6ba573dfd3308d07d01b19 Mon Sep 17 00:00:00 2001
From: Jon Evans <jon@xxxxxxxxxxxxx>
Date: Sun, 17 Sep 2017 18:36:54 -0400
Subject: [PATCH 2/5] Move zoom_tool.h to include

---
 {common => include}/tool/zoom_tool.h | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename {common => include}/tool/zoom_tool.h (100%)

diff --git a/common/tool/zoom_tool.h b/include/tool/zoom_tool.h
similarity index 100%
rename from common/tool/zoom_tool.h
rename to include/tool/zoom_tool.h
-- 
2.11.0

From f673beed34b6cdbf2d4941adbc99a693b5aea7b4 Mon Sep 17 00:00:00 2001
From: Jon Evans <jon@xxxxxxxxxxxxx>
Date: Sun, 17 Sep 2017 18:38:18 -0400
Subject: [PATCH 3/5] Move forceColorsToLegacy() to EDA_DRAW_FRAME as virtual
 method

---
 include/draw_frame.h  | 6 ++++++
 include/wxPcbStruct.h | 7 ++-----
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/include/draw_frame.h b/include/draw_frame.h
index aa01690d0..f6eb1e120 100644
--- a/include/draw_frame.h
+++ b/include/draw_frame.h
@@ -149,6 +149,12 @@ protected:
     virtual void unitsChangeRefresh();
 
     /**
+     * Helper function to coerce all colors to legacy-compatible when
+     * switching from GAL to legacy canvas
+     */
+    virtual void forceColorsToLegacy() {}
+
+    /**
      * Function GeneralControlKeyMovement
      * Handle the common part of GeneralControl dedicated to global
      * cursor keys (i.e. cursor movement by keyboard) */
diff --git a/include/wxPcbStruct.h b/include/wxPcbStruct.h
index 84788ef51..c5c7b5717 100644
--- a/include/wxPcbStruct.h
+++ b/include/wxPcbStruct.h
@@ -123,11 +123,8 @@ protected:
      */
     virtual void SwitchCanvas( wxCommandEvent& aEvent ) override;
 
-    /**
-     * Helper function to coerce all colors to legacy-compatible when
-     * switching from GAL to legacy canvas
-     */
-    void forceColorsToLegacy();
+    ///> @copydoc EDA_DRAW_FRAME::forceColorsToLegacy()
+    virtual void forceColorsToLegacy() override;
 
 #if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
     /**
-- 
2.11.0

From 8a97c1993a29b03ccbd43f3d0acb8e64685e8c26 Mon Sep 17 00:00:00 2001
From: Jon Evans <jon@xxxxxxxxxxxxx>
Date: Sun, 17 Sep 2017 18:43:20 -0400
Subject: [PATCH 4/5] GerbView GAL support part 1: changes to existing files

- New GAL draw layers for GerbView
- Improved bounding boxes for Gerber shapes
- Switched to use of SHAPE_POLY_SET for polygons
- Add GAL methods to support selection and rendering
- Add GUI support of editing GAL options
- Rename get/setActiveLayer to Get/SetActiveLayer to match convention
---
 gerbview/class_aperture_macro.cpp                  |  58 +++--
 gerbview/class_aperture_macro.h                    |  20 ++
 gerbview/class_gbr_display_options.h               |   4 +
 gerbview/class_gbr_layer_box_selector.cpp          |   2 +-
 gerbview/class_gbr_layout.cpp                      |  67 +++++-
 gerbview/class_gbr_layout.h                        |  27 ++-
 gerbview/class_gerber_draw_item.cpp                | 252 ++++++++++++++++++---
 gerbview/class_gerber_draw_item.h                  |  41 +++-
 gerbview/class_gerber_file_image.cpp               |  59 ++++-
 gerbview/class_gerber_file_image.h                 |  30 ++-
 gerbview/class_gerber_file_image_list.cpp          |   4 +-
 gerbview/class_gerber_file_image_list.h            |  13 +-
 gerbview/class_gerbview_layer_widget.cpp           |  85 ++++++-
 gerbview/class_gerbview_layer_widget.h             |   2 +
 gerbview/clear_gbr_drawlayers.cpp                  |  10 +-
 gerbview/dcode.cpp                                 | 121 +++++-----
 gerbview/dcode.h                                   |  11 +-
 .../gerbview_dialog_display_options_frame.cpp      |  52 +++--
 .../gerbview_dialog_display_options_frame_base.cpp |  45 ++--
 .../gerbview_dialog_display_options_frame_base.fbp | 129 ++---------
 .../gerbview_dialog_display_options_frame_base.h   |   8 +-
 gerbview/draw_gerber_screen.cpp                    |  11 +-
 gerbview/events_called_functions.cpp               | 154 +++++++++++--
 gerbview/excellon_read_drill_file.cpp              |  26 ++-
 gerbview/files.cpp                                 |  22 +-
 gerbview/gerbview_config.cpp                       |   7 +-
 gerbview/gerbview_frame.cpp                        | 252 +++++++++++++++++++--
 gerbview/gerbview_frame.h                          |  62 ++++-
 gerbview/gerbview_id.h                             |   6 +
 gerbview/hotkeys.cpp                               |  47 +++-
 gerbview/hotkeys.h                                 |   5 +-
 gerbview/job_file_reader.cpp                       |   4 +-
 gerbview/locate.cpp                                |   2 +-
 gerbview/menubar.cpp                               |  28 +++
 gerbview/onleftclick.cpp                           |   4 +-
 gerbview/onrightclick.cpp                          |   2 +-
 gerbview/readgerb.cpp                              |  20 +-
 gerbview/rs274d.cpp                                |  27 ++-
 gerbview/rs274x.cpp                                |   2 +-
 gerbview/toolbars_gerber.cpp                       |  82 ++++---
 include/core/typeinfo.h                            |   5 +-
 include/layers_id_colors_and_visibility.h          |  18 +-
 42 files changed, 1387 insertions(+), 439 deletions(-)

diff --git a/gerbview/class_aperture_macro.cpp b/gerbview/class_aperture_macro.cpp
index 866e00b20..9896ff1b5 100644
--- a/gerbview/class_aperture_macro.cpp
+++ b/gerbview/class_aperture_macro.cpp
@@ -100,7 +100,8 @@ void AM_PRIMITIVE::DrawBasicShape( GERBER_DRAW_ITEM* aParent,
 {
     #define TO_POLY_SHAPE { aShapeBuffer.NewOutline(); \
                             for( unsigned jj = 0; jj < polybuffer.size(); jj++ )\
-                                aShapeBuffer.Append( polybuffer[jj].x, polybuffer[jj].y );}
+                                aShapeBuffer.Append( polybuffer[jj].x, polybuffer[jj].y );\
+                            aShapeBuffer.Append( polybuffer[0].x, polybuffer[0].y );}
 
     // Draw the primitive shape for flashed items.
     static std::vector<wxPoint> polybuffer;     // create a static buffer to avoid a lot of memory reallocation
@@ -746,49 +747,68 @@ int AM_PRIMITIVE::GetShapeDim( GERBER_DRAW_ITEM* aParent )
 }
 
 
-/*
- * Function DrawApertureMacroShape
- * Draw the primitive shape for flashed items.
- * When an item is flashed, this is the shape of the item
- */
-void APERTURE_MACRO::DrawApertureMacroShape( GERBER_DRAW_ITEM* aParent,
-                                             EDA_RECT* aClipBox, wxDC* aDC,
-                                             COLOR4D aColor,
-                                             wxPoint aShapePos, bool aFilledShape )
+SHAPE_POLY_SET* APERTURE_MACRO::GetApertureMacroShape( GERBER_DRAW_ITEM* aParent,
+                                                       wxPoint aShapePos )
 {
-    SHAPE_POLY_SET shapeBuffer;
     SHAPE_POLY_SET holeBuffer;
     bool hasHole = false;
 
+    m_shape.RemoveAllContours();
+
     for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
          prim_macro != primitives.end(); ++prim_macro )
     {
+        if( prim_macro->primitive_id == AMP_COMMENT )
+            continue;
+
         if( prim_macro->IsAMPrimitiveExposureOn( aParent ) )
-            prim_macro->DrawBasicShape( aParent, shapeBuffer, aShapePos );
+            prim_macro->DrawBasicShape( aParent, m_shape, aShapePos );
         else
         {
             prim_macro->DrawBasicShape( aParent, holeBuffer, aShapePos );
 
             if( holeBuffer.OutlineCount() )     // we have a new hole in shape: remove the hole
             {
-                shapeBuffer.BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST );
+                m_shape.BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST );
                 holeBuffer.RemoveAllContours();
                 hasHole = true;
             }
         }
     }
 
-    if( shapeBuffer.OutlineCount() == 0 )
-        return;
-
     // If a hole is defined inside a polygon, we must fracture the polygon
     // to be able to drawn it (i.e link holes by overlapping edges)
     if( hasHole )
-        shapeBuffer.Fracture( SHAPE_POLY_SET::PM_FAST );
+        m_shape.Fracture( SHAPE_POLY_SET::PM_FAST );
+
+    m_boundingBox = EDA_RECT( wxPoint( 0, 0 ), wxSize( 1, 1 ) );
+    auto bb = m_shape.BBox();
+    wxPoint center( bb.Centre().x, bb.Centre().y );
+    m_boundingBox.Move( aParent->GetABPosition( center ) );
+    m_boundingBox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
+
+    return &m_shape;
+}
+
+
+/*
+ * Function DrawApertureMacroShape
+ * Draw the primitive shape for flashed items.
+ * When an item is flashed, this is the shape of the item
+ */
+void APERTURE_MACRO::DrawApertureMacroShape( GERBER_DRAW_ITEM* aParent,
+                                             EDA_RECT* aClipBox, wxDC* aDC,
+                                             COLOR4D aColor,
+                                             wxPoint aShapePos, bool aFilledShape )
+{
+    SHAPE_POLY_SET* shapeBuffer = GetApertureMacroShape( aParent, aShapePos );
+
+    if( shapeBuffer->OutlineCount() == 0 )
+        return;
 
-    for( int ii = 0; ii < shapeBuffer.OutlineCount(); ii++ )
+    for( int ii = 0; ii < shapeBuffer->OutlineCount(); ii++ )
     {
-        SHAPE_LINE_CHAIN& poly = shapeBuffer.Outline( ii );
+        SHAPE_LINE_CHAIN& poly = shapeBuffer->Outline( ii );
 
         GRClosedPoly( aClipBox, aDC,
                       poly.PointCount(), (wxPoint*)&poly.Point( 0 ), aFilledShape, aColor, aColor );
diff --git a/gerbview/class_aperture_macro.h b/gerbview/class_aperture_macro.h
index dc94ef259..c6579828e 100644
--- a/gerbview/class_aperture_macro.h
+++ b/gerbview/class_aperture_macro.h
@@ -35,6 +35,7 @@
 
 #include <base_struct.h>
 #include <class_am_param.h>
+#include <class_eda_rect.h>
 
 class SHAPE_POLY_SET;
 
@@ -170,6 +171,9 @@ struct APERTURE_MACRO
      */
     AM_PARAMS m_localparamStack;
 
+    SHAPE_POLY_SET m_shape;     ///< The shape of the item, calculated by GetApertureMacroShape
+    EDA_RECT m_boundingBox;     ///< The bounding box of the item, calculated by GetApertureMacroShape
+
     /**
      * function GetLocalParam
      * Usually, parameters are defined inside the aperture primitive
@@ -183,6 +187,16 @@ struct APERTURE_MACRO
      */
     double GetLocalParam( const D_CODE* aDcode, unsigned aParamId ) const;
 
+
+    /**
+     * Function GetApertureMacroShape
+     * Calculate the primitive shape for flashed items.
+     * When an item is flashed, this is the shape of the item
+     * @param aParent = the parent GERBER_DRAW_ITEM which is actually drawn
+     * @return The shape of the item
+     */
+    SHAPE_POLY_SET* GetApertureMacroShape( GERBER_DRAW_ITEM* aParent, wxPoint aShapePos );
+
    /**
      * Function DrawApertureMacroShape
      * Draw the primitive shape for flashed items.
@@ -210,6 +224,12 @@ struct APERTURE_MACRO
      * @return a dimension, or -1 if no dim to calculate
      */
     int  GetShapeDim( GERBER_DRAW_ITEM* aParent );
+
+    /// Returns the bounding box of the shape
+    EDA_RECT GetBoundingBox() const
+    {
+        return m_boundingBox;
+    }
 };
 
 
diff --git a/gerbview/class_gbr_display_options.h b/gerbview/class_gbr_display_options.h
index e4b17d44b..43834594a 100644
--- a/gerbview/class_gbr_display_options.h
+++ b/gerbview/class_gbr_display_options.h
@@ -47,6 +47,8 @@ public:
     bool    m_DisplayNegativeObjects;   ///< Option to draw negative objects in a specific color
     bool    m_IsPrinting;               ///< true when printing a page, false when drawing on screen
     bool    m_ForceBlackAndWhite;       ///< Option print in blackand white (ont used id draw mode
+    bool    m_DiffMode;                 ///< Display layers in diff mode
+    bool    m_HighContrastMode;         ///< High contrast mode (dim un-highlighted objects)
     COLOR4D m_NegativeDrawColor;        ///< The color used to draw negative objects, usually the
                                         ///< background color, but not always, when negative objects
                                         ///< must be visible
@@ -65,6 +67,8 @@ public:
         m_ForceBlackAndWhite    = false;
         m_NegativeDrawColor     = COLOR4D( DARKGRAY );
         m_BgDrawColor = COLOR4D::BLACK;
+        m_DiffMode = true;
+        m_HighContrastMode = false;
     }
 };
 
diff --git a/gerbview/class_gbr_layer_box_selector.cpp b/gerbview/class_gbr_layer_box_selector.cpp
index 7ae5b7aaa..b7dfd2227 100644
--- a/gerbview/class_gbr_layer_box_selector.cpp
+++ b/gerbview/class_gbr_layer_box_selector.cpp
@@ -73,7 +73,7 @@ COLOR4D GBR_LAYER_BOX_SELECTOR::GetLayerColor( int aLayer ) const
 {
     GERBVIEW_FRAME* frame = (GERBVIEW_FRAME*) GetParent()->GetParent();
 
-    return frame->GetLayerColor( aLayer );
+    return frame->GetLayerColor( GERBER_DRAW_LAYER( aLayer ) );
 }
 
 
diff --git a/gerbview/class_gbr_layout.cpp b/gerbview/class_gbr_layout.cpp
index ebba33f4c..dc1f20355 100644
--- a/gerbview/class_gbr_layout.cpp
+++ b/gerbview/class_gbr_layout.cpp
@@ -36,7 +36,8 @@
 #include <class_gerber_file_image.h>
 #include <class_gerber_file_image_list.h>
 
-GBR_LAYOUT::GBR_LAYOUT()
+GBR_LAYOUT::GBR_LAYOUT() :
+    EDA_ITEM( (EDA_ITEM*)NULL, GERBER_LAYOUT_T )
 {
 }
 
@@ -46,7 +47,7 @@ GBR_LAYOUT::~GBR_LAYOUT()
 }
 
 // Accessor to the list of gerber files (and drill files) images
-GERBER_FILE_IMAGE_LIST* GBR_LAYOUT::GetImagesList()
+GERBER_FILE_IMAGE_LIST* GBR_LAYOUT::GetImagesList() const
 {
     return &GERBER_FILE_IMAGE_LIST::GetImagesList();
 }
@@ -64,7 +65,7 @@ bool GBR_LAYOUT::IsLayerPrintable( int aLayer ) const
 }
 
 
-EDA_RECT GBR_LAYOUT::ComputeBoundingBox()
+EDA_RECT GBR_LAYOUT::ComputeBoundingBox() const
 {
     EDA_RECT bbox;
     bool first_item = true;
@@ -88,7 +89,10 @@ EDA_RECT GBR_LAYOUT::ComputeBoundingBox()
         }
     }
 
-    SetBoundingBox( bbox );
+    bbox.Inflate( ( bbox.GetWidth() / 10 ) + 100 );
+    bbox.Normalize();
+
+    m_BoundingBox = bbox;
     return bbox;
 }
 
@@ -185,7 +189,7 @@ void GBR_LAYOUT::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode,
     // In non transparent modes, the last layer drawn masks others layers
     for( int layer = GERBER_DRAWLAYERS_COUNT-1; !end; --layer )
     {
-        int active_layer = gerbFrame->getActiveLayer();
+        int active_layer = gerbFrame->GetActiveLayer();
 
         if( layer == active_layer ) // active layer will be drawn after other layers
             continue;
@@ -204,12 +208,12 @@ void GBR_LAYOUT::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode,
         if( aDisplayOptions->m_IsPrinting )
             gerber->m_IsVisible = IsLayerPrintable( layer );
         else
-            gerber->m_IsVisible = gerbFrame->IsLayerVisible( layer );
+            gerber->m_IsVisible = gerbFrame->IsLayerVisible( GERBER_DRAW_LAYER( layer ) );
 
         if( !gerber->m_IsVisible )
             continue;
 
-        gerber->m_PositiveDrawColor = gerbFrame->GetLayerColor( layer );
+        gerber->m_PositiveDrawColor = gerbFrame->GetLayerColor( GERBER_DRAW_LAYER( layer ) );
 
        // Force black and white draw mode on request:
         if( aDisplayOptions->m_ForceBlackAndWhite )
@@ -277,7 +281,7 @@ void GBR_LAYOUT::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode,
 
         int dcode_highlight = 0;
 
-        if( layer == gerbFrame->getActiveLayer() )
+        if( layer == gerbFrame->GetActiveLayer() )
             dcode_highlight = gerber->m_Selected_Tool;
 
         GR_DRAWMODE layerdrawMode = GR_COPY;
@@ -433,3 +437,50 @@ void GBR_LAYOUT::DrawItemsDCodeID( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
         }
     }
 }
+
+
+SEARCH_RESULT GBR_LAYOUT::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
+{
+    KICAD_T        stype;
+    SEARCH_RESULT  result = SEARCH_CONTINUE;
+    const KICAD_T* p    = scanTypes;
+    bool           done = false;
+
+#if 0 && defined(DEBUG)
+    std::cout << GetClass().mb_str() << ' ';
+#endif
+
+    while( !done )
+    {
+        stype = *p;
+
+        switch( stype )
+        {
+        case GERBER_IMAGE_LIST_T:
+            for( unsigned layer = 0; layer < GetImagesList()->ImagesMaxCount(); ++layer )
+            {
+                GERBER_FILE_IMAGE* gerber = GetImagesList()->GetGbrImage( layer );
+
+                if( gerber == NULL )    // Graphic layer not yet used
+                    continue;
+
+                result = gerber->Visit( inspector, testData, p );
+
+                if( result == SEARCH_QUIT )
+                    break;
+            }
+
+            ++p;
+            break;
+
+        default:        // catch EOT or ANY OTHER type here and return.
+            done = true;
+            break;
+        }
+
+        if( result == SEARCH_QUIT )
+            break;
+    }
+
+    return result;
+}
diff --git a/gerbview/class_gbr_layout.h b/gerbview/class_gbr_layout.h
index 8f0ae598f..c68bc2e7f 100644
--- a/gerbview/class_gbr_layout.h
+++ b/gerbview/class_gbr_layout.h
@@ -50,10 +50,10 @@ class GERBER_FILE_IMAGE_LIST;
  * Class GBR_LAYOUT
  * holds list of GERBER_DRAW_ITEM currently loaded.
  */
-class GBR_LAYOUT
+class GBR_LAYOUT : public EDA_ITEM
 {
 private:
-    EDA_RECT            m_BoundingBox;
+    mutable EDA_RECT    m_BoundingBox;
     TITLE_BLOCK         m_titles;
     wxPoint             m_originAxisPosition;
     std::vector<int>    m_printLayersList;  // When printing: the list of graphic layers Id to print
@@ -63,9 +63,14 @@ public:
     GBR_LAYOUT();
     ~GBR_LAYOUT();
 
+    wxString GetClass() const override
+    {
+        return wxT( "GBR_LAYOUT" );
+    }
+
     // Accessor to the GERBER_FILE_IMAGE_LIST,
     // which handles the list of gerber files (and drill files) images loaded
-    GERBER_FILE_IMAGE_LIST* GetImagesList();
+    GERBER_FILE_IMAGE_LIST* GetImagesList() const;
 
     const wxPoint&      GetAuxOrigin() const
     {
@@ -92,14 +97,15 @@ public:
      * calculates the bounding box containing all Gerber items.
      * @return EDA_RECT - the full item list bounding box
      */
-    EDA_RECT ComputeBoundingBox();
+    EDA_RECT ComputeBoundingBox() const;
 
     /**
      * Function GetBoundingBox
-     * may be called soon after ComputeBoundingBox() to return the same EDA_RECT,
-     * as long as the CLASS_GBR_LAYOUT has not changed.
      */
-    EDA_RECT GetBoundingBox() const { return m_BoundingBox; }
+    const EDA_RECT GetBoundingBox() const override
+    {
+        return ComputeBoundingBox();
+    }
 
     void SetBoundingBox( const EDA_RECT& aBox ) { m_BoundingBox = aBox; }
 
@@ -176,8 +182,13 @@ public:
      */
     bool    IsLayerPrintable( int aLayer ) const;
 
+    ///> @copydoc EDA_ITEM::Visit()
+    SEARCH_RESULT Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] ) override;
+
+
 #if defined(DEBUG)
-    void    Show( int nestLevel, std::ostream& os ) const;
+
+    void    Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
 
 #endif
 };
diff --git a/gerbview/class_gerber_draw_item.cpp b/gerbview/class_gerber_draw_item.cpp
index 6cea3a714..0a9ab84d3 100644
--- a/gerbview/class_gerber_draw_item.cpp
+++ b/gerbview/class_gerber_draw_item.cpp
@@ -33,6 +33,7 @@
 #include <class_drawpanel.h>
 #include <msgpanel.h>
 #include <gerbview_frame.h>
+#include <convert_basic_shapes_to_polygon.h>
 
 #include <class_gerber_draw_item.h>
 #include <class_gerber_file_image.h>
@@ -40,7 +41,7 @@
 
 
 GERBER_DRAW_ITEM::GERBER_DRAW_ITEM( GERBER_FILE_IMAGE* aGerberImageFile ) :
-    EDA_ITEM( (EDA_ITEM*)NULL, TYPE_GERBER_DRAW_ITEM )
+    EDA_ITEM( (EDA_ITEM*)NULL, GERBER_DRAW_ITEM_T )
 {
     m_GerberImageFile = aGerberImageFile;
     m_Shape         = GBR_SEGMENT;
@@ -158,7 +159,7 @@ void GERBER_DRAW_ITEM::SetLayerParameters()
 }
 
 
-wxString GERBER_DRAW_ITEM::ShowGBRShape()
+wxString GERBER_DRAW_ITEM::ShowGBRShape() const
 {
     switch( m_Shape )
     {
@@ -203,7 +204,7 @@ wxString GERBER_DRAW_ITEM::ShowGBRShape()
 }
 
 
-D_CODE* GERBER_DRAW_ITEM::GetDcodeDescr()
+D_CODE* GERBER_DRAW_ITEM::GetDcodeDescr() const
 {
     if( (m_DCode < FIRST_DCODE) || (m_DCode > LAST_DCODE) )
         return NULL;
@@ -211,7 +212,7 @@ D_CODE* GERBER_DRAW_ITEM::GetDcodeDescr()
     if( m_GerberImageFile == NULL )
         return NULL;
 
-    return m_GerberImageFile->GetDCODE( m_DCode, false );
+    return m_GerberImageFile->GetDCODE( m_DCode );
 }
 
 
@@ -219,8 +220,100 @@ const EDA_RECT GERBER_DRAW_ITEM::GetBoundingBox() const
 {
     // return a rectangle which is (pos,dim) in nature.  therefore the +1
     EDA_RECT bbox( m_Start, wxSize( 1, 1 ) );
+    D_CODE* code = GetDcodeDescr();
 
-    bbox.Inflate( m_Size.x / 2, m_Size.y / 2 );
+    // TODO(JE) GERBER_DRAW_ITEM maybe should actually be a number of subclasses.
+    // Until/unless that is changed, we need to do different things depending on
+    // what is actually being represented by this GERBER_DRAW_ITEM.
+
+    switch( m_Shape )
+    {
+    case GBR_POLYGON:
+    {
+        auto bb = m_Polygon.BBox();
+        bbox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
+        bbox.SetOrigin( bb.GetOrigin().x, bb.GetOrigin().y );
+        break;
+    }
+
+    case GBR_CIRCLE:
+    {
+        double radius = GetLineLength( m_Start, m_End );
+        bbox.Inflate( radius, radius );
+        break;
+    }
+
+    case GBR_ARC:
+    {
+        // Note: using a larger-than-necessary BB to simplify computation
+        double radius = GetLineLength( m_Start, m_ArcCentre );
+        bbox.Inflate( radius, radius );
+        break;
+    }
+
+    case GBR_SPOT_CIRCLE:
+    {
+        int radius = code->m_Size.x >> 1;
+        bbox.Inflate( radius, radius );
+        break;
+    }
+
+    case GBR_SPOT_RECT:
+    {
+        bbox.Inflate( code->m_Size.x / 2, code->m_Size.y / 2 );
+        break;
+    }
+
+    case GBR_SPOT_OVAL:
+    {
+        bbox.Inflate( code->m_Size.x, code->m_Size.y );
+        break;
+    }
+
+    case GBR_SPOT_POLY:
+    {
+        if( code->m_Polygon.OutlineCount() == 0 )
+            code->ConvertShapeToPolygon();
+
+        bbox.Inflate( code->m_Polygon.BBox().GetWidth() / 2, code->m_Polygon.BBox().GetHeight() / 2 );
+        break;
+    }
+    case GBR_SPOT_MACRO:
+    {
+        bbox = code->GetMacro()->GetBoundingBox();
+        break;
+    }
+
+    case GBR_SEGMENT:
+    {
+        if( code && code->m_Shape == APT_RECT )
+        {
+            if( m_Polygon.OutlineCount() > 0 )
+            {
+                auto bb = m_Polygon.BBox();
+                bbox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
+                bbox.SetOrigin( bb.GetOrigin().x, bb.GetOrigin().y );
+            }
+        }
+        else
+        {
+            int radius = ( m_Size.x + 1 ) / 2;
+
+            int ymax = std::max( m_Start.y, m_End.y ) + radius;
+            int xmax = std::max( m_Start.x, m_End.x ) + radius;
+
+            int ymin = std::min( m_Start.y, m_End.y ) - radius;
+            int xmin = std::min( m_Start.x, m_End.x ) - radius;
+
+            bbox = EDA_RECT( wxPoint( xmin, ymin ), wxSize( xmax - xmin + 1, ymax - ymin + 1 ) );
+        }
+
+        break;
+    }
+    default:
+        wxASSERT_MSG( false, wxT( "GERBER_DRAW_ITEM shape is unknown!" ) );
+        break;
+    }
 
     // calculate the corners coordinates in current gerber axis orientations
     wxPoint org = GetABPosition( bbox.GetOrigin() );
@@ -243,8 +336,11 @@ void GERBER_DRAW_ITEM::MoveAB( const wxPoint& aMoveVector )
     m_End       += xymove;
     m_ArcCentre += xymove;
 
-    for( unsigned ii = 0; ii < m_PolyCorners.size(); ii++ )
-        m_PolyCorners[ii] += xymove;
+    if( m_Polygon.OutlineCount() > 0 )
+    {
+        for( auto it = m_Polygon.Iterate( 0 ); it; ++it )
+            *it += xymove;
+    }
 }
 
 
@@ -254,8 +350,11 @@ void GERBER_DRAW_ITEM::MoveXY( const wxPoint& aMoveVector )
     m_End       += aMoveVector;
     m_ArcCentre += aMoveVector;
 
-    for( unsigned ii = 0; ii < m_PolyCorners.size(); ii++ )
-        m_PolyCorners[ii] += aMoveVector;
+    if( m_Polygon.OutlineCount() > 0 )
+    {
+        for( auto it = m_Polygon.Iterate( 0 ); it; ++it )
+            *it += aMoveVector;
+    }
 }
 
 
@@ -378,8 +477,8 @@ void GERBER_DRAW_ITEM::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDra
          */
         if( d_codeDescr->m_Shape == APT_RECT )
         {
-            if( m_PolyCorners.size() == 0 )
-                ConvertSegmentToPolygon( );
+            if( m_Polygon.OutlineCount() == 0 )
+                ConvertSegmentToPolygon();
 
             DrawGbrPoly( aPanel->GetClipBox(), aDC, color, aOffset, isFilled );
         }
@@ -411,10 +510,10 @@ void GERBER_DRAW_ITEM::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDra
 }
 
 
-void GERBER_DRAW_ITEM::ConvertSegmentToPolygon( )
+void GERBER_DRAW_ITEM::ConvertSegmentToPolygon()
 {
-    m_PolyCorners.clear();
-    m_PolyCorners.reserve(6);
+    m_Polygon.RemoveAllContours();
+    m_Polygon.NewOutline();
 
     wxPoint start = m_Start;
     wxPoint end = m_End;
@@ -443,34 +542,37 @@ void GERBER_DRAW_ITEM::ConvertSegmentToPolygon( )
     wxPoint corner;
     corner.x -= m_Size.x/2;
     corner.y -= m_Size.y/2;
-    m_PolyCorners.push_back( corner );  // Lower left corner, start point (1)
+    wxPoint close = corner;
+    m_Polygon.Append( VECTOR2I( corner ) );  // Lower left corner, start point (1)
     corner.y += m_Size.y;
-    m_PolyCorners.push_back( corner );  // upper left corner, start point (2)
+    m_Polygon.Append( VECTOR2I( corner ) );  // upper left corner, start point (2)
 
     if( delta.x || delta.y)
     {
         corner += delta;
-        m_PolyCorners.push_back( corner );  // upper left corner, end point (3)
+        m_Polygon.Append( VECTOR2I( corner ) );  // upper left corner, end point (3)
     }
 
     corner.x += m_Size.x;
-    m_PolyCorners.push_back( corner );  // upper right corner, end point (4)
+    m_Polygon.Append( VECTOR2I( corner ) );  // upper right corner, end point (4)
     corner.y -= m_Size.y;
-    m_PolyCorners.push_back( corner );  // lower right corner, end point (5)
+    m_Polygon.Append( VECTOR2I( corner ) );  // lower right corner, end point (5)
 
     if( delta.x || delta.y )
     {
         corner -= delta;
-        m_PolyCorners.push_back( corner );  // lower left corner, start point (6)
+        m_Polygon.Append( VECTOR2I( corner ) );  // lower left corner, start point (6)
     }
 
+    m_Polygon.Append( VECTOR2I( close ) );  // close the shape
+
     // Create final polygon:
-    for( unsigned ii = 0; ii < m_PolyCorners.size(); ii++ )
+    for( auto it = m_Polygon.Iterate( 0 ); it; ++it )
     {
         if( change )
-            m_PolyCorners[ii].y = -m_PolyCorners[ii].y;
+            ( *it ).y = -( *it ).y;
 
-         m_PolyCorners[ii] += start;
+        *it += start;
     }
 }
 
@@ -482,15 +584,19 @@ void GERBER_DRAW_ITEM::DrawGbrPoly( EDA_RECT*      aClipBox,
                                     bool           aFilledShape )
 {
     std::vector<wxPoint> points;
+    SHAPE_LINE_CHAIN& poly = m_Polygon.Outline( 0 );
+    int pointCount = poly.PointCount() - 1;
 
-    points = m_PolyCorners;
-    for( unsigned ii = 0; ii < points.size(); ii++ )
+    points.reserve( pointCount );
+
+    for( int ii = 0; ii < pointCount; ii++ )
     {
-        points[ii] += aOffset;
-        points[ii]  = GetABPosition( points[ii] );
+        wxPoint p( poly.Point( ii ).x, poly.Point( ii ).y );
+        points[ii] = p + aOffset;
+        points[ii] = GetABPosition( points[ii] );
     }
 
-    GRClosedPoly( aClipBox, aDC, points.size(), &points[0], aFilledShape, aColor, aColor );
+    GRClosedPoly( aClipBox, aDC, pointCount, &points[0], aFilledShape, aColor, aColor );
 }
 
 
@@ -506,7 +612,7 @@ void GERBER_DRAW_ITEM::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
     msg.Printf( _( "D Code %d" ), m_DCode );
     D_CODE* apertDescr = GetDcodeDescr();
 
-    if( apertDescr->m_AperFunction.IsEmpty() )
+    if( !apertDescr || apertDescr->m_AperFunction.IsEmpty() )
         text = _( "No attribute" );
     else
         text = apertDescr->m_AperFunction;
@@ -582,6 +688,37 @@ bool GERBER_DRAW_ITEM::HitTest( const wxPoint& aRefPos ) const
     // TODO: a better analyze of the shape (perhaps create a D_CODE::HitTest for flashed items)
     int     radius = std::min( m_Size.x, m_Size.y ) >> 1;
 
+    SHAPE_POLY_SET poly;
+
+    switch( m_Shape )
+    {
+    case GBR_POLYGON:
+        poly = m_Polygon;
+        return poly.Contains( VECTOR2I( ref_pos ), 0 );
+        break;
+
+    case GBR_SPOT_POLY:
+        poly = GetDcodeDescr()->m_Polygon;
+        poly.Move( m_Start );
+        return poly.Contains( VECTOR2I( ref_pos ), 0 );
+        break;
+
+    case GBR_SPOT_RECT:
+        return GetBoundingBox().Contains( aRefPos );
+        break;
+
+    case GBR_SPOT_MACRO:
+        // Aperture macro polygons are already in absolute coordinates
+        poly = GetDcodeDescr()->GetMacro()->m_shape;
+        for( int i = 0; i < poly.OutlineCount(); ++i )
+        {
+            if( poly.Contains( VECTOR2I( aRefPos ), i ) )
+                return true;
+        }
+        return false;
+        break;
+    }
+
     if( m_Flashed )
         return HitTestPoints( m_Start, ref_pos, radius );
     else
@@ -624,3 +761,60 @@ void GERBER_DRAW_ITEM::Show( int nestLevel, std::ostream& os ) const
 }
 
 #endif
+
+
+void GERBER_DRAW_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
+{
+    aCount = 2;
+    aLayers[0] = GERBER_DRAW_LAYER( GetLayer() );
+    aLayers[1] = GERBER_DCODE_LAYER( aLayers[0] );
+}
+
+
+const BOX2I GERBER_DRAW_ITEM::ViewBBox() const
+{
+    EDA_RECT bbox = GetBoundingBox();
+    return BOX2I( VECTOR2I( bbox.GetOrigin() ),
+                  VECTOR2I( bbox.GetSize() ) );
+}
+
+
+unsigned int GERBER_DRAW_ITEM::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
+{
+    // DCodes will be shown only if zoom is appropriate
+    if( IsDCodeLayer( aLayer ) )
+    {
+        return ( 400000 / ( m_Size.x + 1 ) );
+    }
+
+    // Other layers are shown without any conditions
+    return 0;
+}
+
+
+SEARCH_RESULT GERBER_DRAW_ITEM::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
+{
+    KICAD_T stype = *scanTypes;
+
+    // If caller wants to inspect my type
+    if( stype == Type() )
+    {
+        if( SEARCH_QUIT == inspector( this, testData ) )
+            return SEARCH_QUIT;
+    }
+
+    return SEARCH_CONTINUE;
+}
+
+
+wxString GERBER_DRAW_ITEM::GetSelectMenuText() const
+{
+    wxString text, layerName;
+
+    layerName = GERBER_FILE_IMAGE_LIST::GetImagesList().GetDisplayName( GetLayer(), true );
+
+    text.Printf( _( "%s (D%d) on layer %d: %s" ), ShowGBRShape(), m_DCode,
+                 GetLayer() + 1, layerName );
+
+    return text;
+}
diff --git a/gerbview/class_gerber_draw_item.h b/gerbview/class_gerber_draw_item.h
index 832528fb7..a272392e3 100644
--- a/gerbview/class_gerber_draw_item.h
+++ b/gerbview/class_gerber_draw_item.h
@@ -35,6 +35,7 @@
 #include <gr_basic.h>
 #include <gbr_netlist_metadata.h>
 #include <dcode.h>
+#include <geometry/shape_poly_set.h>
 
 class GERBER_FILE_IMAGE;
 class GBR_LAYOUT;
@@ -42,6 +43,11 @@ class D_CODE;
 class MSG_PANEL_ITEM;
 class GBR_DISPLAY_OPTIONS;
 
+namespace KIGFX
+{
+    class VIEW;
+};
+
 
 /* Shapes id for basic shapes ( .m_Shape member ) */
 enum Gbr_Basic_Shapes {
@@ -76,7 +82,7 @@ public:
                                             // for flashed items
     wxPoint m_End;                          // Line or arc end point
     wxPoint m_ArcCentre;                    // for arcs only: Centre of arc
-    std::vector <wxPoint> m_PolyCorners;    // list of corners for polygons (G36 to G37 coordinates)
+    SHAPE_POLY_SET m_Polygon;               // Polygon shape data (G36 to G37 coordinates)
                                             // or for complex shapes which are converted to polygon
     wxSize  m_Size;                         // Flashed shapes: size of the shape
                                             // Lines : m_Size.x = m_Size.y = line width
@@ -116,7 +122,7 @@ public:
     GERBER_DRAW_ITEM* Back() const { return static_cast<GERBER_DRAW_ITEM*>( Pback ); }
 
     void SetNetAttributes( const GBR_NETLIST_METADATA& aNetAttributes );
-    const GBR_NETLIST_METADATA& GetNetAttributes()  { return m_netAttributes; }
+    const GBR_NETLIST_METADATA& GetNetAttributes() const { return m_netAttributes; }
 
     /**
      * Function GetLayer
@@ -124,7 +130,7 @@ public:
      */
     int GetLayer() const;
 
-    bool GetLayerPolarity()
+    bool GetLayerPolarity() const
     {
         return m_LayerNegative;
     }
@@ -186,6 +192,11 @@ public:
      */
     wxPoint GetABPosition( const wxPoint& aXYPosition ) const;
 
+    VECTOR2I GetABPosition( const VECTOR2I& aXYPosition ) const
+    {
+        return VECTOR2I( GetABPosition( wxPoint( aXYPosition.x, aXYPosition.y ) ) );
+    }
+
     /**
      * Function GetXYPosition
      * returns the image position of aPosition for this object.
@@ -201,7 +212,7 @@ public:
      * returns the GetDcodeDescr of this object, or NULL.
      * @return D_CODE* - a pointer to the DCode description (for flashed items).
      */
-    D_CODE* GetDcodeDescr();
+    D_CODE* GetDcodeDescr() const;
 
     const EDA_RECT GetBoundingBox() const override;
 
@@ -229,7 +240,7 @@ public:
 
     void GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) override;
 
-    wxString ShowGBRShape();
+    wxString ShowGBRShape() const;
 
     /**
      * Function HitTest
@@ -284,6 +295,26 @@ public:
     void Show( int nestLevel, std::ostream& os ) const override;
 #endif
 
+    /// @copydoc VIEW_ITEM::ViewGetLayers()
+    virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
+
+    /// @copydoc VIEW_ITEM::ViewBBox()
+    virtual const BOX2I ViewBBox() const override;
+
+    /// @copydoc VIEW_ITEM::ViewGetLOD()
+    virtual unsigned int ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override;
+
+    ///> @copydoc EDA_ITEM::Visit()
+    SEARCH_RESULT Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] ) override;
+
+    virtual wxString GetSelectMenuText() const override;
+
+};
+
+
+class GERBER_NEGATIVE_IMAGE_BACKDROP : public EDA_ITEM
+{
+
 };
 
 #endif /* CLASS_GERBER_DRAW_ITEM_H */
diff --git a/gerbview/class_gerber_file_image.cpp b/gerbview/class_gerber_file_image.cpp
index 9e30fc222..0e35991ae 100644
--- a/gerbview/class_gerber_file_image.cpp
+++ b/gerbview/class_gerber_file_image.cpp
@@ -89,7 +89,8 @@ void GERBER_LAYER::ResetDefaultValues()
 }
 
 
-GERBER_FILE_IMAGE::GERBER_FILE_IMAGE( int aLayer )
+GERBER_FILE_IMAGE::GERBER_FILE_IMAGE( int aLayer ) :
+    EDA_ITEM( (EDA_ITEM*)NULL, GERBER_IMAGE_T )
 {
     m_GraphicLayer = aLayer;        // Graphic layer Number
     m_IsVisible    = true;          // must be drawn
@@ -126,7 +127,8 @@ GERBER_DRAW_ITEM * GERBER_FILE_IMAGE::GetItemsList()
     return m_Drawings;
 }
 
-D_CODE* GERBER_FILE_IMAGE::GetDCODE( int aDCODE, bool aCreateIfNoExist )
+
+D_CODE* GERBER_FILE_IMAGE::GetDCODEOrCreate( int aDCODE, bool aCreateIfNoExist )
 {
     unsigned ndx = aDCODE - FIRST_DCODE;
 
@@ -146,6 +148,19 @@ D_CODE* GERBER_FILE_IMAGE::GetDCODE( int aDCODE, bool aCreateIfNoExist )
 }
 
 
+D_CODE* GERBER_FILE_IMAGE::GetDCODE( int aDCODE ) const
+{
+    unsigned ndx = aDCODE - FIRST_DCODE;
+
+    if( ndx < (unsigned) DIM( m_Aperture_List ) )
+    {
+        return m_Aperture_List[ndx];
+    }
+
+    return NULL;
+}
+
+
 APERTURE_MACRO* GERBER_FILE_IMAGE::FindApertureMacro( const APERTURE_MACRO& aLookup )
 {
     APERTURE_MACRO_SET::iterator iter = m_aperture_macros.find( aLookup );
@@ -370,3 +385,43 @@ void GERBER_FILE_IMAGE::RemoveAttribute( X2_ATTRIBUTE& aAttribute )
     if( aAttribute.GetPrm( 1 ).IsEmpty() || aAttribute.GetPrm( 1 ) == ".AperFunction" )
         m_AperFunction.Clear();
 }
+
+
+SEARCH_RESULT GERBER_FILE_IMAGE::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
+{
+    KICAD_T        stype;
+    SEARCH_RESULT  result = SEARCH_CONTINUE;
+    const KICAD_T* p    = scanTypes;
+    bool           done = false;
+
+#if 0 && defined(DEBUG)
+    std::cout << GetClass().mb_str() << ' ';
+#endif
+
+    while( !done )
+    {
+        stype = *p;
+
+        switch( stype )
+        {
+        case GERBER_IMAGE_T:
+        case GERBER_IMAGE_LIST_T:
+            ++p;
+            break;
+
+        case GERBER_DRAW_ITEM_T:
+            result = IterateForward( &m_Drawings[0], inspector, testData, p );
+            ++p;
+            break;
+
+        default:        // catch EOT or ANY OTHER type here and return.
+            done = true;
+            break;
+        }
+
+        if( result == SEARCH_QUIT )
+            break;
+    }
+
+    return result;
+}
diff --git a/gerbview/class_gerber_file_image.h b/gerbview/class_gerber_file_image.h
index 6875e60e4..6d83ae91c 100644
--- a/gerbview/class_gerber_file_image.h
+++ b/gerbview/class_gerber_file_image.h
@@ -95,7 +95,7 @@ private:
  * holds the Image data and parameters for one gerber file
  * and layer parameters (TODO: move them in GERBER_LAYER class
  */
-class GERBER_FILE_IMAGE
+class GERBER_FILE_IMAGE : public EDA_ITEM
 {
     D_CODE*            m_Aperture_List[TOOLS_MAX_COUNT];    ///< Dcode (Aperture) List for this layer (max 999)
     bool               m_Exposure;                          ///< whether an aperture macro tool is flashed on or off
@@ -180,6 +180,11 @@ public:
     GERBER_FILE_IMAGE( int layer );
     virtual ~GERBER_FILE_IMAGE();
 
+    wxString GetClass() const override
+    {
+        return wxT( "GERBER_FILE_IMAGE" );
+    }
+
     void Clear_GERBER_FILE_IMAGE();
 
     /**
@@ -300,7 +305,7 @@ public:
 
 
     /**
-     * Function GetDCODE
+     * Function GetDCODEOrCreate
      * returns a pointer to the D_CODE within this GERBER for the given
      * \a aDCODE.
      * @param aDCODE The numeric value of the D_CODE to look up.
@@ -309,7 +314,17 @@ public:
      * @return D_CODE* - the one implied by the given \a aDCODE, or NULL
      *            if the requested \a aDCODE is out of range.
      */
-    D_CODE*         GetDCODE( int aDCODE, bool aCreateIfNoExist = true );
+    D_CODE*         GetDCODEOrCreate( int aDCODE, bool aCreateIfNoExist = true );
+
+    /**
+     * Function GetDCODE
+     * returns a pointer to the D_CODE within this GERBER for the given
+     * \a aDCODE.
+     * @param aDCODE The numeric value of the D_CODE to look up.
+     * @return D_CODE* - the one implied by the given \a aDCODE, or NULL
+     *            if the requested \a aDCODE is out of range.
+     */
+    D_CODE*         GetDCODE( int aDCODE ) const;
 
     /**
      * Function FindApertureMacro
@@ -350,6 +365,15 @@ public:
      * only this attribute is cleared
      */
     void RemoveAttribute( X2_ATTRIBUTE& aAttribute );
+
+    ///> @copydoc EDA_ITEM::Visit()
+    SEARCH_RESULT Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] ) override;
+
+#if defined(DEBUG)
+
+    void    Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
+
+#endif
 };
 
 #endif  // ifndef CLASS_GERBER_FILE_IMAGE_H
diff --git a/gerbview/class_gerber_file_image_list.cpp b/gerbview/class_gerber_file_image_list.cpp
index 8c89ac064..0f034a53d 100644
--- a/gerbview/class_gerber_file_image_list.cpp
+++ b/gerbview/class_gerber_file_image_list.cpp
@@ -43,7 +43,8 @@ GERBER_FILE_IMAGE_LIST s_GERBER_List;
 
 
 // GERBER_FILE_IMAGE_LIST is a helper class to handle a list of GERBER_FILE_IMAGE files
-GERBER_FILE_IMAGE_LIST::GERBER_FILE_IMAGE_LIST()
+GERBER_FILE_IMAGE_LIST::GERBER_FILE_IMAGE_LIST() :
+    EDA_ITEM( (EDA_ITEM*)NULL, GERBER_IMAGE_LIST_T )
 {
     m_GERBER_List.reserve( GERBER_DRAWLAYERS_COUNT );
 
@@ -244,4 +245,3 @@ void GERBER_FILE_IMAGE_LIST::SortImagesByZOrder()
         gerber->m_GraphicLayer = layer ;
     }
 }
-
diff --git a/gerbview/class_gerber_file_image_list.h b/gerbview/class_gerber_file_image_list.h
index dd4c2baf1..ddd765ffd 100644
--- a/gerbview/class_gerber_file_image_list.h
+++ b/gerbview/class_gerber_file_image_list.h
@@ -59,7 +59,7 @@ class GERBER_FILE_IMAGE;
  * which are loaded and can be displayed
  * there are 32 images max which can be loaded
  */
-class GERBER_FILE_IMAGE_LIST
+class GERBER_FILE_IMAGE_LIST : public EDA_ITEM
 {
     // the list of loaded images (1 image = 1 gerber file)
     std::vector<GERBER_FILE_IMAGE*> m_GERBER_List;
@@ -68,6 +68,11 @@ public:
     GERBER_FILE_IMAGE_LIST();
     ~GERBER_FILE_IMAGE_LIST();
 
+    wxString GetClass() const override
+    {
+        return wxT( "GERBER_FILE_IMAGE_LIST" );
+    }
+
     //Accessor
     static GERBER_FILE_IMAGE_LIST& GetImagesList();
     GERBER_FILE_IMAGE* GetGbrImage( int aIdx );
@@ -115,6 +120,12 @@ public:
      * (SortImagesByZOrder updates the graphic layer of these items)
      */
     void SortImagesByZOrder();
+
+    #if defined(DEBUG)
+
+        void    Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
+
+    #endif
 };
 
 #endif  // ifndef CLASS_GERBER_FILE_IMAGE_LIST_H
diff --git a/gerbview/class_gerbview_layer_widget.cpp b/gerbview/class_gerbview_layer_widget.cpp
index d484f9a4e..4b95e20c9 100644
--- a/gerbview/class_gerbview_layer_widget.cpp
+++ b/gerbview/class_gerbview_layer_widget.cpp
@@ -41,6 +41,10 @@
 #include <layer_widget.h>
 #include <class_gerbview_layer_widget.h>
 
+#include <view/view.h>
+#include <gerbview_painter.h>
+#include <gal/graphics_abstraction_layer.h>
+
 
 /*
  * Class GERBER_LAYER_WIDGET
@@ -83,6 +87,12 @@ GERBER_FILE_IMAGE_LIST* GERBER_LAYER_WIDGET::GetImagesList()
 }
 
 
+bool GERBER_LAYER_WIDGET::AreArbitraryColorsAllowed()
+{
+    return myframe->IsGalCanvasActive();
+}
+
+
 void GERBER_LAYER_WIDGET::SetLayersManagerTabsText( )
 {
     m_notebook->SetPageText(0, _("Layer") );
@@ -193,8 +203,11 @@ void GERBER_LAYER_WIDGET::onPopupSelection( wxCommandEvent& event )
             int layer = getDecodedId( cb->GetId() );
             bool loc_visible = visible;
 
-            if( force_active_layer_visible && (layer == myframe->getActiveLayer() ) )
+            if( force_active_layer_visible &&
+                (layer == GERBER_DRAW_LAYER( myframe->GetActiveLayer() ) ) )
+            {
                 loc_visible = true;
+            }
 
             cb->SetValue( loc_visible );
 
@@ -241,8 +254,19 @@ void GERBER_LAYER_WIDGET::ReFill()
     {
         wxString msg = GetImagesList()->GetDisplayName( layer );
 
-        AppendLayerRow( LAYER_WIDGET::ROW( msg, layer,
-                        myframe->GetLayerColor( layer ), wxEmptyString, true ) );
+        bool visible = true;
+        if( auto canvas = myframe->GetGalCanvas() )
+        {
+            visible = canvas->GetView()->IsLayerVisible( GERBER_DRAW_LAYER( layer ) );
+        }
+        else
+        {
+            visible = myframe->IsLayerVisible( layer );
+        }
+
+        AppendLayerRow( LAYER_WIDGET::ROW( msg, GERBER_DRAW_LAYER( layer ),
+                        myframe->GetLayerColor( GERBER_DRAW_LAYER( layer ) ),
+                        wxEmptyString, visible, true ) );
     }
 
     Thaw();
@@ -256,6 +280,14 @@ void GERBER_LAYER_WIDGET::OnLayerColorChange( int aLayer, COLOR4D aColor )
 {
     myframe->SetLayerColor( aLayer, aColor );
     myframe->m_SelLayerBox->ResyncBitmapOnly();
+
+    if( myframe->IsGalCanvasActive() )
+    {
+        KIGFX::VIEW* view = myframe->GetGalCanvas()->GetView();
+        view->GetPainter()->GetSettings()->ImportLegacyColors( myframe->m_colorsSettings );
+        view->UpdateLayerColor( aLayer );
+    }
+
     myframe->GetCanvas()->Refresh();
 }
 
@@ -263,11 +295,13 @@ bool GERBER_LAYER_WIDGET::OnLayerSelect( int aLayer )
 {
     // the layer change from the GERBER_LAYER_WIDGET can be denied by returning
     // false from this function.
-    int layer = myframe->getActiveLayer( );
-    myframe->setActiveLayer( aLayer, false );
+    int layer = myframe->GetActiveLayer( );
+    // TODO(JE) ActiveLayer is stored as an index from 0 rather than as a layer
+    // id matching GERBER_DRAW_LAYER( idx ), is this what we want long-term?
+    myframe->SetActiveLayer( GERBER_DRAW_LAYER_INDEX( aLayer ), false );
     myframe->syncLayerBox();
 
-    if( layer != myframe->getActiveLayer( ) )
+    if( layer != myframe->GetActiveLayer( ) )
     {
         if( ! OnLayerSelected() )
             myframe->GetCanvas()->Refresh();
@@ -294,13 +328,48 @@ void GERBER_LAYER_WIDGET::OnLayerVisible( int aLayer, bool isVisible, bool isFin
 void GERBER_LAYER_WIDGET::OnRenderColorChange( int aId, COLOR4D aColor )
 {
     myframe->SetVisibleElementColor( (GERBVIEW_LAYER_ID) aId, aColor );
-    myframe->GetCanvas()->Refresh();
+
+    auto galCanvas = myframe->GetGalCanvas();
+
+    if( galCanvas && myframe->IsGalCanvasActive() )
+    {
+        auto view = galCanvas->GetView();
+        view->GetPainter()->GetSettings()->ImportLegacyColors( myframe->m_colorsSettings );
+        view->UpdateLayerColor( aId );
+        // TODO(JE) Why are the below two lines needed? Not needed in pcbnew
+        view->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
+        view->RecacheAllItems();
+    }
+
+    if( galCanvas && myframe->IsGalCanvasActive() )
+        galCanvas->Refresh();
+    else
+        myframe->GetCanvas()->Refresh();
 }
 
 void GERBER_LAYER_WIDGET::OnRenderEnable( int aId, bool isEnabled )
 {
     myframe->SetElementVisibility( (GERBVIEW_LAYER_ID) aId, isEnabled );
-    myframe->GetCanvas()->Refresh();
+
+    auto galCanvas = myframe->GetGalCanvas();
+
+    if( galCanvas )
+    {
+        if( aId == LAYER_GERBVIEW_GRID )
+        {
+            galCanvas->GetGAL()->SetGridVisibility( myframe->IsGridVisible() );
+            galCanvas->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
+            // TODO(JE) Why is the below line needed? Not needed in pcbnew
+            galCanvas->GetView()->RecacheAllItems();
+        }
+        else
+            galCanvas->GetView()->SetLayerVisible( aId, isEnabled );
+    }
+
+    if( galCanvas && myframe->IsGalCanvasActive() )
+        galCanvas->Refresh();
+    else
+        myframe->GetCanvas()->Refresh();
 }
 
 //-----</LAYER_WIDGET callbacks>------------------------------------------
diff --git a/gerbview/class_gerbview_layer_widget.h b/gerbview/class_gerbview_layer_widget.h
index dcac8a781..7a0a55b3c 100644
--- a/gerbview/class_gerbview_layer_widget.h
+++ b/gerbview/class_gerbview_layer_widget.h
@@ -77,6 +77,8 @@ class GERBER_LAYER_WIDGET : public LAYER_WIDGET
      */
     virtual bool useAlternateBitmap(int aRow) override;
 
+    virtual bool AreArbitraryColorsAllowed() override;
+
     GERBER_FILE_IMAGE_LIST* GetImagesList();
 
 public:
diff --git a/gerbview/clear_gbr_drawlayers.cpp b/gerbview/clear_gbr_drawlayers.cpp
index 112afc223..bba59750e 100644
--- a/gerbview/clear_gbr_drawlayers.cpp
+++ b/gerbview/clear_gbr_drawlayers.cpp
@@ -35,6 +35,7 @@
 #include <class_gerber_file_image.h>
 #include <class_gerber_file_image_list.h>
 #include <class_gerbview_layer_widget.h>
+#include <view/view.h>
 
 bool GERBVIEW_FRAME::Clear_DrawLayers( bool query )
 {
@@ -47,11 +48,16 @@ bool GERBVIEW_FRAME::Clear_DrawLayers( bool query )
             return false;
     }
 
+    if( auto canvas = GetGalCanvas() )
+    {
+        canvas->GetView()->Clear();
+    }
+
     GetImagesList()->DeleteAllImages();
 
     GetGerberLayout()->SetBoundingBox( EDA_RECT() );
 
-    setActiveLayer( 0 );
+    SetActiveLayer( 0 );
     ReFillLayerWidget();
     syncLayerBox();
     return true;
@@ -60,7 +66,7 @@ bool GERBVIEW_FRAME::Clear_DrawLayers( bool query )
 
 void GERBVIEW_FRAME::Erase_Current_DrawLayer( bool query )
 {
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
     wxString msg;
 
     msg.Printf( _( "Clear layer %d?" ), layer + 1 );
diff --git a/gerbview/dcode.cpp b/gerbview/dcode.cpp
index b7cd055cb..847a6835c 100644
--- a/gerbview/dcode.cpp
+++ b/gerbview/dcode.cpp
@@ -36,6 +36,7 @@
 #include <gerbview_frame.h>
 #include <class_gerber_file_image.h>
 #include <convert_to_biu.h>
+#include <convert_basic_shapes_to_polygon.h>
 
 #define DCODE_DEFAULT_SIZE Millimeter2iu( 0.1 )
 
@@ -87,7 +88,7 @@ void D_CODE::Clear_D_CODE_Data()
     m_Macro      = NULL;
     m_Rotation   = 0.0;
     m_EdgesCount = 0;
-    m_PolyCorners.clear();
+    m_Polygon.RemoveAllContours();
 }
 
 
@@ -173,7 +174,7 @@ void D_CODE::DrawFlashedShape(  GERBER_DRAW_ITEM* aParent,
                 GRFilledCircle( aClipBox, aDC, aParent->GetABPosition(aShapePos),
                                 radius, aColor );
             }
-            else if( APT_DEF_ROUND_HOLE == 1 )    // round hole in shape
+            else if( m_DrillShape == APT_DEF_ROUND_HOLE )    // round hole in shape
             {
                 int width = (m_Size.x - m_Drill.x ) / 2;
                 GRCircle( aClipBox, aDC,  aParent->GetABPosition(aShapePos),
@@ -181,7 +182,7 @@ void D_CODE::DrawFlashedShape(  GERBER_DRAW_ITEM* aParent,
             }
             else                            // rectangular hole
             {
-                if( m_PolyCorners.size() == 0 )
+                if( m_Polygon.OutlineCount() == 0 )
                     ConvertShapeToPolygon();
 
                 DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
@@ -207,7 +208,7 @@ void D_CODE::DrawFlashedShape(  GERBER_DRAW_ITEM* aParent,
         }
         else
         {
-            if( m_PolyCorners.size() == 0 )
+            if( m_Polygon.OutlineCount() == 0 )
                 ConvertShapeToPolygon();
 
             DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
@@ -248,7 +249,7 @@ void D_CODE::DrawFlashedShape(  GERBER_DRAW_ITEM* aParent,
         }
         else
         {
-            if( m_PolyCorners.size() == 0 )
+            if( m_Polygon.OutlineCount() == 0 )
                 ConvertShapeToPolygon();
 
             DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
@@ -257,7 +258,7 @@ void D_CODE::DrawFlashedShape(  GERBER_DRAW_ITEM* aParent,
     break;
 
     case APT_POLYGON:
-        if( m_PolyCorners.size() == 0 )
+        if( m_Polygon.OutlineCount() == 0 )
             ConvertShapeToPolygon();
 
         DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
@@ -271,27 +272,29 @@ void D_CODE::DrawFlashedPolygon( GERBER_DRAW_ITEM* aParent,
                                  COLOR4D aColor, bool aFilled,
                                  const wxPoint& aPosition )
 {
-    if( m_PolyCorners.size() == 0 )
+    if( m_Polygon.OutlineCount() == 0 )
         return;
 
+    int pointCount = m_Polygon.VertexCount();
     std::vector<wxPoint> points;
-    points = m_PolyCorners;
+    points.reserve( pointCount );
 
-    for( unsigned ii = 0; ii < points.size(); ii++ )
+    for( int ii = 0; ii < pointCount; ii++ )
     {
-        points[ii] += aPosition;
+        wxPoint p( m_Polygon.Vertex( ii ).x, m_Polygon.Vertex( ii ).y );
+        points[ii] = p + aPosition;
         points[ii] = aParent->GetABPosition( points[ii] );
     }
 
-    GRClosedPoly( aClipBox, aDC, points.size(), &points[0], aFilled, aColor, aColor );
+    GRClosedPoly( aClipBox, aDC, pointCount, &points[0], aFilled, aColor, aColor );
 }
 
 
-#define SEGS_CNT 32     // number of segments to approximate a circle
+#define SEGS_CNT 64     // number of segments to approximate a circle
 
 
 // A helper function for D_CODE::ConvertShapeToPolygon().   Add a hole to a polygon
-static void addHoleToPolygon( std::vector<wxPoint>& aBuffer,
+static void addHoleToPolygon( SHAPE_POLY_SET*       aPolygon,
                               APERTURE_DEF_HOLETYPE aHoleShape,
                               wxSize                aSize,
                               wxPoint               aAnchorPos );
@@ -302,43 +305,37 @@ void D_CODE::ConvertShapeToPolygon()
     wxPoint initialpos;
     wxPoint currpos;
 
-    m_PolyCorners.clear();
+    m_Polygon.RemoveAllContours();
 
     switch( m_Shape )
     {
     case APT_CIRCLE:        // creates only a circle with rectangular hole
-        currpos.x  = m_Size.x >> 1;
-        initialpos = currpos;
-
-        for( unsigned ii = 0; ii <= SEGS_CNT; ii++ )
-        {
-            currpos = initialpos;
-            RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
-            m_PolyCorners.push_back( currpos );
-        }
-
-        addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
+        TransformCircleToPolygon( m_Polygon, initialpos, m_Size.x >> 1, SEGS_CNT );
+        addHoleToPolygon( &m_Polygon, m_DrillShape, m_Drill, initialpos );
         break;
 
     case APT_RECT:
+        m_Polygon.NewOutline();
         currpos.x  = m_Size.x / 2;
         currpos.y  = m_Size.y / 2;
         initialpos = currpos;
-        m_PolyCorners.push_back( currpos );
+        m_Polygon.Append( VECTOR2I( currpos ) );
         currpos.x -= m_Size.x;
-        m_PolyCorners.push_back( currpos );
+        m_Polygon.Append( VECTOR2I( currpos ) );
         currpos.y -= m_Size.y;
-        m_PolyCorners.push_back( currpos );
+        m_Polygon.Append( VECTOR2I( currpos ) );
         currpos.x += m_Size.x;
-        m_PolyCorners.push_back( currpos );
+        m_Polygon.Append( VECTOR2I( currpos ) );
         currpos.y += m_Size.y;
-        m_PolyCorners.push_back( currpos );    // close polygon
+        m_Polygon.Append( VECTOR2I( currpos ) );    // close polygon
+        m_Polygon.Append( VECTOR2I( initialpos ) );
 
-        addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
+        addHoleToPolygon( &m_Polygon, m_DrillShape, m_Drill, initialpos );
         break;
 
     case APT_OVAL:
     {
+        m_Polygon.NewOutline();
         int delta, radius;
 
         // we create an horizontal oval shape. then rotate if needed
@@ -355,7 +352,7 @@ void D_CODE::ConvertShapeToPolygon()
 
         currpos.y  = radius;
         initialpos = currpos;
-        m_PolyCorners.push_back( currpos );
+        m_Polygon.Append( VECTOR2I( currpos ) );
 
         // build the right arc of the shape
         unsigned ii = 0;
@@ -365,7 +362,7 @@ void D_CODE::ConvertShapeToPolygon()
             currpos = initialpos;
             RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
             currpos.x += delta;
-            m_PolyCorners.push_back( currpos );
+            m_Polygon.Append( VECTOR2I( currpos ) );
         }
 
         // build the left arc of the shape
@@ -374,22 +371,23 @@ void D_CODE::ConvertShapeToPolygon()
             currpos = initialpos;
             RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
             currpos.x -= delta;
-            m_PolyCorners.push_back( currpos );
+            m_Polygon.Append( VECTOR2I( currpos ) );
         }
 
-        m_PolyCorners.push_back( initialpos );      // close outline
+        m_Polygon.Append( VECTOR2I( initialpos ) );      // close outline
 
         if( m_Size.y > m_Size.x )                   // vertical oval, rotate polygon.
         {
-            for( unsigned jj = 0; jj < m_PolyCorners.size(); jj++ )
-                RotatePoint( &m_PolyCorners[jj], 900 );
+            for( auto it = m_Polygon.Iterate( 0 ); it; ++it )
+                it->Rotate( -M_PI / 2 );
         }
 
-        addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
+        addHoleToPolygon( &m_Polygon, m_DrillShape, m_Drill, initialpos );
     }
     break;
 
     case APT_POLYGON:
+        m_Polygon.NewOutline();
         currpos.x  = m_Size.x >> 1;     // first point is on X axis
         initialpos = currpos;
 
@@ -400,23 +398,21 @@ void D_CODE::ConvertShapeToPolygon()
         if( m_EdgesCount > 12 )
             m_EdgesCount = 12;
 
-        for( int ii = 0; ii <= m_EdgesCount; ii++ )
+        for( int ii = 0; ii < m_EdgesCount; ii++ )
         {
             currpos = initialpos;
             RotatePoint( &currpos, ii * 3600.0 / m_EdgesCount );
-            m_PolyCorners.push_back( currpos );
+            m_Polygon.Append( VECTOR2I( currpos ) );
         }
 
-        addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
+        addHoleToPolygon( &m_Polygon, m_DrillShape, m_Drill, initialpos );
 
         if( m_Rotation )                   // vertical oval, rotate polygon.
         {
             int angle = KiROUND( m_Rotation * 10 );
 
-            for( unsigned jj = 0; jj < m_PolyCorners.size(); jj++ )
-            {
-                RotatePoint( &m_PolyCorners[jj], -angle );
-            }
+            for( auto it = m_Polygon.Iterate( 0 ); it; ++it )
+                it->Rotate( -angle );
         }
 
         break;
@@ -431,39 +427,36 @@ void D_CODE::ConvertShapeToPolygon()
 
 // The helper function for D_CODE::ConvertShapeToPolygon().
 // Add a hole to a polygon
-static void addHoleToPolygon( std::vector<wxPoint>& aBuffer,
+static void addHoleToPolygon( SHAPE_POLY_SET*       aPolygon,
                               APERTURE_DEF_HOLETYPE aHoleShape,
                               wxSize                aSize,
                               wxPoint               aAnchorPos )
 {
     wxPoint currpos;
+    SHAPE_POLY_SET holeBuffer;
 
-    if( aHoleShape == APT_DEF_ROUND_HOLE )      // build a round hole
+    if( aHoleShape == APT_DEF_ROUND_HOLE )
     {
-        for( int ii = 0; ii <= SEGS_CNT; ii++ )
-        {
-            currpos.x = 0;
-            currpos.y = aSize.x / 2;            // aSize.x / 2 is the radius of the hole
-            RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
-            aBuffer.push_back( currpos );
-        }
-
-        aBuffer.push_back( aAnchorPos );        // link to outline
+        TransformCircleToPolygon( holeBuffer, wxPoint( 0, 0 ), aSize.x / 2, SEGS_CNT );
     }
-
-    if( aHoleShape == APT_DEF_RECT_HOLE )       // Create rectangular hole
+    else if( aHoleShape == APT_DEF_RECT_HOLE )
     {
+        holeBuffer.NewOutline();
         currpos.x = aSize.x / 2;
         currpos.y = aSize.y / 2;
-        aBuffer.push_back( currpos );           // link to hole and begin hole
+        holeBuffer.Append( VECTOR2I( currpos ) );       // link to hole and begin hole
         currpos.x -= aSize.x;
-        aBuffer.push_back( currpos );
+        holeBuffer.Append( VECTOR2I( currpos ) );
         currpos.y -= aSize.y;
-        aBuffer.push_back( currpos );
+        holeBuffer.Append( VECTOR2I( currpos ) );
         currpos.x += aSize.x;
-        aBuffer.push_back( currpos );
+        holeBuffer.Append( VECTOR2I( currpos ) );
         currpos.y += aSize.y;
-        aBuffer.push_back( currpos );           // close hole
-        aBuffer.push_back( aAnchorPos );        // link to outline
+        holeBuffer.Append( VECTOR2I( currpos ) );       // close hole
     }
+
+    aPolygon->BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST );
+
+    // Needed for legacy canvas only
+    aPolygon->Fracture( SHAPE_POLY_SET::PM_FAST );
 }
diff --git a/gerbview/dcode.h b/gerbview/dcode.h
index 31c98268b..7b40e205b 100644
--- a/gerbview/dcode.h
+++ b/gerbview/dcode.h
@@ -34,6 +34,7 @@
 
 #include <base_struct.h>
 #include <gal/color4d.h>
+#include <geometry/shape_poly_set.h>
 
 using KIGFX::COLOR4D;
 
@@ -89,11 +90,6 @@ private:
      */
     std::vector<double>   m_am_params;
 
-    std::vector <wxPoint> m_PolyCorners;    /* Polygon used to draw APT_POLYGON shape and some other
-                                             * complex shapes which are converted to polygon
-                                             * (shapes with hole )
-                                             */
-
 public:
     wxSize                m_Size;           ///< Horizontal and vertical dimensions.
     APERTURE_T            m_Shape;          ///< shape ( Line, rectangle, circle , oval .. )
@@ -108,7 +104,10 @@ public:
     bool                  m_Defined;        ///< false if the aperture is not defined in the header
     wxString              m_AperFunction;   ///< the aperture attribute (created by a %TA.AperFunction command)
                                             ///< attached to the D_CODE
-
+    SHAPE_POLY_SET        m_Polygon;        /* Polygon used to draw APT_POLYGON shape and some other
+                                             * complex shapes which are converted to polygon
+                                             * (shapes with hole )
+                                             */
 
 public:
     D_CODE( int num_dcode );
diff --git a/gerbview/dialogs/gerbview_dialog_display_options_frame.cpp b/gerbview/dialogs/gerbview_dialog_display_options_frame.cpp
index 504d742fd..132347752 100644
--- a/gerbview/dialogs/gerbview_dialog_display_options_frame.cpp
+++ b/gerbview/dialogs/gerbview_dialog_display_options_frame.cpp
@@ -32,11 +32,18 @@
 #include <common.h>
 #include <macros.h>
 #include <class_drawpanel.h>
+#include <config_map.h>
 
 #include <gerbview.h>
 #include <gerbview_frame.h>
 #include <gerbview_dialog_display_options_frame_base.h>
 
+#include <class_draw_panel_gal.h>
+#include <view/view.h>
+#include <gerbview_painter.h>
+#include <gal/gal_display_options.h>
+#include <widgets/gal_options_panel.h>
+
 
 /*******************************************/
 /* Dialog frame to select display options */
@@ -45,6 +52,7 @@ class DIALOG_DISPLAY_OPTIONS : public DIALOG_DISPLAY_OPTIONS_BASE
 {
 private:
     GERBVIEW_FRAME* m_Parent;
+    GAL_OPTIONS_PANEL* m_galOptsPanel;
 
 public:
 
@@ -79,6 +87,8 @@ DIALOG_DISPLAY_OPTIONS::DIALOG_DISPLAY_OPTIONS( GERBVIEW_FRAME *parent) :
     GetSizer()->SetSizeHints( this );
     Center();
     m_sdbSizer1OK->SetDefault();
+
+    FinishDialogSettings();
 }
 
 
@@ -90,23 +100,14 @@ void DIALOG_DISPLAY_OPTIONS::OnCancelButtonClick( wxCommandEvent& event )
 
 void DIALOG_DISPLAY_OPTIONS::initOptDialog( )
 {
+    KIGFX::GAL_DISPLAY_OPTIONS& galOptions = m_Parent->GetGalDisplayOptions();
+    m_galOptsPanel = new GAL_OPTIONS_PANEL( this, galOptions );
+    m_UpperSizer->Add( m_galOptsPanel, 0, wxEXPAND, 0 );
+    m_galOptsPanel->TransferDataToWindow();
+
     m_PolarDisplay->SetSelection( m_Parent->m_DisplayOptions.m_DisplayPolarCood ? 1 : 0 );
     m_BoxUnits->SetSelection( g_UserUnit ? 1 : 0 );
 
-    // @todo: LEGACY: Cursor shape can be set using the GAL options
-    // widget, when that is added to gerbview. For now, access the
-    // setting via the frame's GAL options object directly
-
-    // Cursor shape cannot be implemented on OS X
-#ifdef __APPLE__
-    m_CursorShape->Hide();
-#else
-    {
-        auto& galOpts = m_Parent->GetGalDisplayOptions();
-        m_CursorShape->SetSelection( galOpts.m_fullscreenCursor ? 1 : 0 );
-    }
-#endif // __APPLE__
-
     // Show Option Draw Lines. We use DisplayPcbTrackFill as Lines draw option
     m_OptDisplayLines->SetSelection( m_Parent->m_DisplayOptions.m_DisplayLinesFill ? 1 : 0 );
     m_OptDisplayFlashedItems->SetSelection( m_Parent->m_DisplayOptions.m_DisplayFlashedItemsFill ? 1 : 0);
@@ -131,8 +132,6 @@ void DIALOG_DISPLAY_OPTIONS::initOptDialog( )
     }
 
     m_OptDisplayDCodes->SetValue( m_Parent->IsElementVisible( LAYER_DCODES ) );
-
-
     m_OptZoomNoCenter->SetValue( m_Parent->GetCanvas()->GetEnableZoomNoCenter() );
     m_OptMousewheelPan->SetValue( m_Parent->GetCanvas()->GetEnableMousewheelPan() );
 }
@@ -140,18 +139,12 @@ void DIALOG_DISPLAY_OPTIONS::initOptDialog( )
 
 void DIALOG_DISPLAY_OPTIONS::OnOKBUttonClick( wxCommandEvent& event )
 {
+    auto displayOptions = (GBR_DISPLAY_OPTIONS*) m_Parent->GetDisplayOptions();
+
     m_Parent->m_DisplayOptions.m_DisplayPolarCood =
         (m_PolarDisplay->GetSelection() == 0) ? false : true;
     g_UserUnit  = (m_BoxUnits->GetSelection() == 0) ? INCHES : MILLIMETRES;
 
-    // @todo LEGACY: as above, this should be via the GAL display widget
-#ifndef __APPLE__
-    {
-        auto& galOpts = m_Parent->GetGalDisplayOptions();
-        galOpts.m_fullscreenCursor = m_CursorShape->GetSelection();
-    }
-#endif // !__APPLE__
-
     if( m_OptDisplayLines->GetSelection() == 1 )
         m_Parent->m_DisplayOptions.m_DisplayLinesFill = true;
     else
@@ -166,7 +159,6 @@ void DIALOG_DISPLAY_OPTIONS::OnOKBUttonClick( wxCommandEvent& event )
         m_Parent->m_DisplayOptions.m_DisplayFlashedItemsFill = false;
     }
 
-
     if( m_OptDisplayPolygons->GetSelection() == 0 )
         m_Parent->m_DisplayOptions.m_DisplayPolygonsFill = false;
     else
@@ -185,6 +177,16 @@ void DIALOG_DISPLAY_OPTIONS::OnOKBUttonClick( wxCommandEvent& event )
     m_Parent->GetCanvas()->SetEnableZoomNoCenter( m_OptZoomNoCenter->GetValue() );
     m_Parent->GetCanvas()->SetEnableMousewheelPan( m_OptMousewheelPan->GetValue() );
 
+    m_galOptsPanel->TransferDataFromWindow();
+
+    // Apply changes to the GAL
+    auto view = m_Parent->GetGalCanvas()->GetView();
+    auto painter = static_cast<KIGFX::GERBVIEW_PAINTER*>( view->GetPainter() );
+    auto settings = static_cast<KIGFX::GERBVIEW_RENDER_SETTINGS*>( painter->GetSettings() );
+    settings->LoadDisplayOptions( displayOptions );
+    view->RecacheAllItems();
+    view->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
+
     m_Parent->GetCanvas()->Refresh();
 
     EndModal( 1 );
diff --git a/gerbview/dialogs/gerbview_dialog_display_options_frame_base.cpp b/gerbview/dialogs/gerbview_dialog_display_options_frame_base.cpp
index f977d04ce..9c0948028 100644
--- a/gerbview/dialogs/gerbview_dialog_display_options_frame_base.cpp
+++ b/gerbview/dialogs/gerbview_dialog_display_options_frame_base.cpp
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version May  6 2016)
+// C++ code generated with wxFormBuilder (version Nov 30 2016)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO "NOT" EDIT THIS FILE!
@@ -16,8 +16,7 @@ DIALOG_DISPLAY_OPTIONS_BASE::DIALOG_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWi
 	wxBoxSizer* bDialogSizer;
 	bDialogSizer = new wxBoxSizer( wxVERTICAL );
 	
-	wxBoxSizer* bUpperSizer;
-	bUpperSizer = new wxBoxSizer( wxHORIZONTAL );
+	m_UpperSizer = new wxBoxSizer( wxHORIZONTAL );
 	
 	wxBoxSizer* bLeftSizer;
 	bLeftSizer = new wxBoxSizer( wxVERTICAL );
@@ -34,42 +33,30 @@ DIALOG_DISPLAY_OPTIONS_BASE::DIALOG_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWi
 	m_BoxUnits->SetSelection( 0 );
 	bLeftSizer->Add( m_BoxUnits, 0, wxALL|wxEXPAND, 5 );
 	
-	wxString m_CursorShapeChoices[] = { _("Small cross"), _("Full screen cursor") };
-	int m_CursorShapeNChoices = sizeof( m_CursorShapeChoices ) / sizeof( wxString );
-	m_CursorShape = new wxRadioBox( this, wxID_ANY, _("Cursor"), wxDefaultPosition, wxDefaultSize, m_CursorShapeNChoices, m_CursorShapeChoices, 1, wxRA_SPECIFY_COLS );
-	m_CursorShape->SetSelection( 1 );
-	bLeftSizer->Add( m_CursorShape, 0, wxALL|wxEXPAND, 5 );
-	
-	m_OptDisplayDCodes = new wxCheckBox( this, wxID_ANY, _("Show D codes"), wxDefaultPosition, wxDefaultSize, 0 );
-	m_OptDisplayDCodes->SetValue(true); 
-	bLeftSizer->Add( m_OptDisplayDCodes, 0, wxALL, 5 );
-	
-	
-	bUpperSizer->Add( bLeftSizer, 1, wxALL|wxEXPAND, 5 );
-	
-	wxBoxSizer* bMiddleSizer;
-	bMiddleSizer = new wxBoxSizer( wxVERTICAL );
+	wxString m_OptDisplayFlashedItemsChoices[] = { _("Sketch"), _("Filled") };
+	int m_OptDisplayFlashedItemsNChoices = sizeof( m_OptDisplayFlashedItemsChoices ) / sizeof( wxString );
+	m_OptDisplayFlashedItems = new wxRadioBox( this, wxID_ANY, _("Flashed items"), wxDefaultPosition, wxDefaultSize, m_OptDisplayFlashedItemsNChoices, m_OptDisplayFlashedItemsChoices, 1, wxRA_SPECIFY_COLS );
+	m_OptDisplayFlashedItems->SetSelection( 1 );
+	bLeftSizer->Add( m_OptDisplayFlashedItems, 0, wxALL|wxEXPAND, 5 );
 	
 	wxString m_OptDisplayLinesChoices[] = { _("Sketch"), _("Filled") };
 	int m_OptDisplayLinesNChoices = sizeof( m_OptDisplayLinesChoices ) / sizeof( wxString );
 	m_OptDisplayLines = new wxRadioBox( this, wxID_ANY, _("Lines"), wxDefaultPosition, wxDefaultSize, m_OptDisplayLinesNChoices, m_OptDisplayLinesChoices, 1, wxRA_SPECIFY_COLS );
 	m_OptDisplayLines->SetSelection( 1 );
-	bMiddleSizer->Add( m_OptDisplayLines, 0, wxALL|wxEXPAND, 5 );
-	
-	wxString m_OptDisplayFlashedItemsChoices[] = { _("Sketch"), _("Filled") };
-	int m_OptDisplayFlashedItemsNChoices = sizeof( m_OptDisplayFlashedItemsChoices ) / sizeof( wxString );
-	m_OptDisplayFlashedItems = new wxRadioBox( this, wxID_ANY, _("Flashed items"), wxDefaultPosition, wxDefaultSize, m_OptDisplayFlashedItemsNChoices, m_OptDisplayFlashedItemsChoices, 1, wxRA_SPECIFY_COLS );
-	m_OptDisplayFlashedItems->SetSelection( 1 );
-	bMiddleSizer->Add( m_OptDisplayFlashedItems, 0, wxALL|wxEXPAND, 5 );
+	bLeftSizer->Add( m_OptDisplayLines, 0, wxALL|wxEXPAND, 5 );
 	
 	wxString m_OptDisplayPolygonsChoices[] = { _("Sketch"), _("Filled") };
 	int m_OptDisplayPolygonsNChoices = sizeof( m_OptDisplayPolygonsChoices ) / sizeof( wxString );
 	m_OptDisplayPolygons = new wxRadioBox( this, wxID_ANY, _("Polygons"), wxDefaultPosition, wxDefaultSize, m_OptDisplayPolygonsNChoices, m_OptDisplayPolygonsChoices, 1, wxRA_SPECIFY_COLS );
 	m_OptDisplayPolygons->SetSelection( 1 );
-	bMiddleSizer->Add( m_OptDisplayPolygons, 0, wxALL|wxEXPAND, 5 );
+	bLeftSizer->Add( m_OptDisplayPolygons, 0, wxALL|wxEXPAND, 5 );
+	
+	m_OptDisplayDCodes = new wxCheckBox( this, wxID_ANY, _("Show D codes"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_OptDisplayDCodes->SetValue(true); 
+	bLeftSizer->Add( m_OptDisplayDCodes, 0, wxALL, 5 );
 	
 	
-	bUpperSizer->Add( bMiddleSizer, 1, wxALL|wxEXPAND, 5 );
+	m_UpperSizer->Add( bLeftSizer, 1, wxALL|wxEXPAND, 5 );
 	
 	wxBoxSizer* bRightSizer;
 	bRightSizer = new wxBoxSizer( wxVERTICAL );
@@ -97,10 +84,10 @@ DIALOG_DISPLAY_OPTIONS_BASE::DIALOG_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWi
 	bRightSizer->Add( bLeftBottomSizer, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 );
 	
 	
-	bUpperSizer->Add( bRightSizer, 2, wxALL|wxEXPAND, 5 );
+	m_UpperSizer->Add( bRightSizer, 2, wxALL|wxEXPAND, 5 );
 	
 	
-	bDialogSizer->Add( bUpperSizer, 1, wxEXPAND, 5 );
+	bDialogSizer->Add( m_UpperSizer, 1, wxEXPAND, 5 );
 	
 	m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
 	bDialogSizer->Add( m_staticline1, 0, wxEXPAND|wxLEFT|wxRIGHT, 5 );
diff --git a/gerbview/dialogs/gerbview_dialog_display_options_frame_base.fbp b/gerbview/dialogs/gerbview_dialog_display_options_frame_base.fbp
index 1eafe8b81..5581a3d0a 100644
--- a/gerbview/dialogs/gerbview_dialog_display_options_frame_base.fbp
+++ b/gerbview/dialogs/gerbview_dialog_display_options_frame_base.fbp
@@ -99,9 +99,9 @@
                     <property name="proportion">1</property>
                     <object class="wxBoxSizer" expanded="1">
                         <property name="minimum_size"></property>
-                        <property name="name">bUpperSizer</property>
+                        <property name="name">m_UpperSizer</property>
                         <property name="orient">wxHORIZONTAL</property>
-                        <property name="permission">none</property>
+                        <property name="permission">protected</property>
                         <object class="sizeritem" expanded="1">
                             <property name="border">5</property>
                             <property name="flag">wxALL|wxEXPAND</property>
@@ -309,7 +309,7 @@
                                         <property name="caption"></property>
                                         <property name="caption_visible">1</property>
                                         <property name="center_pane">0</property>
-                                        <property name="choices">&quot;Small cross&quot; &quot;Full screen cursor&quot;</property>
+                                        <property name="choices">&quot;Sketch&quot; &quot;Filled&quot;</property>
                                         <property name="close_button">1</property>
                                         <property name="context_help"></property>
                                         <property name="context_menu">1</property>
@@ -324,7 +324,7 @@
                                         <property name="gripper">0</property>
                                         <property name="hidden">0</property>
                                         <property name="id">wxID_ANY</property>
-                                        <property name="label">Cursor</property>
+                                        <property name="label">Flashed items</property>
                                         <property name="majorDimension">1</property>
                                         <property name="max_size"></property>
                                         <property name="maximize_button">0</property>
@@ -333,7 +333,7 @@
                                         <property name="minimize_button">0</property>
                                         <property name="minimum_size"></property>
                                         <property name="moveable">1</property>
-                                        <property name="name">m_CursorShape</property>
+                                        <property name="name">m_OptDisplayFlashedItems</property>
                                         <property name="pane_border">1</property>
                                         <property name="pane_position"></property>
                                         <property name="pane_size"></property>
@@ -383,105 +383,6 @@
                                 </object>
                                 <object class="sizeritem" expanded="1">
                                     <property name="border">5</property>
-                                    <property name="flag">wxALL</property>
-                                    <property name="proportion">0</property>
-                                    <object class="wxCheckBox" expanded="1">
-                                        <property name="BottomDockable">1</property>
-                                        <property name="LeftDockable">1</property>
-                                        <property name="RightDockable">1</property>
-                                        <property name="TopDockable">1</property>
-                                        <property name="aui_layer"></property>
-                                        <property name="aui_name"></property>
-                                        <property name="aui_position"></property>
-                                        <property name="aui_row"></property>
-                                        <property name="best_size"></property>
-                                        <property name="bg"></property>
-                                        <property name="caption"></property>
-                                        <property name="caption_visible">1</property>
-                                        <property name="center_pane">0</property>
-                                        <property name="checked">1</property>
-                                        <property name="close_button">1</property>
-                                        <property name="context_help"></property>
-                                        <property name="context_menu">1</property>
-                                        <property name="default_pane">0</property>
-                                        <property name="dock">Dock</property>
-                                        <property name="dock_fixed">0</property>
-                                        <property name="docking">Left</property>
-                                        <property name="enabled">1</property>
-                                        <property name="fg"></property>
-                                        <property name="floatable">1</property>
-                                        <property name="font"></property>
-                                        <property name="gripper">0</property>
-                                        <property name="hidden">0</property>
-                                        <property name="id">wxID_ANY</property>
-                                        <property name="label">Show D codes</property>
-                                        <property name="max_size"></property>
-                                        <property name="maximize_button">0</property>
-                                        <property name="maximum_size"></property>
-                                        <property name="min_size"></property>
-                                        <property name="minimize_button">0</property>
-                                        <property name="minimum_size"></property>
-                                        <property name="moveable">1</property>
-                                        <property name="name">m_OptDisplayDCodes</property>
-                                        <property name="pane_border">1</property>
-                                        <property name="pane_position"></property>
-                                        <property name="pane_size"></property>
-                                        <property name="permission">protected</property>
-                                        <property name="pin_button">1</property>
-                                        <property name="pos"></property>
-                                        <property name="resize">Resizable</property>
-                                        <property name="show">1</property>
-                                        <property name="size"></property>
-                                        <property name="style"></property>
-                                        <property name="subclass"></property>
-                                        <property name="toolbar_pane">0</property>
-                                        <property name="tooltip"></property>
-                                        <property name="validator_data_type"></property>
-                                        <property name="validator_style">wxFILTER_NONE</property>
-                                        <property name="validator_type">wxDefaultValidator</property>
-                                        <property name="validator_variable"></property>
-                                        <property name="window_extra_style"></property>
-                                        <property name="window_name"></property>
-                                        <property name="window_style"></property>
-                                        <event name="OnChar"></event>
-                                        <event name="OnCheckBox"></event>
-                                        <event name="OnEnterWindow"></event>
-                                        <event name="OnEraseBackground"></event>
-                                        <event name="OnKeyDown"></event>
-                                        <event name="OnKeyUp"></event>
-                                        <event name="OnKillFocus"></event>
-                                        <event name="OnLeaveWindow"></event>
-                                        <event name="OnLeftDClick"></event>
-                                        <event name="OnLeftDown"></event>
-                                        <event name="OnLeftUp"></event>
-                                        <event name="OnMiddleDClick"></event>
-                                        <event name="OnMiddleDown"></event>
-                                        <event name="OnMiddleUp"></event>
-                                        <event name="OnMotion"></event>
-                                        <event name="OnMouseEvents"></event>
-                                        <event name="OnMouseWheel"></event>
-                                        <event name="OnPaint"></event>
-                                        <event name="OnRightDClick"></event>
-                                        <event name="OnRightDown"></event>
-                                        <event name="OnRightUp"></event>
-                                        <event name="OnSetFocus"></event>
-                                        <event name="OnSize"></event>
-                                        <event name="OnUpdateUI"></event>
-                                    </object>
-                                </object>
-                            </object>
-                        </object>
-                        <object class="sizeritem" expanded="1">
-                            <property name="border">5</property>
-                            <property name="flag">wxALL|wxEXPAND</property>
-                            <property name="proportion">1</property>
-                            <object class="wxBoxSizer" expanded="1">
-                                <property name="minimum_size"></property>
-                                <property name="name">bMiddleSizer</property>
-                                <property name="orient">wxVERTICAL</property>
-                                <property name="permission">none</property>
-                                <object class="sizeritem" expanded="1">
-                                    <property name="border">5</property>
                                     <property name="flag">wxALL|wxEXPAND</property>
                                     <property name="proportion">0</property>
                                     <object class="wxRadioBox" expanded="1">
@@ -603,7 +504,7 @@
                                         <property name="gripper">0</property>
                                         <property name="hidden">0</property>
                                         <property name="id">wxID_ANY</property>
-                                        <property name="label">Flashed items</property>
+                                        <property name="label">Polygons</property>
                                         <property name="majorDimension">1</property>
                                         <property name="max_size"></property>
                                         <property name="maximize_button">0</property>
@@ -612,7 +513,7 @@
                                         <property name="minimize_button">0</property>
                                         <property name="minimum_size"></property>
                                         <property name="moveable">1</property>
-                                        <property name="name">m_OptDisplayFlashedItems</property>
+                                        <property name="name">m_OptDisplayPolygons</property>
                                         <property name="pane_border">1</property>
                                         <property name="pane_position"></property>
                                         <property name="pane_size"></property>
@@ -662,9 +563,9 @@
                                 </object>
                                 <object class="sizeritem" expanded="1">
                                     <property name="border">5</property>
-                                    <property name="flag">wxALL|wxEXPAND</property>
+                                    <property name="flag">wxALL</property>
                                     <property name="proportion">0</property>
-                                    <object class="wxRadioBox" expanded="1">
+                                    <object class="wxCheckBox" expanded="1">
                                         <property name="BottomDockable">1</property>
                                         <property name="LeftDockable">1</property>
                                         <property name="RightDockable">1</property>
@@ -678,7 +579,7 @@
                                         <property name="caption"></property>
                                         <property name="caption_visible">1</property>
                                         <property name="center_pane">0</property>
-                                        <property name="choices">&quot;Sketch&quot; &quot;Filled&quot;</property>
+                                        <property name="checked">1</property>
                                         <property name="close_button">1</property>
                                         <property name="context_help"></property>
                                         <property name="context_menu">1</property>
@@ -693,8 +594,7 @@
                                         <property name="gripper">0</property>
                                         <property name="hidden">0</property>
                                         <property name="id">wxID_ANY</property>
-                                        <property name="label">Polygons</property>
-                                        <property name="majorDimension">1</property>
+                                        <property name="label">Show D codes</property>
                                         <property name="max_size"></property>
                                         <property name="maximize_button">0</property>
                                         <property name="maximum_size"></property>
@@ -702,7 +602,7 @@
                                         <property name="minimize_button">0</property>
                                         <property name="minimum_size"></property>
                                         <property name="moveable">1</property>
-                                        <property name="name">m_OptDisplayPolygons</property>
+                                        <property name="name">m_OptDisplayDCodes</property>
                                         <property name="pane_border">1</property>
                                         <property name="pane_position"></property>
                                         <property name="pane_size"></property>
@@ -710,10 +610,9 @@
                                         <property name="pin_button">1</property>
                                         <property name="pos"></property>
                                         <property name="resize">Resizable</property>
-                                        <property name="selection">1</property>
                                         <property name="show">1</property>
                                         <property name="size"></property>
-                                        <property name="style">wxRA_SPECIFY_COLS</property>
+                                        <property name="style"></property>
                                         <property name="subclass"></property>
                                         <property name="toolbar_pane">0</property>
                                         <property name="tooltip"></property>
@@ -725,6 +624,7 @@
                                         <property name="window_name"></property>
                                         <property name="window_style"></property>
                                         <event name="OnChar"></event>
+                                        <event name="OnCheckBox"></event>
                                         <event name="OnEnterWindow"></event>
                                         <event name="OnEraseBackground"></event>
                                         <event name="OnKeyDown"></event>
@@ -741,7 +641,6 @@
                                         <event name="OnMouseEvents"></event>
                                         <event name="OnMouseWheel"></event>
                                         <event name="OnPaint"></event>
-                                        <event name="OnRadioBox"></event>
                                         <event name="OnRightDClick"></event>
                                         <event name="OnRightDown"></event>
                                         <event name="OnRightUp"></event>
diff --git a/gerbview/dialogs/gerbview_dialog_display_options_frame_base.h b/gerbview/dialogs/gerbview_dialog_display_options_frame_base.h
index d9eae9242..a24010777 100644
--- a/gerbview/dialogs/gerbview_dialog_display_options_frame_base.h
+++ b/gerbview/dialogs/gerbview_dialog_display_options_frame_base.h
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version May  6 2016)
+// C++ code generated with wxFormBuilder (version Nov 30 2016)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO "NOT" EDIT THIS FILE!
@@ -38,13 +38,13 @@ class DIALOG_DISPLAY_OPTIONS_BASE : public DIALOG_SHIM
 	private:
 	
 	protected:
+		wxBoxSizer* m_UpperSizer;
 		wxRadioBox* m_PolarDisplay;
 		wxRadioBox* m_BoxUnits;
-		wxRadioBox* m_CursorShape;
-		wxCheckBox* m_OptDisplayDCodes;
-		wxRadioBox* m_OptDisplayLines;
 		wxRadioBox* m_OptDisplayFlashedItems;
+		wxRadioBox* m_OptDisplayLines;
 		wxRadioBox* m_OptDisplayPolygons;
+		wxCheckBox* m_OptDisplayDCodes;
 		wxRadioBox* m_ShowPageLimits;
 		wxCheckBox* m_OptZoomNoCenter;
 		wxCheckBox* m_OptMousewheelPan;
diff --git a/gerbview/draw_gerber_screen.cpp b/gerbview/draw_gerber_screen.cpp
index 64c630c4a..f1a7ea6bc 100644
--- a/gerbview/draw_gerber_screen.cpp
+++ b/gerbview/draw_gerber_screen.cpp
@@ -135,7 +135,16 @@ void GERBVIEW_FRAME::RedrawActiveWindow( wxDC* DC, bool EraseBg )
     if( IsShown() )
     {
         m_overlay.Reset();
-        wxDCOverlay overlaydc( m_overlay, (wxWindowDC*)DC );
+
+        // On macOS, the call to create overlaydc fails for some reason due to
+        // the DC having zero size initially.
+        wxCoord w = 0, h = 0;
+        ( (wxWindowDC*)DC )->GetSize( &w, &h );
+        if( w == 0 || h == 0)
+        {
+            w = h = 1;
+        }
+        wxDCOverlay overlaydc( m_overlay, (wxWindowDC*)DC, 0, 0, 1, 1 );
         overlaydc.Clear();
     }
 #endif
diff --git a/gerbview/events_called_functions.cpp b/gerbview/events_called_functions.cpp
index 4e7021901..310815279 100644
--- a/gerbview/events_called_functions.cpp
+++ b/gerbview/events_called_functions.cpp
@@ -43,6 +43,10 @@
 #include <class_gerbview_layer_widget.h>
 #include <dialog_show_page_borders.h>
 
+#include <tool/tool_manager.h>
+#include <gerbview_painter.h>
+#include <view/view.h>
+
 
 // Event table:
 
@@ -83,6 +87,12 @@ BEGIN_EVENT_TABLE( GERBVIEW_FRAME, EDA_DRAW_FRAME )
     EVT_MENU( ID_MENU_GERBVIEW_SHOW_HIDE_LAYERS_MANAGER_DIALOG,
               GERBVIEW_FRAME::OnSelectOptionToolbar )
     EVT_MENU( wxID_PREFERENCES, GERBVIEW_FRAME::InstallGerberOptionsDialog )
+    EVT_UPDATE_UI( ID_MENU_CANVAS_LEGACY, GERBVIEW_FRAME::OnUpdateSwitchCanvas )
+    EVT_UPDATE_UI( ID_MENU_CANVAS_CAIRO, GERBVIEW_FRAME::OnUpdateSwitchCanvas )
+    EVT_UPDATE_UI( ID_MENU_CANVAS_OPENGL, GERBVIEW_FRAME::OnUpdateSwitchCanvas )
+    EVT_MENU( ID_MENU_CANVAS_LEGACY, GERBVIEW_FRAME::SwitchCanvas )
+    EVT_MENU( ID_MENU_CANVAS_CAIRO, GERBVIEW_FRAME::SwitchCanvas )
+    EVT_MENU( ID_MENU_CANVAS_OPENGL, GERBVIEW_FRAME::SwitchCanvas )
 
     // menu Postprocess
     EVT_MENU( ID_GERBVIEW_SHOW_LIST_DCODES, GERBVIEW_FRAME::Process_Special_Functions )
@@ -115,6 +125,7 @@ BEGIN_EVENT_TABLE( GERBVIEW_FRAME, EDA_DRAW_FRAME )
     // Option toolbar
     //EVT_TOOL( ID_NO_TOOL_SELECTED, GERBVIEW_FRAME::Process_Special_Functions ) // mentioned below
     EVT_TOOL( ID_ZOOM_SELECTION, GERBVIEW_FRAME::Process_Special_Functions )
+    EVT_TOOL( ID_TB_MEASUREMENT_TOOL, GERBVIEW_FRAME::Process_Special_Functions )
     EVT_TOOL( ID_TB_OPTIONS_SHOW_POLAR_COORD, GERBVIEW_FRAME::OnSelectOptionToolbar )
     EVT_TOOL( ID_TB_OPTIONS_SHOW_POLYGONS_SKETCH, GERBVIEW_FRAME::OnSelectOptionToolbar )
     EVT_TOOL( ID_TB_OPTIONS_SHOW_FLASHED_ITEMS_SKETCH, GERBVIEW_FRAME::OnSelectOptionToolbar )
@@ -125,11 +136,13 @@ BEGIN_EVENT_TABLE( GERBVIEW_FRAME, EDA_DRAW_FRAME )
     EVT_TOOL( ID_TB_OPTIONS_SHOW_NEGATIVE_ITEMS, GERBVIEW_FRAME::OnSelectOptionToolbar )
     EVT_TOOL_RANGE( ID_TB_OPTIONS_SHOW_GBR_MODE_0, ID_TB_OPTIONS_SHOW_GBR_MODE_2,
                     GERBVIEW_FRAME::OnSelectDisplayMode )
+    EVT_TOOL( ID_TB_OPTIONS_DIFF_MODE, GERBVIEW_FRAME::OnSelectOptionToolbar )
+    EVT_TOOL( ID_TB_OPTIONS_HIGH_CONTRAST_MODE, GERBVIEW_FRAME::OnSelectOptionToolbar )
 
     // Auxiliary horizontal toolbar
-    EVT_CHOICE( ID_GBR_AUX_TOOLBAR_PCB_CMP_CHOICE, GERBVIEW_FRAME::Process_Special_Functions )
-    EVT_CHOICE( ID_GBR_AUX_TOOLBAR_PCB_NET_CHOICE, GERBVIEW_FRAME::Process_Special_Functions )
-    EVT_CHOICE( ID_GBR_AUX_TOOLBAR_PCB_APERATTRIBUTES_CHOICE, GERBVIEW_FRAME::Process_Special_Functions )
+    EVT_CHOICE( ID_GBR_AUX_TOOLBAR_PCB_CMP_CHOICE, GERBVIEW_FRAME::OnSelectHighlightChoice )
+    EVT_CHOICE( ID_GBR_AUX_TOOLBAR_PCB_NET_CHOICE, GERBVIEW_FRAME::OnSelectHighlightChoice )
+    EVT_CHOICE( ID_GBR_AUX_TOOLBAR_PCB_APERATTRIBUTES_CHOICE, GERBVIEW_FRAME::OnSelectHighlightChoice )
 
     // Right click context menu
     EVT_MENU( ID_HIGHLIGHT_CMP_ITEMS, GERBVIEW_FRAME::Process_Special_Functions )
@@ -139,6 +152,7 @@ BEGIN_EVENT_TABLE( GERBVIEW_FRAME, EDA_DRAW_FRAME )
 
     EVT_UPDATE_UI( ID_NO_TOOL_SELECTED, GERBVIEW_FRAME::OnUpdateSelectTool )
     EVT_UPDATE_UI( ID_ZOOM_SELECTION, GERBVIEW_FRAME::OnUpdateSelectTool )
+    EVT_UPDATE_UI( ID_TB_MEASUREMENT_TOOL, GERBVIEW_FRAME::OnUpdateSelectTool )
     EVT_UPDATE_UI( ID_TB_OPTIONS_SHOW_POLAR_COORD, GERBVIEW_FRAME::OnUpdateCoordType )
     EVT_UPDATE_UI( ID_TB_OPTIONS_SHOW_FLASHED_ITEMS_SKETCH,
                    GERBVIEW_FRAME::OnUpdateFlashedItemsDrawMode )
@@ -223,6 +237,10 @@ void GERBVIEW_FRAME::Process_Special_Functions( wxCommandEvent& event )
             SetNoToolSelected();
         break;
 
+    case ID_TB_MEASUREMENT_TOOL:
+        SetToolID( id, wxCURSOR_DEFAULT, _( "Unsupported tool in this canvas" ) );
+        break;
+
     case ID_POPUP_CLOSE_CURRENT_TOOL:
         SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
         break;
@@ -246,12 +264,6 @@ void GERBVIEW_FRAME::Process_Special_Functions( wxCommandEvent& event )
         HandleBlockEnd( &dc );
         break;
 
-    case ID_GBR_AUX_TOOLBAR_PCB_CMP_CHOICE:
-    case ID_GBR_AUX_TOOLBAR_PCB_NET_CHOICE:
-    case ID_GBR_AUX_TOOLBAR_PCB_APERATTRIBUTES_CHOICE:
-            m_canvas->Refresh();
-        break;
-
     case ID_HIGHLIGHT_CMP_ITEMS:
         if( m_SelComponentBox->SetStringSelection( currItem->GetNetAttributes().m_Cmpref ) )
             m_canvas->Refresh();
@@ -275,8 +287,8 @@ void GERBVIEW_FRAME::Process_Special_Functions( wxCommandEvent& event )
         m_SelNetnameBox->SetSelection( 0 );
         m_SelAperAttributesBox->SetSelection( 0 );
 
-        if( GetGbrImage( getActiveLayer() ) )
-            GetGbrImage( getActiveLayer() )->m_Selected_Tool = 0;
+        if( GetGbrImage( GetActiveLayer() ) )
+            GetGbrImage( GetActiveLayer() )->m_Selected_Tool = 0;
 
         m_canvas->Refresh();
         break;
@@ -288,9 +300,40 @@ void GERBVIEW_FRAME::Process_Special_Functions( wxCommandEvent& event )
 }
 
 
+// Handles the changing of the highlighted component/net/attribute
+void GERBVIEW_FRAME::OnSelectHighlightChoice( wxCommandEvent& event )
+{
+    if( IsGalCanvasActive() )
+    {
+        auto settings = static_cast<KIGFX::GERBVIEW_PAINTER*>( GetGalCanvas()->GetView()->GetPainter() )->GetSettings();
+
+        switch( event.GetId() )
+        {
+        case ID_GBR_AUX_TOOLBAR_PCB_CMP_CHOICE:
+            settings->m_componentHighlightString = m_SelComponentBox->GetStringSelection();
+            break;
+
+        case ID_GBR_AUX_TOOLBAR_PCB_NET_CHOICE:
+            settings->m_netHighlightString = m_SelNetnameBox->GetStringSelection();
+            break;
+
+        case ID_GBR_AUX_TOOLBAR_PCB_APERATTRIBUTES_CHOICE:
+            settings->m_attributeHighlightString = m_SelAperAttributesBox->GetStringSelection();
+            break;
+
+        }
+
+        GetGalCanvas()->GetView()->RecacheAllItems();
+        GetGalCanvas()->Refresh();
+    }
+    else
+        m_canvas->Refresh();
+}
+
+
 void GERBVIEW_FRAME::OnSelectActiveDCode( wxCommandEvent& event )
 {
-    GERBER_FILE_IMAGE* gerber_image = GetGbrImage( getActiveLayer() );
+    GERBER_FILE_IMAGE* gerber_image = GetGbrImage( GetActiveLayer() );
 
     if( gerber_image )
     {
@@ -307,11 +350,11 @@ void GERBVIEW_FRAME::OnSelectActiveDCode( wxCommandEvent& event )
 
 void GERBVIEW_FRAME::OnSelectActiveLayer( wxCommandEvent& event )
 {
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
 
-    setActiveLayer( event.GetSelection() );
+    SetActiveLayer( event.GetSelection() );
 
-    if( layer != getActiveLayer() )
+    if( layer != GetActiveLayer() )
     {
         if( m_LayersManager->OnLayerSelected() )
             m_canvas->Refresh();
@@ -321,7 +364,7 @@ void GERBVIEW_FRAME::OnSelectActiveLayer( wxCommandEvent& event )
 
 void GERBVIEW_FRAME::OnShowGerberSourceFile( wxCommandEvent& event )
 {
-    int     layer = getActiveLayer();
+    int     layer = GetActiveLayer();
     GERBER_FILE_IMAGE* gerber_layer = GetGbrImage( layer );
 
     if( gerber_layer )
@@ -427,16 +470,19 @@ void GERBVIEW_FRAME::OnSelectOptionToolbar( wxCommandEvent& event )
 
     case ID_TB_OPTIONS_SHOW_FLASHED_ITEMS_SKETCH:
         m_DisplayOptions.m_DisplayFlashedItemsFill = not state;
+        applyDisplaySettingsToGAL();
         m_canvas->Refresh( true );
         break;
 
     case ID_TB_OPTIONS_SHOW_LINES_SKETCH:
         m_DisplayOptions.m_DisplayLinesFill = not state;
+        applyDisplaySettingsToGAL();
         m_canvas->Refresh( true );
         break;
 
     case ID_TB_OPTIONS_SHOW_POLYGONS_SKETCH:
         m_DisplayOptions.m_DisplayPolygonsFill = not state;
+        applyDisplaySettingsToGAL();
         m_canvas->Refresh( true );
         break;
 
@@ -450,6 +496,18 @@ void GERBVIEW_FRAME::OnSelectOptionToolbar( wxCommandEvent& event )
         m_canvas->Refresh( true );
         break;
 
+    case ID_TB_OPTIONS_DIFF_MODE:
+        m_DisplayOptions.m_DiffMode = state;
+        applyDisplaySettingsToGAL();
+        m_canvas->Refresh( true );
+        break;
+
+    case ID_TB_OPTIONS_HIGH_CONTRAST_MODE:
+        m_DisplayOptions.m_HighContrastMode = state;
+        applyDisplaySettingsToGAL();
+        m_canvas->Refresh( true );
+        break;
+
     case ID_TB_OPTIONS_SHOW_LAYERS_MANAGER_VERTICAL_TOOLBAR:
 
         // show/hide auxiliary Vertical layers and visibility manager toolbar
@@ -461,6 +519,11 @@ void GERBVIEW_FRAME::OnSelectOptionToolbar( wxCommandEvent& event )
                                 _("Hide &Layers Manager" ) : _("Show &Layers Manager" ));
         break;
 
+    // collect GAL-only tools here:
+    case ID_TB_MEASUREMENT_TOOL:
+        SetToolID( id, wxCURSOR_DEFAULT, _( "Unsupported tool in this canvas" ) );
+        break;
+
     default:
         wxMessageBox( wxT( "GERBVIEW_FRAME::OnSelectOptionToolbar error" ) );
         break;
@@ -472,3 +535,62 @@ void GERBVIEW_FRAME::OnUpdateSelectTool( wxUpdateUIEvent& aEvent )
 {
     aEvent.Check( GetToolId() == aEvent.GetId() );
 }
+
+
+void GERBVIEW_FRAME::SwitchCanvas( wxCommandEvent& aEvent )
+{
+    bool use_gal = false;
+    EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
+
+    switch( aEvent.GetId() )
+    {
+    case ID_MENU_CANVAS_LEGACY:
+        break;
+
+    case ID_MENU_CANVAS_CAIRO:
+        use_gal = GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO );
+
+        if( use_gal )
+            canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO;
+        break;
+
+    case ID_MENU_CANVAS_OPENGL:
+        use_gal = GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL );
+
+        if( use_gal )
+            canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
+        break;
+    }
+
+    SaveCanvasTypeSetting( canvasType );
+    UseGalCanvas( use_gal );
+    wxUpdateUIEvent e;
+    OnUpdateSwitchCanvas( e );
+}
+
+
+void GERBVIEW_FRAME::OnUpdateSwitchCanvas( wxUpdateUIEvent& aEvent )
+{
+    wxMenuBar* menuBar = GetMenuBar();
+    EDA_DRAW_PANEL_GAL* gal_canvas = GetGalCanvas();
+    EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
+
+    if( IsGalCanvasActive() && gal_canvas )
+        canvasType = gal_canvas->GetBackend();
+
+    struct { int menuId; int galType; } menuList[] =
+    {
+        { ID_MENU_CANVAS_LEGACY,    EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE },
+        { ID_MENU_CANVAS_OPENGL,    EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL },
+        { ID_MENU_CANVAS_CAIRO,     EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO },
+    };
+
+    for( auto ii: menuList )
+    {
+        wxMenuItem* item = menuBar->FindItem( ii.menuId );
+        if( ii.galType == canvasType )
+        {
+            item->Check( true );
+        }
+    }
+}
diff --git a/gerbview/excellon_read_drill_file.cpp b/gerbview/excellon_read_drill_file.cpp
index 458e95370..b916f3677 100644
--- a/gerbview/excellon_read_drill_file.cpp
+++ b/gerbview/excellon_read_drill_file.cpp
@@ -73,6 +73,7 @@
 #include <class_excellon.h>
 #include <kicad_string.h>
 #include <class_X2_gerber_attributes.h>
+#include <view/view.h>
 
 #include <cmath>
 
@@ -160,7 +161,7 @@ static EXCELLON_CMD excellon_G_CmdList[] =
 bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName )
 {
     wxString msg;
-    int layerId = getActiveLayer();      // current layer used in GerbView
+    int layerId = GetActiveLayer();      // current layer used in GerbView
     GERBER_FILE_IMAGE_LIST* images = GetGerberLayout()->GetImagesList();
     EXCELLON_IMAGE* drill_Layer = (EXCELLON_IMAGE*) images->GetGbrImage( layerId );
 
@@ -193,6 +194,21 @@ bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName )
         dlg.ListSet( drill_Layer->GetMessages() );
         dlg.ShowModal();
     }
+
+    // TODO(JE) Is this the best place to add items to the view?
+    if( success )
+    {
+        EDA_DRAW_PANEL_GAL* canvas = GetGalCanvas();
+        if( canvas )
+        {
+            KIGFX::VIEW* view = canvas->GetView();
+            for( GERBER_DRAW_ITEM* item = drill_Layer->GetItemsList(); item; item = item->Next() )
+            {
+                view->Add( (KIGFX::VIEW_ITEM*) item );
+            }
+        }
+    }
+
     return success;
 }
 
@@ -494,7 +510,7 @@ bool EXCELLON_IMAGE::readToolInformation( char*& aText )
 
     // Initialize Dcode to handle this Tool
     // Remember: dcodes are >= FIRST_DCODE
-    D_CODE* dcode = GetDCODE( iprm + FIRST_DCODE );
+    D_CODE* dcode = GetDCODEOrCreate( iprm + FIRST_DCODE );
 
     if( dcode == NULL )
         return false;
@@ -532,7 +548,7 @@ bool EXCELLON_IMAGE::Execute_Drill_Command( char*& text )
                 Execute_EXCELLON_G_Command( text );
                 break;
             case 0:     // E.O.L: execute command
-                tool = GetDCODE( m_Current_Tool, false );
+                tool = GetDCODE( m_Current_Tool );
 
                 if( !tool )
                 {
@@ -593,13 +609,13 @@ bool EXCELLON_IMAGE::Select_Tool( char*& text )
             dcode_id = TOOLS_MAX_COUNT - 1;
 
         m_Current_Tool = dcode_id;
-        D_CODE* currDcode = GetDCODE( dcode_id , false );
+        D_CODE* currDcode = GetDCODEOrCreate( dcode_id, true );
 
         if( currDcode == NULL && tool_id > 0 )   // if the definition is embedded, enter it
         {
             text = startline;   // text starts at the beginning of the command
             readToolInformation( text );
-            currDcode = GetDCODE( dcode_id , false );
+            currDcode = GetDCODE( dcode_id );
         }
 
         if( currDcode )
diff --git a/gerbview/files.cpp b/gerbview/files.cpp
index 3562a2948..3634ac705 100644
--- a/gerbview/files.cpp
+++ b/gerbview/files.cpp
@@ -222,7 +222,7 @@ bool GERBVIEW_FRAME::LoadGerberFiles( const wxString& aFullFileName )
 
     // Read gerber files: each file is loaded on a new GerbView layer
     bool success = true;
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
 
     // Manage errors when loading files
     wxString msg;
@@ -237,7 +237,7 @@ bool GERBVIEW_FRAME::LoadGerberFiles( const wxString& aFullFileName )
 
         m_lastFileName = filename.GetFullPath();
 
-        setActiveLayer( layer, false );
+        SetActiveLayer( layer, false );
 
         if( Read_GERBER_File( filename.GetFullPath() ) )
         {
@@ -263,7 +263,7 @@ bool GERBVIEW_FRAME::LoadGerberFiles( const wxString& aFullFileName )
                 break;
             }
 
-            setActiveLayer( layer, false );
+            SetActiveLayer( layer, false );
         }
     }
 
@@ -278,7 +278,7 @@ bool GERBVIEW_FRAME::LoadGerberFiles( const wxString& aFullFileName )
 
     // Synchronize layers tools with actual active layer:
     ReFillLayerWidget();
-    setActiveLayer( getActiveLayer() );
+    SetActiveLayer( GetActiveLayer() );
     m_LayersManager->UpdateLayerIcons();
     syncLayerBox();
     return success;
@@ -325,7 +325,7 @@ bool GERBVIEW_FRAME::LoadExcellonFiles( const wxString& aFullFileName )
 
     // Read Excellon drill files: each file is loaded on a new GerbView layer
     bool success = true;
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
 
     // Manage errors when loading files
     wxString msg;
@@ -340,7 +340,7 @@ bool GERBVIEW_FRAME::LoadExcellonFiles( const wxString& aFullFileName )
 
         m_lastFileName = filename.GetFullPath();
 
-        setActiveLayer( layer, false );
+        SetActiveLayer( layer, false );
 
         if( Read_EXCELLON_File( filename.GetFullPath() ) )
         {
@@ -367,7 +367,7 @@ bool GERBVIEW_FRAME::LoadExcellonFiles( const wxString& aFullFileName )
                 break;
             }
 
-            setActiveLayer( layer, false );
+            SetActiveLayer( layer, false );
         }
     }
 
@@ -382,7 +382,7 @@ bool GERBVIEW_FRAME::LoadExcellonFiles( const wxString& aFullFileName )
 
     // Synchronize layers tools with actual active layer:
     ReFillLayerWidget();
-    setActiveLayer( getActiveLayer() );
+    SetActiveLayer( GetActiveLayer() );
     m_LayersManager->UpdateLayerIcons();
     syncLayerBox();
 
@@ -454,7 +454,7 @@ bool GERBVIEW_FRAME::unarchiveFiles( const wxString& aFullFileName, REPORTER* aR
             continue;
         }
 
-        int layer = getActiveLayer();
+        int layer = GetActiveLayer();
 
         if( layer == NO_AVAILABLE_LAYERS )
         {
@@ -531,7 +531,7 @@ bool GERBVIEW_FRAME::unarchiveFiles( const wxString& aFullFileName, REPORTER* aR
                 gerber_image->m_FileName = fname;
 
             layer = getNextAvailableLayer( layer );
-            setActiveLayer( layer, false );
+            SetActiveLayer( layer, false );
         }
     }
 
@@ -584,7 +584,7 @@ bool GERBVIEW_FRAME::LoadZipArchiveFile( const wxString& aFullFileName )
 
     // Synchronize layers tools with actual active layer:
     ReFillLayerWidget();
-    setActiveLayer( getActiveLayer() );
+    SetActiveLayer( GetActiveLayer() );
     m_LayersManager->UpdateLayerIcons();
     syncLayerBox();
 
diff --git a/gerbview/gerbview_config.cpp b/gerbview/gerbview_config.cpp
index 8b52e1a19..e233fcaab 100644
--- a/gerbview/gerbview_config.cpp
+++ b/gerbview/gerbview_config.cpp
@@ -92,6 +92,11 @@ PARAM_CFG_ARRAY& GERBVIEW_FRAME::GetConfigurationSettings()
                                                         &g_ColorsSettings.m_LayersColors[
                                                             LAYER_NEGATIVE_OBJECTS],
                                                         DARKGRAY ) );
+    m_configSettings.push_back( new PARAM_CFG_SETCOLOR( true,
+                                                        wxT( "GridColorEx" ),
+                                                        &g_ColorsSettings.m_LayersColors[
+                                                            LAYER_GERBVIEW_GRID],
+                                                        DARKGRAY ) );
     m_configSettings.push_back( new PARAM_CFG_BOOL( true,
                                                     wxT( "DisplayPolarCoordinates" ),
                                                     &m_DisplayOptions.m_DisplayPolarCood,
@@ -129,7 +134,7 @@ PARAM_CFG_ARRAY& GERBVIEW_FRAME::GetConfigurationSettings()
 
     for( unsigned i = 0; i < DIM(keys);  ++i )
     {
-        COLOR4D* prm = &g_ColorsSettings.m_LayersColors[i];
+        COLOR4D* prm = &g_ColorsSettings.m_LayersColors[ GERBER_DRAW_LAYER( i ) ];
 
         PARAM_CFG_SETCOLOR* prm_entry =
             new PARAM_CFG_SETCOLOR( true, keys[i], prm, color_default[i] );
diff --git a/gerbview/gerbview_frame.cpp b/gerbview/gerbview_frame.cpp
index fbb669865..9f1539267 100644
--- a/gerbview/gerbview_frame.cpp
+++ b/gerbview/gerbview_frame.cpp
@@ -49,6 +49,14 @@
 #include <class_DCodeSelectionbox.h>
 #include <class_gerbview_layer_widget.h>
 
+#include <gerbview_draw_panel_gal.h>
+#include <gal/graphics_abstraction_layer.h>
+#include <tool/tool_manager.h>
+#include <tool/tool_dispatcher.h>
+#include <tools/gerbview_actions.h>
+#include <view/view.h>
+#include <gerbview_painter.h>
+
 
 // Config keywords
 static const wxString   cfgShowPageSizeOption( wxT( "PageSizeOpt" ) );
@@ -56,6 +64,8 @@ static const wxString   cfgShowDCodes( wxT( "ShowDCodesOpt" ) );
 static const wxString   cfgShowNegativeObjects( wxT( "ShowNegativeObjectsOpt" ) );
 static const wxString   cfgShowBorderAndTitleBlock( wxT( "ShowBorderAndTitleBlock" ) );
 
+const wxChar GERBVIEW_FRAME::CANVAS_TYPE_KEY[] = wxT( "canvas_type" );
+
 
 // Colors for layers and items
 COLORS_DESIGN_SETTINGS g_ColorsSettings( FRAME_GERBER );
@@ -87,6 +97,16 @@ GERBVIEW_FRAME::GERBVIEW_FRAME( KIWAY* aKiway, wxWindow* aParent ):
     m_zipFileHistory.SetBaseId( ID_GERBVIEW_ZIP_FILE1 );
     m_jobFileHistory.SetBaseId( ID_GERBVIEW_JOB_FILE1 );
 
+    EDA_DRAW_PANEL_GAL* galCanvas = new GERBVIEW_DRAW_PANEL_GAL( this, -1, wxPoint( 0, 0 ),
+                                            m_FrameSize,
+                                            GetGalDisplayOptions(),
+                                            EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE );
+
+    SetGalCanvas( galCanvas );
+
+    // GerbView requires draw priority for rendering negative objects
+    galCanvas->GetView()->UseDrawPriority( true );
+
     if( m_canvas )
         m_canvas->SetEnableBlockCommands( true );
 
@@ -174,22 +194,59 @@ GERBVIEW_FRAME::GERBVIEW_FRAME( KIWAY* aKiway, wxWindow* aParent ):
         m_auimgr.AddPane( m_canvas,
                           wxAuiPaneInfo().Name( wxT( "DrawFrame" ) ).CentrePane() );
 
+    if( GetGalCanvas() )
+        m_auimgr.AddPane( (wxWindow*) GetGalCanvas(),
+                          wxAuiPaneInfo().Name( wxT( "DrawFrameGal" ) ).CentrePane().Hide() );
+
     if( m_messagePanel )
         m_auimgr.AddPane( m_messagePanel,
                           wxAuiPaneInfo( mesg ).Name( wxT( "MsgPanel" ) ).Bottom().Layer( 10 ) );
 
     ReFillLayerWidget();                // this is near end because contents establish size
-    m_LayersManager->ReFillRender();    // Update colors in Render after the config is read
     m_auimgr.Update();
 
-    setActiveLayer( 0, true );
+    setupTools();
+
+    SetActiveLayer( 0, true );
     Zoom_Automatique( false );           // Gives a default zoom value
     UpdateTitleAndInfo();
+
+    EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = LoadCanvasTypeSetting();
+
+    if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE )
+    {
+        if( GetGalCanvas()->SwitchBackend( canvasType ) )
+            UseGalCanvas( true );
+        wxUpdateUIEvent e;
+        OnUpdateSwitchCanvas( e );
+    }
+    else
+    {
+        forceColorsToLegacy();
+        m_canvas->Refresh();
+    }
+
+    // Enable the axes to match legacy draw style
+    auto& galOptions = GetGalDisplayOptions();
+    galOptions.m_axesEnabled = true;
+    galOptions.NotifyChanged();
+
+    m_LayersManager->ReFill();
+    m_LayersManager->ReFillRender();    // Update colors in Render after the config is read
 }
 
 
 GERBVIEW_FRAME::~GERBVIEW_FRAME()
 {
+    if( m_toolManager )
+        m_toolManager->DeactivateTool();
+
+    if( auto canvas = GetGalCanvas() )
+    {
+        canvas->GetView()->Clear();
+    }
+
+    GetGerberLayout()->GetImagesList()->DeleteAllImages();
 }
 
 
@@ -228,7 +285,7 @@ bool GERBVIEW_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
 
         for( unsigned i=0;  i<limit;  ++i, ++layer )
         {
-            setActiveLayer( layer );
+            SetActiveLayer( layer );
 
             // Try to guess the type of file by its ext
             // if it is .drl (Kicad files), it is a drill file
@@ -400,10 +457,25 @@ void GERBVIEW_FRAME::SetElementVisibility( GERBVIEW_LAYER_ID aItemIdVisible,
         wxLogDebug( wxT( "GERBVIEW_FRAME::SetElementVisibility(): bad arg %d" ), aItemIdVisible );
     }
 
+    applyDisplaySettingsToGAL();
     m_LayersManager->SetRenderState( aItemIdVisible, aNewState );
 }
 
 
+void GERBVIEW_FRAME::applyDisplaySettingsToGAL()
+{
+    auto view = GetGalCanvas()->GetView();
+    auto painter = static_cast<KIGFX::GERBVIEW_PAINTER*>( view->GetPainter() );
+    auto settings = static_cast<KIGFX::GERBVIEW_RENDER_SETTINGS*>( painter->GetSettings() );
+    settings->LoadDisplayOptions( &m_DisplayOptions );
+
+    settings->ImportLegacyColors( m_colorsSettings );
+
+    view->RecacheAllItems();
+    view->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
+}
+
+
 int GERBVIEW_FRAME::getNextAvailableLayer( int aLayer ) const
 {
     int layer = aLayer;
@@ -427,7 +499,7 @@ int GERBVIEW_FRAME::getNextAvailableLayer( int aLayer ) const
 
 void GERBVIEW_FRAME::syncLayerWidget()
 {
-    m_LayersManager->SelectLayer( getActiveLayer() );
+    m_LayersManager->SelectLayer( GetActiveLayer() );
     UpdateTitleAndInfo();
 }
 
@@ -437,10 +509,10 @@ void GERBVIEW_FRAME::syncLayerBox( bool aRebuildLayerBox )
     if( aRebuildLayerBox )
         m_SelLayerBox->Resync();
 
-    m_SelLayerBox->SetSelection( getActiveLayer() );
+    m_SelLayerBox->SetSelection( GetActiveLayer() );
 
     int dcodeSelected = -1;
-    GERBER_FILE_IMAGE*   gerber = GetGbrImage( getActiveLayer() );
+    GERBER_FILE_IMAGE*   gerber = GetGbrImage( GetActiveLayer() );
 
     if( gerber )
         dcodeSelected = gerber->m_Selected_Tool;
@@ -462,7 +534,7 @@ void GERBVIEW_FRAME::Liste_D_Codes()
     wxString        Line;
     wxArrayString   list;
     double          scale = g_UserUnit == INCHES ? IU_PER_MILS * 1000 : IU_PER_MM;
-    int       curr_layer = getActiveLayer();
+    int       curr_layer = GetActiveLayer();
 
     for( int layer = 0; layer < (int)ImagesMaxCount(); ++layer )
     {
@@ -485,7 +557,7 @@ void GERBVIEW_FRAME::Liste_D_Codes()
 
         for( ii = 0, jj = 1; ii < TOOLS_MAX_COUNT; ii++ )
         {
-            D_CODE* pt_D_code = gerber->GetDCODE( ii + FIRST_DCODE, false );
+            D_CODE* pt_D_code = gerber->GetDCODE( ii + FIRST_DCODE );
 
             if( pt_D_code == NULL )
                 continue;
@@ -522,7 +594,7 @@ void GERBVIEW_FRAME::Liste_D_Codes()
 
 void GERBVIEW_FRAME::UpdateTitleAndInfo()
 {
-    GERBER_FILE_IMAGE* gerber = GetGbrImage( getActiveLayer() );
+    GERBER_FILE_IMAGE* gerber = GetGbrImage( GetActiveLayer() );
 
     // Display the gerber filename
     if( gerber == NULL )
@@ -532,7 +604,7 @@ void GERBVIEW_FRAME::UpdateTitleAndInfo()
         SetStatusText( wxEmptyString, 0 );
 
         wxString info;
-        info.Printf( _( "Drawing layer %d not in use" ), getActiveLayer() + 1 );
+        info.Printf( _( "Drawing layer %d not in use" ), GetActiveLayer() + 1 );
         m_TextInfo->SetValue( info );
 
         if( EnsureTextCtrlWidth( m_TextInfo, &info ) )  // Resized
@@ -611,14 +683,29 @@ long GERBVIEW_FRAME::GetVisibleLayers() const
 
 void GERBVIEW_FRAME::SetVisibleLayers( long aLayerMask )
 {
-//    GetGerberLayout()->SetVisibleLayers( aLayerMask );
+    if( auto canvas = GetGalCanvas() )
+    {
+        // NOTE: This assumes max 32 drawlayers!
+        for( int i = 0; i < GERBER_DRAWLAYERS_COUNT; i++ )
+        {
+            bool v = ( aLayerMask & ( 1 << i ) );
+            int layer = GERBER_DRAW_LAYER( i );
+            canvas->GetView()->SetLayerVisible( layer, v );
+            canvas->GetView()->SetLayerVisible( GERBER_DCODE_LAYER( layer ), v );
+        }
+    }
 }
 
 
 bool GERBVIEW_FRAME::IsLayerVisible( int aLayer ) const
 {
     if( ! m_DisplayOptions.m_IsPrinting )
+    {
+        if( IsGalCanvasActive() )
+            aLayer = GERBER_DRAW_LAYER( aLayer );
+
         return m_LayersManager->IsLayerVisible( aLayer );
+    }
     else
         return GetGerberLayout()->IsLayerPrintable( aLayer );
 }
@@ -666,6 +753,8 @@ void GERBVIEW_FRAME::SetVisibleElementColor( GERBVIEW_LAYER_ID aItemIdVisible,
         break;
 
     case LAYER_GERBVIEW_GRID:
+        // Ensure grid always has low alpha
+        aColor.a = 0.8;
         SetGridColor( aColor );
         m_colorsSettings->SetItemColor( aItemIdVisible, aColor );
         break;
@@ -694,21 +783,35 @@ COLOR4D GERBVIEW_FRAME::GetLayerColor( int aLayer ) const
 void GERBVIEW_FRAME::SetLayerColor( int aLayer, COLOR4D aColor )
 {
     m_colorsSettings->SetLayerColor( aLayer, aColor );
+    applyDisplaySettingsToGAL();
 }
 
 
-int GERBVIEW_FRAME::getActiveLayer()
+int GERBVIEW_FRAME::GetActiveLayer()
 {
     return ( (GBR_SCREEN*) GetScreen() )->m_Active_Layer;
 }
 
 
-void GERBVIEW_FRAME::setActiveLayer( int aLayer, bool doLayerWidgetUpdate )
+void GERBVIEW_FRAME::SetActiveLayer( int aLayer, bool doLayerWidgetUpdate )
 {
     ( (GBR_SCREEN*) GetScreen() )->m_Active_Layer = aLayer;
 
     if( doLayerWidgetUpdate )
-        m_LayersManager->SelectLayer( getActiveLayer() );
+        m_LayersManager->SelectLayer( GetActiveLayer() );
+
+    if( IsGalCanvasActive() )
+    {
+        m_toolManager->RunAction( GERBVIEW_ACTIONS::layerChanged );       // notify other tools
+        GetGalCanvas()->SetFocus();                 // otherwise hotkeys are stuck somewhere
+
+        GetGalCanvas()->SetTopLayer( GERBER_DRAW_LAYER( aLayer ) );
+
+        GetGalCanvas()->SetHighContrastLayer( GERBER_DRAW_LAYER( aLayer ) );
+        GetGalCanvas()->Refresh();
+
+        GetGalCanvas()->GetView()->RecacheAllItems();
+    }
 }
 
 
@@ -784,6 +887,17 @@ void GERBVIEW_FRAME::SetCurItem( GERBER_DRAW_ITEM* aItem, bool aDisplayInfo )
 }
 
 
+void GERBVIEW_FRAME::SetGridColor( COLOR4D aColor )
+{
+    if( IsGalCanvasActive() )
+    {
+        GetGalCanvas()->GetGAL()->SetGridColor( aColor );
+    }
+
+    m_gridColor = aColor;
+}
+
+
 EDA_RECT GERBVIEW_FRAME::GetGerberLayoutBoundingBox()
 {
     GetGerberLayout()->ComputeBoundingBox();
@@ -909,3 +1023,113 @@ void GERBVIEW_FRAME::unitsChangeRefresh()
     EDA_DRAW_FRAME::unitsChangeRefresh();
     updateDCodeSelectBox();
 }
+
+
+void GERBVIEW_FRAME::forceColorsToLegacy()
+{
+    for( int i = 0; i < LAYER_ID_COUNT; i++ )
+    {
+        COLOR4D c = m_colorsSettings->GetLayerColor( i );
+        c.SetToNearestLegacyColor();
+        m_colorsSettings->SetLayerColor( i, c );
+    }
+}
+
+
+void GERBVIEW_FRAME::UseGalCanvas( bool aEnable )
+{
+    EDA_DRAW_FRAME::UseGalCanvas( aEnable );
+
+    EDA_DRAW_PANEL_GAL* galCanvas = GetGalCanvas();
+
+    if( m_toolManager )
+        m_toolManager->SetEnvironment( m_gerberLayout, GetGalCanvas()->GetView(),
+                                       GetGalCanvas()->GetViewControls(), this );
+
+    if( aEnable )
+    {
+        if( m_toolManager )
+            m_toolManager->ResetTools( TOOL_BASE::GAL_SWITCH );
+
+        galCanvas->GetGAL()->SetGridColor( GetLayerColor( LAYER_GERBVIEW_GRID ) );
+
+        galCanvas->GetView()->RecacheAllItems();
+        galCanvas->SetEventDispatcher( m_toolDispatcher );
+        galCanvas->StartDrawing();
+    }
+    else
+    {
+        if( m_toolManager )
+            m_toolManager->ResetTools( TOOL_BASE::GAL_SWITCH );
+
+        galCanvas->StopDrawing();
+
+        // Redirect all events to the legacy canvas
+        galCanvas->SetEventDispatcher( NULL );
+
+        forceColorsToLegacy();
+        m_canvas->Refresh();
+    }
+
+    m_LayersManager->ReFill();
+    m_LayersManager->ReFillRender();
+
+    ReCreateOptToolbar();
+}
+
+
+EDA_DRAW_PANEL_GAL::GAL_TYPE GERBVIEW_FRAME::LoadCanvasTypeSetting() const
+{
+    EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
+    wxConfigBase* cfg = Kiface().KifaceSettings();
+
+    if( cfg )
+        canvasType = (EDA_DRAW_PANEL_GAL::GAL_TYPE) cfg->ReadLong( CANVAS_TYPE_KEY,
+                                                                   EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE );
+
+    if( canvasType < EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE
+            || canvasType >= EDA_DRAW_PANEL_GAL::GAL_TYPE_LAST )
+    {
+        assert( false );
+        canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
+    }
+
+    return canvasType;
+}
+
+
+bool GERBVIEW_FRAME::SaveCanvasTypeSetting( EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType )
+{
+    if( aCanvasType < EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE
+            || aCanvasType >= EDA_DRAW_PANEL_GAL::GAL_TYPE_LAST )
+    {
+        assert( false );
+        return false;
+    }
+
+    wxConfigBase* cfg = Kiface().KifaceSettings();
+
+    if( cfg )
+        return cfg->Write( CANVAS_TYPE_KEY, (long) aCanvasType );
+
+    return false;
+}
+
+
+void GERBVIEW_FRAME::setupTools()
+{
+    // Create the manager and dispatcher & route draw panel events to the dispatcher
+    m_toolManager = new TOOL_MANAGER;
+    m_toolManager->SetEnvironment( m_gerberLayout, GetGalCanvas()->GetView(),
+                                   GetGalCanvas()->GetViewControls(), this );
+    m_actions = new GERBVIEW_ACTIONS();
+    m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager, m_actions );
+
+    // Register tools
+    m_actions->RegisterAllTools( m_toolManager );
+    m_toolManager->InitTools();
+
+    // Run the selection tool, it is supposed to be always active
+    m_toolManager->InvokeTool( "gerbview.InteractiveSelection" );
+}
+
diff --git a/gerbview/gerbview_frame.h b/gerbview/gerbview_frame.h
index 39a7f1b86..a25b18f1a 100644
--- a/gerbview/gerbview_frame.h
+++ b/gerbview/gerbview_frame.h
@@ -36,10 +36,12 @@
 #include <layers_id_colors_and_visibility.h>
 
 #include <gerbview.h>
+#include <convert_to_biu.h>
 #include <class_gbr_layout.h>
 #include <class_gbr_screen.h>
 #include <class_page_info.h>
 #include <class_gbr_display_options.h>
+#include <class_draw_panel_gal.h>
 #include <class_colors_design_settings.h>
 
 extern COLORS_DESIGN_SETTINGS g_ColorsSettings;
@@ -154,6 +156,9 @@ public:
      */
     int SelectPCBLayer( int aDefaultLayer, int aOpperLayerCount, bool aNullLayer = false );
 
+    ///> @copydoc EDA_DRAW_FRAME::SetGridColor()
+    virtual void SetGridColor( COLOR4D aColor ) override;
+
 protected:
     GERBER_LAYER_WIDGET*    m_LayersManager;
 
@@ -169,6 +174,9 @@ protected:
     /// The last filename chosen to be proposed to the user
     wxString                m_lastFileName;
 
+    ///> @copydoc EDA_DRAW_FRAME::forceColorsToLegacy()
+    virtual void forceColorsToLegacy() override;
+
 public:
     wxChoice* m_SelComponentBox;                // a choice box to display and highlight component graphic items
     wxChoice* m_SelNetnameBox;                  // a choice box to display and highlight netlist graphic items
@@ -179,6 +187,8 @@ public:
     wxTextCtrl*             m_TextInfo;         // a wxTextCtrl used to display some info about
                                                 // gerber data (format..)
 
+    COLORS_DESIGN_SETTINGS* m_colorsSettings;
+
 private:
     /// Auxiliary tool bar typically shown below the main tool bar at the top of the
     /// main window.
@@ -186,7 +196,6 @@ private:
 
     // list of PARAM_CFG_xxx to read/write parameters saved in config
     PARAM_CFG_ARRAY         m_configSettings;
-    COLORS_DESIGN_SETTINGS* m_colorsSettings;
 
     int m_displayMode;                  // Gerber images ("layers" in Gerbview) can be drawn:
                                         // - in fast mode (write mode) but if there are negative
@@ -206,9 +215,15 @@ private:
     void            updateDCodeSelectBox();
     virtual void    unitsChangeRefresh() override;      // See class EDA_DRAW_FRAME
 
+    // The Tool Framework initalization
+    void setupTools();
+
     // An array string to store warning messages when reading a gerber file.
     wxArrayString   m_Messages;
 
+    /// Updates the GAL with display settings changes
+    void applyDisplaySettingsToGAL();
+
 public:
     GERBVIEW_FRAME( KIWAY* aKiway, wxWindow* aParent );
     ~GERBVIEW_FRAME();
@@ -243,6 +258,8 @@ public:
     double  BestZoom() override;
     void    UpdateStatusBar() override;
 
+    wxAuiToolBar* GetMainToolBar() { return m_optionsToolBar; }
+
     /**
      * Function GetZoomLevelIndicator
      * returns a human readable value which can be displayed as zoom
@@ -332,7 +349,7 @@ public:
     /**
      * Function IsLayerVisible
      * tests whether a given layer is visible
-     * @param aLayer = The layer to be tested
+     * @param aLayer = The layer to be tested (still 0-31!)
      * @return bool - true if the layer is visible.
      */
     bool    IsLayerVisible( int aLayer ) const;
@@ -403,17 +420,17 @@ public:
     void    ReFillLayerWidget();
 
     /**
-     * Function setActiveLayer
+     * Function SetActiveLayer
      * will change the currently active layer to \a aLayer and also
      * update the GERBER_LAYER_WIDGET.
      */
-    void    setActiveLayer( int aLayer, bool doLayerWidgetUpdate = true );
+    void SetActiveLayer( int aLayer, bool doLayerWidgetUpdate = true );
 
     /**
-     * Function getActiveLayer
+     * Function SetActiveLayer
      * returns the active layer
      */
-    int getActiveLayer();
+    int GetActiveLayer();
 
     /**
      * Function getNextAvailableLayer
@@ -432,7 +449,7 @@ public:
     /**
      * Function syncLayerWidget
      * updates the currently "selected" layer within the GERBER_LAYER_WIDGET.
-     * The currently active layer is defined by the return value of getActiveLayer().
+     * The currently active layer is defined by the return value of GetActiveLayer().
      * <p>
      * This function cannot be inline without including layer_widget.h in
      * here and we do not want to do that.
@@ -443,7 +460,7 @@ public:
      * Function syncLayerBox
      * updates the currently "selected" layer within m_SelLayerBox
      * The currently active layer, as defined by the return value of
-     * getActiveLayer().
+     * GetActiveLayer().
      * @param aRebuildLayerBox = true to rebuild the layer box
      *  false to just updates the selection.
      */
@@ -479,6 +496,7 @@ public:
 
     void                Process_Special_Functions( wxCommandEvent& event );
     void                OnSelectOptionToolbar( wxCommandEvent& event );
+    void                OnSelectHighlightChoice( wxCommandEvent& event );
 
     /**
      * Function OnSelectActiveDCode
@@ -714,6 +732,34 @@ public:
     virtual void    PrintPage( wxDC* aDC, LSET aPrintMasklayer, bool aPrintMirrorMode,
                                void* aData = NULL ) override;
 
+    ///> @copydoc EDA_DRAW_FRAME::UseGalCanvas
+    virtual void UseGalCanvas( bool aEnable ) override;
+
+    /**
+     * switches currently used canvas (default / Cairo / OpenGL).
+     */
+    void SwitchCanvas( wxCommandEvent& aEvent );
+
+    /**
+     * Update UI called when switches currently used canvas (default / Cairo / OpenGL).
+     */
+    void OnUpdateSwitchCanvas( wxUpdateUIEvent& aEvent );
+
+    /**
+     * Function LoadCanvasTypeSetting()
+     * Returns the canvas type stored in the application settings.
+     */
+    EDA_DRAW_PANEL_GAL::GAL_TYPE LoadCanvasTypeSetting() const;
+
+    /**
+     * Function SaveCanvasTypeSetting()
+     * Stores the canvas type in the application settings.
+     */
+    bool SaveCanvasTypeSetting( EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType );
+
+    ///> Key in KifaceSettings to store the canvas type.
+    static const wxChar CANVAS_TYPE_KEY[];
+
     DECLARE_EVENT_TABLE()
 };
 
diff --git a/gerbview/gerbview_id.h b/gerbview/gerbview_id.h
index e54a6bb07..2e4494b81 100644
--- a/gerbview/gerbview_id.h
+++ b/gerbview/gerbview_id.h
@@ -50,6 +50,9 @@ enum gerbview_ids
 
     ID_MENU_GERBVIEW_SHOW_HIDE_LAYERS_MANAGER_DIALOG,
     ID_MENU_GERBVIEW_SELECT_PREFERED_EDITOR,
+    ID_MENU_CANVAS_LEGACY,
+    ID_MENU_CANVAS_OPENGL,
+    ID_MENU_CANVAS_CAIRO,
 
     ID_GBR_AUX_TOOLBAR_PCB_CMP_CHOICE,
     ID_GBR_AUX_TOOLBAR_PCB_NET_CHOICE,
@@ -104,6 +107,9 @@ enum gerbview_ids
     ID_TB_OPTIONS_SHOW_GBR_MODE_0,
     ID_TB_OPTIONS_SHOW_GBR_MODE_1,
     ID_TB_OPTIONS_SHOW_GBR_MODE_2,
+    ID_TB_OPTIONS_DIFF_MODE,
+    ID_TB_OPTIONS_HIGH_CONTRAST_MODE,
+    ID_TB_MEASUREMENT_TOOL,
 
     // Right click context menu
     ID_HIGHLIGHT_REMOVE_ALL,
diff --git a/gerbview/hotkeys.cpp b/gerbview/hotkeys.cpp
index 2a830358c..19ed1315a 100644
--- a/gerbview/hotkeys.cpp
+++ b/gerbview/hotkeys.cpp
@@ -28,7 +28,7 @@
 
 #include <fctsys.h>
 #include <common.h>
-#include <id.h>
+#include <gerbview_id.h>
 
 #include <gerbview.h>
 #include <gerbview_frame.h>
@@ -86,6 +86,25 @@ static EDA_HOTKEY   HkSwitch2NextCopperLayer( _HKI( "Switch to Next Layer" ),
 static EDA_HOTKEY   HkSwitch2PreviousCopperLayer( _HKI( "Switch to Previous Layer" ),
                                               HK_SWITCH_LAYER_TO_PREVIOUS, '-' );
 
+static EDA_HOTKEY HkCanvasDefault( _HKI( "Switch to Legacy Canvas" ),
+                                   HK_CANVAS_LEGACY,
+#ifdef __WXMAC__
+                                   GR_KB_ALT +
+#endif
+                                   WXK_F9 );
+static EDA_HOTKEY HkCanvasOpenGL( _HKI( "Switch to OpenGL Canvas" ),
+                                  HK_CANVAS_OPENGL,
+#ifdef __WXMAC__
+                                  GR_KB_ALT +
+#endif
+                                  WXK_F11 );
+static EDA_HOTKEY HkCanvasCairo( _HKI( "Switch to Cairo Canvas" ),
+                                 HK_CANVAS_CAIRO,
+#ifdef __WXMAC__
+                                 GR_KB_ALT +
+#endif
+                                 WXK_F12 );
+
 // List of common hotkey descriptors
 EDA_HOTKEY* gerbviewHotkeyList[] = {
     &HkHelp,
@@ -95,6 +114,9 @@ EDA_HOTKEY* gerbviewHotkeyList[] = {
     &HkDCodesDisplayMode, &HkNegativeObjDisplayMode,
     &HkSwitch2NextCopperLayer,
     &HkSwitch2PreviousCopperLayer,
+    &HkCanvasDefault,
+    &HkCanvasOpenGL,
+    &HkCanvasCairo,
     NULL
 };
 
@@ -205,22 +227,37 @@ bool GERBVIEW_FRAME::OnHotKey( wxDC* aDC, int aHotkeyCode, const wxPoint& aPosit
         break;
 
     case HK_SWITCH_LAYER_TO_PREVIOUS:
-        if( getActiveLayer() > 0 )
+        if( GetActiveLayer() > 0 )
         {
-            setActiveLayer( getActiveLayer() - 1 );
+            SetActiveLayer( GetActiveLayer() - 1 );
             m_LayersManager->OnLayerSelected();
             m_canvas->Refresh();
         }
         break;
 
     case HK_SWITCH_LAYER_TO_NEXT:
-        if( getActiveLayer() < 31 )
+        if( GetActiveLayer() < GERBER_DRAWLAYERS_COUNT - 1 )
         {
-            setActiveLayer( getActiveLayer() + 1 );
+            SetActiveLayer( GetActiveLayer() + 1 );
             m_LayersManager->OnLayerSelected();
             m_canvas->Refresh();
         }
         break;
+
+    case HK_CANVAS_CAIRO:
+        cmd.SetId( ID_MENU_CANVAS_CAIRO );
+        GetEventHandler()->ProcessEvent( cmd );
+        break;
+
+    case HK_CANVAS_OPENGL:
+        cmd.SetId( ID_MENU_CANVAS_OPENGL );
+        GetEventHandler()->ProcessEvent( cmd );
+        break;
+
+    case HK_CANVAS_LEGACY:
+        cmd.SetId( ID_MENU_CANVAS_LEGACY );
+        GetEventHandler()->ProcessEvent( cmd );
+        break;
     }
 
     return true;
diff --git a/gerbview/hotkeys.h b/gerbview/hotkeys.h
index 206d53775..21e2c30b6 100644
--- a/gerbview/hotkeys.h
+++ b/gerbview/hotkeys.h
@@ -42,7 +42,10 @@ enum hotkey_id_commnand {
     HK_GBR_NEGATIVE_DISPLAY_ONOFF,
     HK_GBR_DCODE_DISPLAY_ONOFF,
     HK_SWITCH_LAYER_TO_NEXT,
-    HK_SWITCH_LAYER_TO_PREVIOUS
+    HK_SWITCH_LAYER_TO_PREVIOUS,
+    HK_CANVAS_LEGACY,
+    HK_CANVAS_OPENGL,
+    HK_CANVAS_CAIRO
 };
 
 // List of hotkey descriptors for GerbView.
diff --git a/gerbview/job_file_reader.cpp b/gerbview/job_file_reader.cpp
index 48798dbba..faf731d19 100644
--- a/gerbview/job_file_reader.cpp
+++ b/gerbview/job_file_reader.cpp
@@ -193,7 +193,7 @@ bool GERBVIEW_FRAME::LoadGerberJobFile( const wxString& aFullFileName )
             wxFileName gbr_fn = filename;
             bool read_ok;
             int layer = 0;
-            setActiveLayer( layer, false );
+            SetActiveLayer( layer, false );
 
             for( unsigned ii = 0; ii < gbrfiles.GetCount(); ii++ )
             {
@@ -207,7 +207,7 @@ bool GERBVIEW_FRAME::LoadGerberJobFile( const wxString& aFullFileName )
                     if( read_ok )
                     {
                         layer = getNextAvailableLayer( layer );
-                        setActiveLayer( layer, false );
+                        SetActiveLayer( layer, false );
                     }
                 }
                 else
diff --git a/gerbview/locate.cpp b/gerbview/locate.cpp
index 5ee8869f6..d3ff27ca5 100644
--- a/gerbview/locate.cpp
+++ b/gerbview/locate.cpp
@@ -49,7 +49,7 @@ GERBER_DRAW_ITEM* GERBVIEW_FRAME::Locate( const wxPoint& aPosition, int aTypeloc
     if( aTypeloc == CURSEUR_ON_GRILLE )
         ref = GetNearestGridPosition( ref );
 
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
     GERBER_FILE_IMAGE* gerber = GetGbrImage( layer );
 
     GERBER_DRAW_ITEM* gerb_item = NULL;
diff --git a/gerbview/menubar.cpp b/gerbview/menubar.cpp
index 40bb93fb9..fa3f372b7 100644
--- a/gerbview/menubar.cpp
+++ b/gerbview/menubar.cpp
@@ -42,6 +42,7 @@ void GERBVIEW_FRAME::ReCreateMenuBar()
 {
     // Create and try to get the current menubar
     wxMenuBar* menuBar = GetMenuBar();
+    wxString   text;
 
     if( !menuBar )
         menuBar = new wxMenuBar();
@@ -207,6 +208,33 @@ void GERBVIEW_FRAME::ReCreateMenuBar()
     // Hotkey submenu
     AddHotkeyConfigMenu( configMenu );
 
+    // Canvas selection
+    configMenu->AppendSeparator();
+
+    text = AddHotkeyName( _( "Legacy Canva&s" ), GerbviewHokeysDescr,
+                          HK_CANVAS_LEGACY );
+
+    configMenu->Append(
+        new wxMenuItem( configMenu, ID_MENU_CANVAS_LEGACY,
+                        text, _( "Switch the canvas implementation to Legacy" ),
+                        wxITEM_RADIO ) );
+
+    text = AddHotkeyName( _( "Open&GL Canvas" ), GerbviewHokeysDescr,
+                          HK_CANVAS_OPENGL );
+
+    configMenu->Append(
+        new wxMenuItem( configMenu, ID_MENU_CANVAS_OPENGL,
+                        text, _( "Switch the canvas implementation to OpenGL" ),
+                        wxITEM_RADIO ) );
+
+    text = AddHotkeyName( _( "&Cairo Canvas" ), GerbviewHokeysDescr,
+                          HK_CANVAS_CAIRO );
+
+    configMenu->Append(
+        new wxMenuItem( configMenu, ID_MENU_CANVAS_CAIRO,
+                        text, _( "Switch the canvas implementation to Cairo" ),
+                        wxITEM_RADIO ) );
+
     // Menu miscellaneous
     wxMenu* miscellaneousMenu = new wxMenu;
 
diff --git a/gerbview/onleftclick.cpp b/gerbview/onleftclick.cpp
index c07bb9621..9855d7173 100644
--- a/gerbview/onleftclick.cpp
+++ b/gerbview/onleftclick.cpp
@@ -36,13 +36,15 @@
  */
 void GERBVIEW_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition )
 {
+    SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
+
     GERBER_DRAW_ITEM* DrawStruct = Locate( aPosition, CURSEUR_OFF_GRILLE );
 
     GetScreen()->SetCurItem( DrawStruct );
 
     if( DrawStruct == NULL )
     {
-        GERBER_FILE_IMAGE* gerber = GetGbrImage( getActiveLayer() );
+        GERBER_FILE_IMAGE* gerber = GetGbrImage( GetActiveLayer() );
 
         if( gerber )
             gerber->DisplayImageInfo( this );
diff --git a/gerbview/onrightclick.cpp b/gerbview/onrightclick.cpp
index 1c3849e6d..1d676bd56 100644
--- a/gerbview/onrightclick.cpp
+++ b/gerbview/onrightclick.cpp
@@ -120,7 +120,7 @@ bool GERBVIEW_FRAME::OnRightClick( const wxPoint& aPosition, wxMenu* aPopMenu )
 
         D_CODE* apertDescr = currItem->GetDcodeDescr();
 
-        if( !apertDescr->m_AperFunction.IsEmpty() )
+        if( apertDescr && !apertDescr->m_AperFunction.IsEmpty() )
         {
             AddMenuItem( aPopMenu, ID_HIGHLIGHT_APER_ATTRIBUTE_ITEMS,
                          wxString::Format( _( "Highlight aperture type '%s'" ),
diff --git a/gerbview/readgerb.cpp b/gerbview/readgerb.cpp
index 59f2ec61f..f55c10f74 100644
--- a/gerbview/readgerb.cpp
+++ b/gerbview/readgerb.cpp
@@ -30,6 +30,7 @@
 #include <gerbview_frame.h>
 #include <class_gerber_file_image.h>
 #include <class_gerber_file_image_list.h>
+#include <view/view.h>
 
 #include <html_messagebox.h>
 #include <macros.h>
@@ -40,7 +41,7 @@ bool GERBVIEW_FRAME::Read_GERBER_File( const wxString& GERBER_FullFileName )
 {
     wxString msg;
 
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
     GERBER_FILE_IMAGE_LIST* images = GetImagesList();
     GERBER_FILE_IMAGE* gerber = GetGbrImage( layer );
 
@@ -79,6 +80,23 @@ bool GERBVIEW_FRAME::Read_GERBER_File( const wxString& GERBER_FullFileName )
         wxMessageBox( msg );
     }
 
+    auto canvas = GetGalCanvas();
+    if( canvas )
+    {
+        auto view = canvas->GetView();
+
+        if( gerber->m_ImageNegative )
+        {
+            // TODO: find a way to handle negative images
+            // (maybe convert geometry into positives?)
+        }
+
+        for( auto item = gerber->GetItemsList(); item; item = item->Next() )
+        {
+            view->Add( (KIGFX::VIEW_ITEM*) item );
+        }
+    }
+
     return true;
 }
 
diff --git a/gerbview/rs274d.cpp b/gerbview/rs274d.cpp
index 45831d7ee..3d66bf8fe 100644
--- a/gerbview/rs274d.cpp
+++ b/gerbview/rs274d.cpp
@@ -140,6 +140,9 @@ void fillFlashedGBRITEM(  GERBER_DRAW_ITEM* aGbrItem,
 
     case APT_MACRO:
         aGbrItem->m_Shape = GBR_SPOT_MACRO;
+
+        // Cache the bounding box for aperture macros
+        aGbrItem->GetDcodeDescr()->GetMacro()->GetApertureMacroShape( aGbrItem, aPos );
         break;
     }
 }
@@ -379,6 +382,9 @@ static void fillArcPOLY(  GERBER_DRAW_ITEM* aGbrItem,
     const int increment_angle = 3600 / 36;
     int count = std::abs( arc_angle / increment_angle );
 
+    if( aGbrItem->m_Polygon.OutlineCount() == 0 )
+        aGbrItem->m_Polygon.NewOutline();
+
     // calculate polygon corners
     // when arc is counter-clockwise, dummyGbrItem arc goes from end to start
     // and we must always create a polygon from start to end.
@@ -397,7 +403,7 @@ static void fillArcPOLY(  GERBER_DRAW_ITEM* aGbrItem,
         else    // last point
             end_arc = aClockwise ? end : start;
 
-        aGbrItem->m_PolyCorners.push_back( end_arc + center );
+        aGbrItem->m_Polygon.Append( VECTOR2I( end_arc + center ) );
 
         start_arc = end_arc;
     }
@@ -510,7 +516,7 @@ bool GERBER_FILE_IMAGE::Execute_G_Command( char*& text, int G_command )
         if( D_commande > (TOOLS_MAX_COUNT - 1) )
             D_commande = TOOLS_MAX_COUNT - 1;
         m_Current_Tool = D_commande;
-        D_CODE* pt_Dcode = GetDCODE( D_commande, false );
+        D_CODE* pt_Dcode = GetDCODE( D_commande );
         if( pt_Dcode )
             pt_Dcode->m_InUse = true;
         break;
@@ -552,6 +558,7 @@ bool GERBER_FILE_IMAGE::Execute_G_Command( char*& text, int G_command )
         if( m_Exposure && GetItemsList() )    // End of polygon
         {
             GERBER_DRAW_ITEM * gbritem = m_Drawings.GetLast();
+            gbritem->m_Polygon.Append( gbritem->m_Polygon.Vertex( 0 ) );
             StepAndRepeatItem( *gbritem );
         }
         m_Exposure = false;
@@ -594,7 +601,7 @@ bool GERBER_FILE_IMAGE::Execute_DCODE_Command( char*& text, int D_commande )
         // call
         m_Current_Tool = D_commande;
 
-        D_CODE* pt_Dcode = GetDCODE( D_commande, false );
+        D_CODE* pt_Dcode = GetDCODE( D_commande );
         if( pt_Dcode )
             pt_Dcode->m_InUse = true;
 
@@ -635,11 +642,14 @@ bool GERBER_FILE_IMAGE::Execute_DCODE_Command( char*& text, int D_commande )
                 gbritem = m_Drawings.GetLast();
 
                 gbritem->m_Start = m_PreviousPos;       // m_Start is used as temporary storage
-                if( gbritem->m_PolyCorners.size() == 0 )
-                    gbritem->m_PolyCorners.push_back( gbritem->m_Start );
+                if( gbritem->m_Polygon.OutlineCount() == 0 )
+                {
+                    gbritem->m_Polygon.NewOutline();
+                    gbritem->m_Polygon.Append( VECTOR2I( gbritem->m_Start ) );
+                }
 
                 gbritem->m_End = m_CurrentPos;       // m_End is used as temporary storage
-                gbritem->m_PolyCorners.push_back( gbritem->m_End );
+                gbritem->m_Polygon.Append( VECTOR2I( gbritem->m_End ) );
                 break;
             }
 
@@ -651,6 +661,7 @@ bool GERBER_FILE_IMAGE::Execute_DCODE_Command( char*& text, int D_commande )
             if( m_Exposure && GetItemsList() )    // End of polygon
             {
                 gbritem = m_Drawings.GetLast();
+                gbritem->m_Polygon.Append( gbritem->m_Polygon.Vertex( 0 ) );
                 StepAndRepeatItem( *gbritem );
             }
             m_Exposure    = false;
@@ -669,7 +680,7 @@ bool GERBER_FILE_IMAGE::Execute_DCODE_Command( char*& text, int D_commande )
         case 1:     // code D01 Draw line, exposure ON
             m_Exposure = true;
 
-            tool = GetDCODE( m_Current_Tool, false );
+            tool = GetDCODE( m_Current_Tool );
             if( tool )
             {
                 size     = tool->m_Size;
@@ -722,7 +733,7 @@ bool GERBER_FILE_IMAGE::Execute_DCODE_Command( char*& text, int D_commande )
             break;
 
         case 3:     // code D3: flash aperture
-            tool = GetDCODE( m_Current_Tool, false );
+            tool = GetDCODE( m_Current_Tool );
             if( tool )
             {
                 size     = tool->m_Size;
diff --git a/gerbview/rs274x.cpp b/gerbview/rs274x.cpp
index 73d3c6381..fca35208b 100644
--- a/gerbview/rs274x.cpp
+++ b/gerbview/rs274x.cpp
@@ -710,7 +710,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
         code = ReadInt( text );
 
         D_CODE* dcode;
-        dcode = GetDCODE( code );
+        dcode = GetDCODEOrCreate( code );
 
         if( dcode == NULL )
             break;
diff --git a/gerbview/toolbars_gerber.cpp b/gerbview/toolbars_gerber.cpp
index 367870265..43384e7e0 100644
--- a/gerbview/toolbars_gerber.cpp
+++ b/gerbview/toolbars_gerber.cpp
@@ -206,11 +206,10 @@ void GERBVIEW_FRAME::ReCreateVToolbar( void )
 void GERBVIEW_FRAME::ReCreateOptToolbar( void )
 {
     if( m_optionsToolBar )
-        return;
-
-    // creation of tool bar options
-    m_optionsToolBar = new wxAuiToolBar( this, ID_OPT_TOOLBAR, wxDefaultPosition, wxDefaultSize,
-                                         wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_VERTICAL );
+        m_optionsToolBar->Clear();
+    else
+        m_optionsToolBar = new wxAuiToolBar( this, ID_OPT_TOOLBAR, wxDefaultPosition, wxDefaultSize,
+                                             wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_VERTICAL );
 
     // TODO: these can be moved to the 'proper' vertical toolbar if and when there are
     // actual tools to put there. That, or I'll get around to implementing configurable
@@ -218,6 +217,17 @@ void GERBVIEW_FRAME::ReCreateOptToolbar( void )
     m_optionsToolBar->AddTool( ID_NO_TOOL_SELECTED, wxEmptyString, KiBitmap( cursor_xpm ),
                                wxEmptyString, wxITEM_CHECK );
 
+    m_optionsToolBar->AddTool( ID_ZOOM_SELECTION, wxEmptyString, KiBitmap( zoom_area_xpm ),
+                               _( "Zoom to selection" ), wxITEM_CHECK );
+
+    if( IsGalCanvasActive() )
+    {
+        m_optionsToolBar->AddTool( ID_TB_MEASUREMENT_TOOL, wxEmptyString,
+                                   KiBitmap( measurement_xpm ),
+                                   _( "Measure distance between two points" ),
+                                   wxITEM_CHECK );
+    }
+
     m_optionsToolBar->AddSeparator();
 
     m_optionsToolBar->AddTool( ID_TB_OPTIONS_SHOW_GRID, wxEmptyString, KiBitmap( grid_xpm ),
@@ -264,23 +274,39 @@ void GERBVIEW_FRAME::ReCreateOptToolbar( void )
                                KiBitmap( show_dcodenumber_xpm ),
                                _( "Show dcode number" ), wxITEM_CHECK );
 
-    // tools to select draw mode in GerbView
-    m_optionsToolBar->AddSeparator();
-    m_optionsToolBar->AddTool( ID_TB_OPTIONS_SHOW_GBR_MODE_0, wxEmptyString,
-                               KiBitmap( gbr_select_mode0_xpm ),
-                               _( "Show layers in raw mode \
-(could have problems with negative items when more than one gerber file is shown)" ),
-                               wxITEM_CHECK );
-    m_optionsToolBar->AddTool( ID_TB_OPTIONS_SHOW_GBR_MODE_1, wxEmptyString,
-                               KiBitmap( gbr_select_mode1_xpm ),
-                               _( "Show layers in stacked mode \
-(show negative items without artifacts, sometimes slow)" ),
-                               wxITEM_CHECK );
-    m_optionsToolBar->AddTool( ID_TB_OPTIONS_SHOW_GBR_MODE_2, wxEmptyString,
-                               KiBitmap( gbr_select_mode2_xpm ),
-                               _( "Show layers in transparency mode \
-(show negative items without artifacts, sometimes slow)" ),
-                               wxITEM_CHECK );
+    // tools to select draw mode in GerbView, unused in GAL
+    if( !IsGalCanvasActive() )
+    {
+        m_optionsToolBar->AddSeparator();
+        m_optionsToolBar->AddTool( ID_TB_OPTIONS_SHOW_GBR_MODE_0, wxEmptyString,
+                                   KiBitmap( gbr_select_mode0_xpm ),
+                                   _( "Show layers in raw mode \
+    (could have problems with negative items when more than one gerber file is shown)" ),
+                                   wxITEM_CHECK );
+        m_optionsToolBar->AddTool( ID_TB_OPTIONS_SHOW_GBR_MODE_1, wxEmptyString,
+                                   KiBitmap( gbr_select_mode1_xpm ),
+                                   _( "Show layers in stacked mode \
+    (show negative items without artifacts, sometimes slow)" ),
+                                   wxITEM_CHECK );
+        m_optionsToolBar->AddTool( ID_TB_OPTIONS_SHOW_GBR_MODE_2, wxEmptyString,
+                                   KiBitmap( gbr_select_mode2_xpm ),
+                                   _( "Show layers in transparency mode \
+    (show negative items without artifacts, sometimes slow)" ),
+                                   wxITEM_CHECK );
+    }
+    else
+    {
+        m_optionsToolBar->AddTool( ID_TB_OPTIONS_DIFF_MODE, wxEmptyString,
+                                   KiBitmap( gbr_select_mode2_xpm ),
+                                   _( "Show layers in diff (compare) mode" ),
+                                   wxITEM_CHECK );
+
+        m_optionsToolBar->AddTool( ID_TB_OPTIONS_HIGH_CONTRAST_MODE, wxEmptyString,
+                                   KiBitmap( contrast_mode_xpm ),
+                                   _( "Enable high contrast display mode" ),
+                                   wxITEM_CHECK );
+
+    }
 
     // Tools to show/hide toolbars:
     m_optionsToolBar->AddSeparator();
@@ -304,7 +330,7 @@ void GERBVIEW_FRAME::updateDCodeSelectBox()
     // Add an empty string to deselect net highlight
     m_DCodeSelector->Append( NO_SELECTION_STRING );
 
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
     GERBER_FILE_IMAGE* gerber = GetGbrImage( layer );
 
     if( !gerber || gerber->GetDcodesCount() == 0 )
@@ -323,7 +349,7 @@ void GERBVIEW_FRAME::updateDCodeSelectBox()
 
     for( int ii = 0; ii < TOOLS_MAX_COUNT; ii++ )
     {
-        D_CODE* dcode = gerber->GetDCODE( ii + FIRST_DCODE, false );
+        D_CODE* dcode = gerber->GetDCODE( ii + FIRST_DCODE );
 
         if( dcode == NULL )
             continue;
@@ -433,7 +459,7 @@ void GERBVIEW_FRAME::updateAperAttributesSelectBox()
 
         for( int ii = 0; ii < TOOLS_MAX_COUNT; ii++ )
         {
-            D_CODE* aperture = gerber->GetDCODE( ii + FIRST_DCODE, false );
+            D_CODE* aperture = gerber->GetDCODE( ii + FIRST_DCODE );
 
             if( aperture == NULL )
                 continue;
@@ -534,7 +560,7 @@ void GERBVIEW_FRAME::OnUpdateSelectDCode( wxUpdateUIEvent& aEvent )
     if( !m_DCodeSelector )
         return;
 
-    int layer = getActiveLayer();
+    int layer = GetActiveLayer();
     GERBER_FILE_IMAGE* gerber = GetGbrImage( layer );
     int selected = ( gerber ) ? gerber->m_Selected_Tool : 0;
 
@@ -553,8 +579,8 @@ void GERBVIEW_FRAME::OnUpdateSelectDCode( wxUpdateUIEvent& aEvent )
 
 void GERBVIEW_FRAME::OnUpdateLayerSelectBox( wxUpdateUIEvent& aEvent )
 {
-    if( m_SelLayerBox->GetSelection() != getActiveLayer() )
+    if( m_SelLayerBox->GetSelection() != GetActiveLayer() )
     {
-        m_SelLayerBox->SetSelection( getActiveLayer() );
+        m_SelLayerBox->SetSelection( GetActiveLayer() );
     }
 }
diff --git a/include/core/typeinfo.h b/include/core/typeinfo.h
index 1c4c4e597..34c3324e6 100644
--- a/include/core/typeinfo.h
+++ b/include/core/typeinfo.h
@@ -171,7 +171,10 @@ enum KICAD_T
     /*
      * For GerbView: items type:
      */
-    TYPE_GERBER_DRAW_ITEM,
+    GERBER_LAYOUT_T,
+    GERBER_DRAW_ITEM_T,
+    GERBER_IMAGE_LIST_T,
+    GERBER_IMAGE_T,
 
     /*
      * for Pl_Editor, in undo/redo commands
diff --git a/include/layers_id_colors_and_visibility.h b/include/layers_id_colors_and_visibility.h
index 26404fec1..db861838e 100644
--- a/include/layers_id_colors_and_visibility.h
+++ b/include/layers_id_colors_and_visibility.h
@@ -276,8 +276,8 @@ enum GERBVIEW_LAYER_ID: int
 {
     GERBVIEW_LAYER_ID_START = SCH_LAYER_ID_END,
 
-    /// GerbView draw layers
-    GERBVIEW_LAYER_ID_RESERVED = GERBVIEW_LAYER_ID_START + GERBER_DRAWLAYERS_COUNT,
+    /// GerbView draw layers and d-code layers
+    GERBVIEW_LAYER_ID_RESERVED = GERBVIEW_LAYER_ID_START + ( 2 * GERBER_DRAWLAYERS_COUNT ),
 
     LAYER_DCODES,
     LAYER_NEGATIVE_OBJECTS,
@@ -288,6 +288,12 @@ enum GERBVIEW_LAYER_ID: int
     GERBVIEW_LAYER_ID_END
 };
 
+#define GERBER_DRAW_LAYER( x ) ( GERBVIEW_LAYER_ID_START + x )
+
+#define GERBER_DCODE_LAYER( x ) ( GERBER_DRAWLAYERS_COUNT + x )
+
+#define GERBER_DRAW_LAYER_INDEX( x ) ( x - GERBVIEW_LAYER_ID_START )
+
 /// Must update this if you add any enums after GerbView!
 #define LAYER_ID_COUNT GERBVIEW_LAYER_ID_END
 
@@ -605,6 +611,7 @@ private:
     }
 };
 
+
 /**
  * Function IsValidLayer
  * tests whether a given integer is a valid layer index, i.e. can
@@ -774,6 +781,13 @@ inline bool IsNetnameLayer( LAYER_NUM aLayer )
 }
 
 
+inline bool IsDCodeLayer( int aLayer )
+{
+    return aLayer >= (GERBVIEW_LAYER_ID_START + GERBER_DRAWLAYERS_COUNT) &&
+           aLayer < (GERBVIEW_LAYER_ID_START + (2 * GERBER_DRAWLAYERS_COUNT));
+}
+
+
 PCB_LAYER_ID ToLAYER_ID( int aLayer );
 
 #endif // LAYERS_ID_AND_VISIBILITY_H_
-- 
2.11.0

From 2a4cfd83dd70f5db957513a05aa6b4cfb325b43d Mon Sep 17 00:00:00 2001
From: Jon Evans <jon@xxxxxxxxxxxxx>
Date: Sun, 17 Sep 2017 18:44:30 -0400
Subject: [PATCH 5/5] GerbView GAL support part 2: New files for GAL

- New draw panel
- New painter
- GerbView-specific tools
---
 AUTHORS.txt                          |   1 +
 gerbview/CMakeLists.txt              |   8 +
 gerbview/gerber_collectors.cpp       |  71 +++
 gerbview/gerber_collectors.h         | 134 +++++
 gerbview/gerbview_draw_panel_gal.cpp | 154 ++++++
 gerbview/gerbview_draw_panel_gal.h   |  64 +++
 gerbview/gerbview_painter.cpp        | 563 ++++++++++++++++++++
 gerbview/gerbview_painter.h          | 203 ++++++++
 gerbview/tools/gerbview_actions.cpp  |  76 +++
 gerbview/tools/gerbview_actions.h    | 139 +++++
 gerbview/tools/gerbview_control.cpp  | 128 +++++
 gerbview/tools/gerbview_control.h    |  66 +++
 gerbview/tools/selection_tool.cpp    | 959 +++++++++++++++++++++++++++++++++++
 gerbview/tools/selection_tool.h      | 253 +++++++++
 14 files changed, 2819 insertions(+)
 create mode 100644 gerbview/gerber_collectors.cpp
 create mode 100644 gerbview/gerber_collectors.h
 create mode 100644 gerbview/gerbview_draw_panel_gal.cpp
 create mode 100644 gerbview/gerbview_draw_panel_gal.h
 create mode 100644 gerbview/gerbview_painter.cpp
 create mode 100644 gerbview/gerbview_painter.h
 create mode 100644 gerbview/tools/gerbview_actions.cpp
 create mode 100644 gerbview/tools/gerbview_actions.h
 create mode 100644 gerbview/tools/gerbview_control.cpp
 create mode 100644 gerbview/tools/gerbview_control.h
 create mode 100644 gerbview/tools/selection_tool.cpp
 create mode 100644 gerbview/tools/selection_tool.h

diff --git a/AUTHORS.txt b/AUTHORS.txt
index 103047ca8..2129a8113 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -41,6 +41,7 @@ Mateusz Skowroński <skowri[at]gmail-dot-com>
 Cheng Sheng <chengsheng[at]google-dot-com> Google Inc.
 Kristoffer Ödmark <kristoffer.odmark90[at]gmail-dot-com>
 Oliver Walters <oliver.henry.walters[at]gmail-dot-com>
+Jon Evans <jon[at]craftyjon-dot-com>
 
 See also CHANGELOG.txt for contributors.
 
diff --git a/gerbview/CMakeLists.txt b/gerbview/CMakeLists.txt
index be12ffae3..8a6a71b03 100644
--- a/gerbview/CMakeLists.txt
+++ b/gerbview/CMakeLists.txt
@@ -63,6 +63,14 @@ set( GERBVIEW_SRCS
     rs274x.cpp
     select_layers_to_pcb.cpp
     toolbars_gerber.cpp
+
+    gerbview_draw_panel_gal.cpp
+    gerbview_painter.cpp
+
+    tools/gerbview_actions.cpp
+    tools/selection_tool.cpp
+    tools/gerbview_control.cpp
+    gerber_collectors.cpp
     )
 
 set( GERBVIEW_EXTRA_SRCS
diff --git a/gerbview/gerber_collectors.cpp b/gerbview/gerber_collectors.cpp
new file mode 100644
index 000000000..639fcab5e
--- /dev/null
+++ b/gerbview/gerber_collectors.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gerber_collectors.h"
+
+const KICAD_T GERBER_COLLECTOR::AllItems[] = {
+    GERBER_IMAGE_LIST_T,
+    GERBER_IMAGE_T,
+    GERBER_DRAW_ITEM_T,
+    EOT
+};
+
+
+/**
+ * Function Inspect
+ * is the examining function within the INSPECTOR which is passed to the
+ * Iterate function.  Searches and collects all the objects that the old
+ * function PcbGeneralLocateAndDisplay() would find, except that it keeps all
+ * that it finds and does not do any displaying.
+ *
+ * @param testItem An EDA_ITEM to examine.
+ * @param testData not used here.
+ * @return SEARCH_RESULT - SEARCH_QUIT if the Iterator is to stop the scan,
+ *   else SCAN_CONTINUE;
+ */
+SEARCH_RESULT GERBER_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
+{
+    if( testItem->HitTest( m_RefPos ) )
+        Append( testItem );
+
+    return SEARCH_CONTINUE;
+}
+
+
+void GERBER_COLLECTOR::Collect( EDA_ITEM* aItem, const KICAD_T aScanList[],
+                                const wxPoint& aRefPos/*, const COLLECTORS_GUIDE& aGuide*/ )
+{
+    Empty();        // empty the collection, primary criteria list
+
+    // remember guide, pass it to Inspect()
+    //SetGuide( &aGuide );
+
+    SetScanTypes( aScanList );
+
+    // remember where the snapshot was taken from and pass refPos to
+    // the Inspect() function.
+    SetRefPos( aRefPos );
+
+    aItem->Visit( m_inspector, NULL, m_ScanTypes );
+
+    SetTimeNow();               // when snapshot was taken
+
+    // record the length of the primary list before concatenating on to it.
+    m_PrimaryLength = m_List.size();
+}
diff --git a/gerbview/gerber_collectors.h b/gerbview/gerber_collectors.h
new file mode 100644
index 000000000..64714a9b7
--- /dev/null
+++ b/gerbview/gerber_collectors.h
@@ -0,0 +1,134 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GERBER_COLLECTORS_H
+#define GERBER_COLLECTORS_H
+
+#include <class_collector.h>
+
+/**
+ * Class GERBER_COLLECTOR
+ * is intended for use when the right click button is pressed, or when the
+ * plain "arrow" tool is in effect.
+ */
+class GERBER_COLLECTOR : public COLLECTOR
+{
+protected:
+    /**
+     * A place to hold collected objects which don't match precisely the search
+     * criteria, but would be acceptable if nothing else is found.
+     * "2nd" choice, which will be appended to the end of COLLECTOR's prime
+     * "list" at the end of the search.
+     */
+    std::vector<EDA_ITEM*> m_List2nd;
+
+
+    /**
+     * Determines which items are to be collected by Inspect()
+     */
+    //const COLLECTORS_GUIDE* m_Guide;
+
+
+    /**
+     * The number of items that were originally in the primary list before the
+     * m_List2nd was concatenated onto the end of it.
+     */
+    int m_PrimaryLength;
+
+
+public:
+
+    /**
+     * A scan list for all selectable gerber items
+     */
+    static const KICAD_T AllItems[];
+
+    GERBER_COLLECTOR()
+    {
+        //m_Guide = NULL;
+        m_PrimaryLength = 0;
+        SetScanTypes( AllItems );
+    }
+
+    void Empty2nd()
+    {
+        m_List2nd.clear();
+    }
+
+    /*void Append2nd( BOARD_ITEM* item )
+    {
+        m_List2nd.push_back( item );
+    }*/
+
+
+    /**
+     * Function SetGuide
+     * records which COLLECTORS_GUIDE to use.
+     * @param aGuide Which guide to use in the collection.
+     */
+    //void SetGuide( const COLLECTORS_GUIDE* aGuide ) { m_Guide = aGuide; }
+
+
+    /**
+     * Function operator[int]
+     * overloads COLLECTOR::operator[](int) to return a EDA_ITEM* instead of
+     * an EDA_ITEM* type.
+     * @param ndx The index into the list.
+     * @return EDA_ITEM* - or something derived from it, or NULL.
+     */
+    EDA_ITEM* operator[]( int ndx ) const
+    {
+        if( (unsigned)ndx < (unsigned)GetCount() )
+            return (EDA_ITEM*) m_List[ ndx ];
+        return NULL;
+    }
+
+    /**
+     * Function GetPrimaryCount
+     * @return int - The number if items which met the primary search criteria
+     */
+    int GetPrimaryCount() { return m_PrimaryLength; }
+
+    /**
+     * Function Inspect
+     * is the examining function within the INSPECTOR which is passed to the
+     * Iterate function.
+     *
+     * @param testItem An EDA_ITEM to examine.
+     * @param testData is not used in this class.
+     * @return SEARCH_RESULT - SEARCH_QUIT if the Iterator is to stop the scan,
+     *   else SCAN_CONTINUE;
+     */
+    SEARCH_RESULT Inspect( EDA_ITEM* testItem, void* testData )  override;
+
+    /**
+     * Function Collect
+     * scans an EDA_ITEM using this class's Inspector method, which does the collection.
+     * @param aItem An EDA_ITEM to scan
+     * @param aScanList A list of KICAD_Ts with a terminating EOT, that specs
+     *  what is to be collected and the priority order of the resultant
+     *  collection in "m_List".
+     * @param aRefPos A wxPoint to use in hit-testing.
+     * @param aGuide The COLLECTORS_GUIDE to use in collecting items.
+     */
+    void Collect( EDA_ITEM* aItem, const KICAD_T aScanList[],
+                 const wxPoint& aRefPos/*, const COLLECTORS_GUIDE& aGuide */);
+};
+
+#endif
diff --git a/gerbview/gerbview_draw_panel_gal.cpp b/gerbview/gerbview_draw_panel_gal.cpp
new file mode 100644
index 000000000..69e930019
--- /dev/null
+++ b/gerbview/gerbview_draw_panel_gal.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gerbview_draw_panel_gal.h"
+#include <view/view.h>
+#include <view/wx_view_controls.h>
+#include <gerbview_painter.h>
+
+#include <class_colors_design_settings.h>
+#include <gerbview_frame.h>
+#include <class_gbr_display_options.h>
+#include <gal/graphics_abstraction_layer.h>
+
+#include <functional>
+using namespace std::placeholders;
+
+
+GERBVIEW_DRAW_PANEL_GAL::GERBVIEW_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWindowId,
+                                        const wxPoint& aPosition, const wxSize& aSize,
+                                        KIGFX::GAL_DISPLAY_OPTIONS& aOptions, GAL_TYPE aGalType ) :
+EDA_DRAW_PANEL_GAL( aParentWindow, aWindowId, aPosition, aSize, aOptions, aGalType )
+{
+    setDefaultLayerDeps();
+
+    m_painter = new KIGFX::GERBVIEW_PAINTER( m_gal );
+    m_view->SetPainter( m_painter );
+
+    // Load display options (such as filled/outline display of items).
+    auto frame = static_cast< GERBVIEW_FRAME* >( GetParentEDAFrame() );
+
+    if( frame )
+    {
+        auto displ_opts = (GBR_DISPLAY_OPTIONS*) frame->GetDisplayOptions();
+        static_cast<KIGFX::GERBVIEW_RENDER_SETTINGS*>( m_view->GetPainter()->GetSettings() )->LoadDisplayOptions( displ_opts );
+        UseColorScheme( frame->m_colorsSettings );
+    }
+}
+
+
+GERBVIEW_DRAW_PANEL_GAL::~GERBVIEW_DRAW_PANEL_GAL()
+{
+}
+
+
+void GERBVIEW_DRAW_PANEL_GAL::UseColorScheme( const COLORS_DESIGN_SETTINGS* aSettings )
+{
+    KIGFX::GERBVIEW_RENDER_SETTINGS* rs;
+    rs = static_cast<KIGFX::GERBVIEW_RENDER_SETTINGS*>( m_view->GetPainter()->GetSettings() );
+    rs->ImportLegacyColors( aSettings );
+}
+
+
+void GERBVIEW_DRAW_PANEL_GAL::SetHighContrastLayer( int aLayer )
+{
+    // Set display settings for high contrast mode
+    KIGFX::RENDER_SETTINGS* rSettings = m_view->GetPainter()->GetSettings();
+
+    SetTopLayer( aLayer );
+
+    rSettings->ClearActiveLayers();
+    rSettings->SetActiveLayer( aLayer );
+    rSettings->SetActiveLayer( GERBER_DCODE_LAYER( aLayer ) );
+
+    m_view->UpdateAllLayersColor();
+}
+
+
+void GERBVIEW_DRAW_PANEL_GAL::GetMsgPanelInfo( std::vector<MSG_PANEL_ITEM>& aList )
+{
+
+}
+
+
+void GERBVIEW_DRAW_PANEL_GAL::OnShow()
+{
+    GERBVIEW_FRAME* frame = dynamic_cast<GERBVIEW_FRAME*>( GetParent() );
+
+    if( frame )
+    {
+        SetTopLayer( frame->GetActiveLayer() );
+        GBR_DISPLAY_OPTIONS* displ_opts = (GBR_DISPLAY_OPTIONS*) frame->GetDisplayOptions();
+        static_cast<KIGFX::GERBVIEW_RENDER_SETTINGS*>(
+            m_view->GetPainter()->GetSettings() )->LoadDisplayOptions( displ_opts );
+    }
+
+    m_view->RecacheAllItems();
+}
+
+
+bool GERBVIEW_DRAW_PANEL_GAL::SwitchBackend( GAL_TYPE aGalType )
+{
+    bool rv = EDA_DRAW_PANEL_GAL::SwitchBackend( aGalType );
+    setDefaultLayerDeps();
+    return rv;
+}
+
+
+void GERBVIEW_DRAW_PANEL_GAL::setDefaultLayerDeps()
+{
+    // caching makes no sense for Cairo and other software renderers
+    auto target = m_backend == GAL_TYPE_OPENGL ? KIGFX::TARGET_CACHED : KIGFX::TARGET_NONCACHED;
+
+    for( int i = 0; i < KIGFX::VIEW::VIEW_MAX_LAYERS; i++ )
+        m_view->SetLayerTarget( i, target );
+
+    // for( int i = GERBVIEW_LAYER_ID_START; i < GERBVIEW_LAYER_ID_RESERVED; i++ )
+    //     m_view->SetLayerDisplayOnly( i );
+
+    m_view->SetLayerDisplayOnly( LAYER_DCODES );
+    m_view->SetLayerDisplayOnly( LAYER_NEGATIVE_OBJECTS );
+    m_view->SetLayerDisplayOnly( LAYER_GERBVIEW_GRID );
+    m_view->SetLayerDisplayOnly( LAYER_GERBVIEW_AXES );
+    m_view->SetLayerDisplayOnly( LAYER_GERBVIEW_BACKGROUND );
+
+    m_view->SetLayerTarget( LAYER_GP_OVERLAY , KIGFX::TARGET_OVERLAY );
+    m_view->SetLayerDisplayOnly( LAYER_GP_OVERLAY );
+}
+
+
+void GERBVIEW_DRAW_PANEL_GAL::SetTopLayer( int aLayer )
+{
+    m_view->ClearTopLayers();
+
+    for( int i = 0; i < GERBER_DRAWLAYERS_COUNT; ++i )
+    {
+        m_view->SetLayerOrder( GERBER_DCODE_LAYER( GERBER_DRAW_LAYER( i ) ), 2 * i );
+        m_view->SetLayerOrder( i, ( 2 * i ) + 1 );
+    }
+
+    m_view->SetTopLayer( aLayer );
+
+    // Move DCODE layer to the top
+    m_view->SetTopLayer( GERBER_DCODE_LAYER( aLayer ) );
+
+    m_view->SetTopLayer( LAYER_GP_OVERLAY );
+
+    m_view->UpdateAllLayersOrder();
+}
diff --git a/gerbview/gerbview_draw_panel_gal.h b/gerbview/gerbview_draw_panel_gal.h
new file mode 100644
index 000000000..c83def259
--- /dev/null
+++ b/gerbview/gerbview_draw_panel_gal.h
@@ -0,0 +1,64 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GERBVIEW_DRAW_PANEL_GAL_H_
+#define GERBVIEW_DRAW_PANEL_GAL_H_
+
+#include <class_draw_panel_gal.h>
+
+class COLORS_DESIGN_SETTINGS;
+
+
+class GERBVIEW_DRAW_PANEL_GAL : public EDA_DRAW_PANEL_GAL
+{
+public:
+    GERBVIEW_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWindowId, const wxPoint& aPosition,
+                        const wxSize& aSize, KIGFX::GAL_DISPLAY_OPTIONS& aOptions,
+                        GAL_TYPE aGalType = GAL_TYPE_OPENGL );
+
+    virtual ~GERBVIEW_DRAW_PANEL_GAL();
+
+    /**
+     * Function UseColorScheme
+     * Applies layer color settings.
+     * @param aSettings are the new settings.
+     */
+    void UseColorScheme( const COLORS_DESIGN_SETTINGS* aSettings );
+
+    ///> @copydoc EDA_DRAW_PANEL_GAL::SetHighContrastLayer()
+    virtual void SetHighContrastLayer( int aLayer ) override;
+
+    ///> @copydoc EDA_DRAW_PANEL_GAL::GetMsgPanelInfo()
+    void GetMsgPanelInfo( std::vector<MSG_PANEL_ITEM>& aList ) override;
+
+    ///> @copydoc EDA_DRAW_PANEL_GAL::OnShow()
+    void OnShow() override;
+
+    bool SwitchBackend( GAL_TYPE aGalType ) override;
+
+    ///> @copydoc EDA_DRAW_PANEL_GAL::SetTopLayer
+    virtual void SetTopLayer( int aLayer ) override;
+
+protected:
+    ///> Sets rendering targets & dependencies for layers.
+    void setDefaultLayerDeps();
+};
+
+
+#endif /* GERBVIEW_DRAW_PANEL_GAL_H_ */
diff --git a/gerbview/gerbview_painter.cpp b/gerbview/gerbview_painter.cpp
new file mode 100644
index 000000000..bc95af6b7
--- /dev/null
+++ b/gerbview/gerbview_painter.cpp
@@ -0,0 +1,563 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <class_colors_design_settings.h>
+
+#include <gerbview_painter.h>
+#include <gal/graphics_abstraction_layer.h>
+#include <convert_basic_shapes_to_polygon.h>
+#include <convert_to_biu.h>
+#include <gerbview.h>
+
+#include <class_gerber_draw_item.h>
+#include <class_gerber_file_image.h>
+
+using namespace KIGFX;
+
+GERBVIEW_RENDER_SETTINGS::GERBVIEW_RENDER_SETTINGS()
+{
+    m_backgroundColor = COLOR4D( 0.0, 0.0, 0.0, 1.0 );
+
+    m_spotFill          = true;
+    m_lineFill          = true;
+    m_polygonFill       = true;
+    m_showNegativeItems = false;
+    m_showCodes         = false;
+    m_diffMode          = true;
+
+    m_componentHighlightString = "";
+    m_netHighlightString       = "";
+    m_attributeHighlightString = "";
+
+    update();
+}
+
+
+void GERBVIEW_RENDER_SETTINGS::ImportLegacyColors( const COLORS_DESIGN_SETTINGS* aSettings )
+{
+    for( int i = GERBVIEW_LAYER_ID_START;
+         i < GERBVIEW_LAYER_ID_START + GERBER_DRAWLAYERS_COUNT; i++ )
+    {
+        COLOR4D baseColor = aSettings->GetLayerColor( i );
+        m_layerColors[i] = baseColor;
+        m_layerColorsHi[i] = baseColor.Brightened( 0.5 );
+        m_layerColorsSel[i] = baseColor.Brightened( 0.8 );
+        m_layerColorsDark[i] = baseColor.Darkened( 0.25 );
+    }
+
+    for( int i = LAYER_DCODES; i < GERBVIEW_LAYER_ID_END; i++ )
+        m_layerColors[i] = aSettings->GetLayerColor( i );
+
+    for( int i = GAL_LAYER_ID_START; i < GAL_LAYER_ID_END; i++ )
+        m_layerColors[i] = aSettings->GetLayerColor( i );
+
+    update();
+}
+
+
+void GERBVIEW_RENDER_SETTINGS::LoadDisplayOptions( const GBR_DISPLAY_OPTIONS* aOptions )
+{
+    if( aOptions == NULL )
+        return;
+
+    m_spotFill          = aOptions->m_DisplayFlashedItemsFill;
+    m_lineFill          = aOptions->m_DisplayLinesFill;
+    m_polygonFill       = aOptions->m_DisplayPolygonsFill;
+    m_showNegativeItems = aOptions->m_DisplayNegativeObjects;
+    m_showCodes         = aOptions->m_DisplayDCodes;
+    m_diffMode          = aOptions->m_DiffMode;
+    m_hiContrastEnabled = aOptions->m_HighContrastMode;
+
+    update();
+}
+
+
+const COLOR4D& GERBVIEW_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) const
+{
+    const GERBER_DRAW_ITEM* item = static_cast<const GERBER_DRAW_ITEM*>( aItem );
+
+    // All DCODE layers stored under a single color setting
+    if( IsDCodeLayer( aLayer ) )
+        return m_layerColors[ LAYER_DCODES ];
+
+    if( item )
+    {
+        if( item->IsSelected() )
+            return m_layerColorsSel[aLayer];
+    }
+
+    if( !m_netHighlightString.IsEmpty() &&
+        m_netHighlightString == item->GetNetAttributes().m_Netname )
+        return m_layerColorsHi[aLayer];
+
+    if( !m_componentHighlightString.IsEmpty() &&
+        m_componentHighlightString == item->GetNetAttributes().m_Cmpref )
+        return m_layerColorsHi[aLayer];
+
+    if( !m_attributeHighlightString.IsEmpty() && item->GetDcodeDescr() &&
+        m_attributeHighlightString == item->GetDcodeDescr()->m_AperFunction )
+        return m_layerColorsHi[aLayer];
+
+    // Return grayish color for non-highlighted layers in the high contrast mode
+    if( m_hiContrastEnabled && m_activeLayers.count( aLayer ) == 0)
+        return m_hiContrastColor;
+
+    // Catch the case when highlight and high-contraste modes are enabled
+    // and we are drawing a not highlighted track
+    if( m_highlightEnabled )
+        return m_layerColorsDark[aLayer];
+
+    // No special modificators enabled
+    return m_layerColors[aLayer];
+}
+
+
+GERBVIEW_PAINTER::GERBVIEW_PAINTER( GAL* aGal ) :
+    PAINTER( aGal )
+{
+}
+
+
+// TODO(JE): Pull up to PAINTER?
+int GERBVIEW_PAINTER::getLineThickness( int aActualThickness ) const
+{
+    // if items have 0 thickness, draw them with the outline
+    // width, otherwise respect the set value (which, no matter
+    // how small will produce something)
+    if( aActualThickness == 0 )
+        return m_gerbviewSettings.m_outlineWidth;
+
+    return aActualThickness;
+}
+
+
+bool GERBVIEW_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
+{
+    const EDA_ITEM* item = static_cast<const EDA_ITEM*>( aItem );
+
+    // the "cast" applied in here clarifies which overloaded draw() is called
+    switch( item->Type() )
+    {
+    case GERBER_DRAW_ITEM_T:
+        draw( static_cast<GERBER_DRAW_ITEM*>( const_cast<EDA_ITEM*>( item ) ), aLayer );
+        break;
+
+    default:
+        // Painter does not know how to draw the object
+        return false;
+    }
+
+    return true;
+}
+
+
+// TODO(JE) aItem can't be const because of GetDcodeDescr()
+// Probably that can be refactored in GERBER_DRAW_ITEM to allow const here.
+void GERBVIEW_PAINTER::draw( /*const*/ GERBER_DRAW_ITEM* aItem, int aLayer )
+{
+    VECTOR2D start( aItem->GetABPosition( aItem->m_Start ) );   // TODO(JE) Getter
+    VECTOR2D end( aItem->GetABPosition( aItem->m_End ) );       // TODO(JE) Getter
+    int      width = aItem->m_Size.x;   // TODO(JE) Getter
+    bool     isFilled = true;
+    COLOR4D  color;
+    // TODO(JE) This doesn't actually work properly for ImageNegative
+    bool     isNegative = (aItem->GetLayerPolarity() ^ aItem->m_GerberImageFile->m_ImageNegative);
+
+    // Draw DCODEs if enabled
+    if( IsDCodeLayer( aLayer ) )
+    {
+        if( !m_gerbviewSettings.m_showCodes )
+            return;
+
+        wxString codeText;
+        VECTOR2D textPosition;
+        double textSize;
+
+        if( aItem->GetDcodeDescr() )
+            textSize = aItem->GetDcodeDescr()->GetShapeDim( aItem ) / 3.0;
+        else
+            textSize = std::min( aItem->m_Size.x, aItem->m_Size.y ) / 2.0;
+
+        if( aItem->m_Shape == GBR_ARC )
+        {
+            textPosition = start;
+        }
+        else if( aItem->m_Flashed )
+        {
+            BOX2I bb = aItem->ViewBBox();
+            textPosition = bb.Centre();
+        }
+        else
+        {
+
+            textPosition.x = (start.x + end.x) / 2;
+            textPosition.y = (start.y + end.y) / 2;
+        }
+
+        color = m_gerbviewSettings.GetColor( aItem, aLayer );
+        codeText.Printf( wxT( "D%d" ), aItem->m_DCode );
+
+        m_gal->SetIsStroke( true );
+        m_gal->SetIsFill( false );
+        m_gal->SetStrokeColor( color );
+        m_gal->SetFillColor( COLOR4D( 0, 0, 0, 0 ) );
+        m_gal->SetLineWidth( 2 );
+        m_gal->SetFontBold( false );
+        m_gal->SetFontItalic( false );
+        m_gal->SetTextMirrored( false );
+        m_gal->SetGlyphSize( VECTOR2D( textSize, textSize) );
+        m_gal->SetHorizontalJustify( GR_TEXT_HJUSTIFY_CENTER );
+        m_gal->SetVerticalJustify( GR_TEXT_VJUSTIFY_CENTER );
+        m_gal->BitmapText( codeText, textPosition, 0 );
+
+        return;
+    }
+
+    color = m_gerbviewSettings.GetColor( aItem, aLayer );
+
+    if( isNegative )
+    {
+        if( m_gerbviewSettings.m_showNegativeItems )
+            color = m_gerbviewSettings.GetLayerColor( LAYER_NEGATIVE_OBJECTS );
+        else
+            color = COLOR4D( 0, 0, 0, 0 );
+    }
+    else if( m_gerbviewSettings.m_diffMode )
+    {
+        color.a = 0.75;
+    }
+
+    m_gal->SetNegativeDrawMode( isNegative );
+    m_gal->SetStrokeColor( color );
+    m_gal->SetFillColor( color );
+    m_gal->SetIsFill( isFilled );
+    m_gal->SetIsStroke( !isFilled );
+
+    switch( aItem->m_Shape )
+    {
+    case GBR_POLYGON:
+    {
+        isFilled = m_gerbviewSettings.m_polygonFill;
+        m_gal->SetIsFill( isFilled );
+        m_gal->SetIsStroke( !isFilled );
+
+        if( isNegative && !isFilled )
+        {
+            m_gal->SetNegativeDrawMode( false );
+            m_gal->SetStrokeColor( GetSettings()->GetColor( aItem, aLayer ) );
+        }
+
+        if( !isFilled )
+            m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
+
+        SHAPE_POLY_SET absolutePolygon = aItem->m_Polygon;
+
+        for( auto it = absolutePolygon.Iterate( 0 ); it; ++it )
+            *it = aItem->GetABPosition( *it );
+
+        if( !isFilled )
+            m_gal->DrawPolyline( absolutePolygon.COutline( 0 ) );
+        else
+            m_gal->DrawPolygon( absolutePolygon );
+        break;
+    }
+
+    case GBR_CIRCLE:
+    {
+        isFilled = m_gerbviewSettings.m_lineFill;
+        double radius = GetLineLength( aItem->m_Start, aItem->m_End );
+        m_gal->DrawCircle( start, radius );
+        break;
+    }
+
+    case GBR_ARC:
+    {
+        isFilled = m_gerbviewSettings.m_lineFill;
+
+        // Gerber arcs are 3-point (start, center, end)
+        // GAL needs center, radius, start angle, end angle
+        double   radius = GetLineLength( aItem->m_Start, aItem->m_ArcCentre );
+        VECTOR2D center = aItem->GetABPosition( aItem->m_ArcCentre );
+        VECTOR2D startVec = VECTOR2D( aItem->GetABPosition( aItem->m_Start ) ) - center;
+        VECTOR2D endVec = VECTOR2D( aItem->GetABPosition( aItem->m_End ) ) - center;
+
+        m_gal->SetIsFill( isFilled );
+        m_gal->SetIsStroke( !isFilled );
+        m_gal->SetLineWidth( isFilled ? width : m_gerbviewSettings.m_outlineWidth );
+
+        double startAngle = startVec.Angle();
+        double endAngle = endVec.Angle();
+
+        if( endAngle >= M_PI )
+            endAngle *= -1;
+
+        // 360-degree arcs are stored in the file with start equal to end
+        if( aItem->m_Start == aItem->m_End )
+        {
+            startAngle = 0;
+            endAngle = 2 * M_PI;
+        }
+
+        m_gal->DrawArcSegment( center, radius, startAngle, endAngle, width );
+
+        // Arc Debugging
+        // m_gal->SetLineWidth( 5 );
+        // m_gal->SetStrokeColor( COLOR4D( 0.0, 1.0, 0.0, 1.0 ) );
+        // m_gal->DrawLine( center, aItem->GetABPosition( aItem->m_Start ) );
+        // m_gal->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.0, 1.0 ) );
+        // m_gal->DrawLine( center, aItem->GetABPosition( aItem->m_End ) );
+        break;
+    }
+
+    case GBR_SPOT_CIRCLE:
+    case GBR_SPOT_RECT:
+    case GBR_SPOT_OVAL:
+    case GBR_SPOT_POLY:
+    case GBR_SPOT_MACRO:
+    {
+        isFilled = m_gerbviewSettings.m_spotFill;
+        drawFlashedShape( aItem, isFilled );
+        break;
+    }
+
+    case GBR_SEGMENT:
+    {
+        /* Plot a line from m_Start to m_End.
+         * Usually, a round pen is used, but some gerber files use a rectangular pen
+         * In fact, any aperture can be used to plot a line.
+         * currently: only a square pen is handled (I believe using a polygon gives a strange plot).
+         */
+        isFilled = m_gerbviewSettings.m_lineFill;
+        m_gal->SetIsFill( isFilled );
+        m_gal->SetIsStroke( !isFilled );
+
+        if( isNegative && !isFilled )
+            m_gal->SetStrokeColor( GetSettings()->GetColor( aItem, aLayer ) );
+
+        // TODO(JE) Refactor this to allow const aItem
+        D_CODE* code = aItem->GetDcodeDescr();
+        if( code && code->m_Shape == APT_RECT )
+        {
+            if( aItem->m_Polygon.OutlineCount() == 0 )
+                aItem->ConvertSegmentToPolygon();
+            drawPolygon( aItem, aItem->m_Polygon, isFilled );
+        }
+        else
+        {
+            if( !isFilled )
+                m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
+
+            m_gal->DrawSegment( start, end, width );
+        }
+        break;
+    }
+
+    default:
+        wxASSERT_MSG( false, wxT( "GERBER_DRAW_ITEM shape is unknown!" ) );
+        break;
+    }
+
+    // Enable for bounding box debugging
+    #if 0
+    const BOX2I& bb = aItem->ViewBBox();
+    m_gal->SetIsStroke( true );
+    m_gal->SetIsFill( true );
+    m_gal->SetLineWidth( 3 );
+    m_gal->SetStrokeColor( COLOR4D(0.9, 0.9, 0, 0.4) );
+    m_gal->SetFillColor( COLOR4D(0.9, 0.9, 0, 0.1) );
+    m_gal->DrawRectangle( bb.GetOrigin(), bb.GetEnd() );
+    #endif
+}
+
+
+void GERBVIEW_PAINTER::drawPolygon( GERBER_DRAW_ITEM* aParent,
+                                    SHAPE_POLY_SET aPolygon,
+                                    bool aFilled )
+{
+    for( auto it = aPolygon.Iterate( 0 ); it; ++it )
+        *it = aParent->GetABPosition( *it );
+
+    if( !m_gerbviewSettings.m_polygonFill )
+        m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
+
+    if( !aFilled )
+    {
+        for( int i = 0; i < aPolygon.OutlineCount(); i++ )
+            m_gal->DrawPolyline( aPolygon.COutline( i ) );
+    }
+    else
+        m_gal->DrawPolygon( aPolygon );
+}
+
+
+void GERBVIEW_PAINTER::drawFlashedShape( GERBER_DRAW_ITEM* aItem, bool aFilled )
+{
+    D_CODE* code = aItem->GetDcodeDescr();
+
+    wxASSERT_MSG( code, wxT( "drawFlashedShape: Item has no D_CODE!" ) );
+
+    m_gal->SetIsFill( aFilled );
+    m_gal->SetIsStroke( !aFilled );
+    m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
+
+    switch( aItem->m_Shape )
+    {
+    case GBR_SPOT_CIRCLE:
+    {
+        int radius = code->m_Size.x >> 1;
+        VECTOR2D start( aItem->GetABPosition( aItem->m_Start ) );
+
+        if( !aFilled )
+        {
+            m_gal->DrawCircle( start, radius );
+        }
+        else
+        {
+            if( code->m_DrillShape == APT_DEF_NO_HOLE )
+            {
+                m_gal->DrawCircle( start, radius );
+            }
+            else    // rectangular hole
+            {
+                if( code->m_Polygon.OutlineCount() == 0 )
+                    code->ConvertShapeToPolygon();
+
+                SHAPE_POLY_SET poly = code->m_Polygon;
+                poly.Move( aItem->m_Start );
+
+                drawPolygon( aItem, poly, aFilled );
+            }
+        }
+        break;
+    }
+
+    case GBR_SPOT_RECT:
+    {
+        wxPoint codeStart;
+        wxPoint aShapePos = aItem->m_Start;
+        codeStart.x = aShapePos.x - code->m_Size.x / 2;
+        codeStart.y = aShapePos.y - code->m_Size.y / 2;
+        wxPoint codeEnd = codeStart + code->m_Size;
+        codeStart = aItem->GetABPosition( codeStart );
+        codeEnd = aItem->GetABPosition( codeEnd );
+
+        if( !aFilled || code->m_DrillShape == APT_DEF_NO_HOLE  )
+        {
+            m_gal->DrawRectangle( VECTOR2D( codeStart ), VECTOR2D( codeEnd ) );
+        }
+        else
+        {
+            if( code->m_Polygon.OutlineCount() == 0 )
+                code->ConvertShapeToPolygon();
+
+            SHAPE_POLY_SET poly = code->m_Polygon;
+            poly.Move( aItem->m_Start );
+
+            drawPolygon( aItem, poly, aFilled );
+        }
+        break;
+    }
+
+    case GBR_SPOT_OVAL:
+    {
+        int radius = 0;
+
+        wxPoint codeStart = aItem->m_Start;
+        wxPoint codeEnd = aItem->m_Start;
+
+        if( code->m_Size.x > code->m_Size.y )   // horizontal oval
+        {
+            int delta = (code->m_Size.x - code->m_Size.y) / 2;
+            codeStart.x -= delta;
+            codeEnd.x   += delta;
+            radius   = code->m_Size.y;
+        }
+        else   // horizontal oval
+        {
+            int delta = (code->m_Size.y - code->m_Size.x) / 2;
+            codeStart.y -= delta;
+            codeEnd.y   += delta;
+            radius   = code->m_Size.x;
+        }
+
+        codeStart = aItem->GetABPosition( codeStart );
+        codeEnd = aItem->GetABPosition( codeEnd );
+
+        if( !aFilled || code->m_DrillShape == APT_DEF_NO_HOLE )
+        {
+            m_gal->DrawSegment( codeStart, codeEnd, radius );
+        }
+        else
+        {
+            if( code->m_Polygon.OutlineCount() == 0 )
+                code->ConvertShapeToPolygon();
+
+            SHAPE_POLY_SET poly = code->m_Polygon;
+            poly.Move( aItem->m_Start );
+
+            drawPolygon( aItem, poly, aFilled );
+        }
+        break;
+    }
+
+    case GBR_SPOT_POLY:
+    {
+        if( code->m_Polygon.OutlineCount() == 0 )
+            code->ConvertShapeToPolygon();
+
+        SHAPE_POLY_SET poly = code->m_Polygon;
+        poly.Move( aItem->m_Start );
+
+        drawPolygon( aItem, poly, aFilled );
+        break;
+    }
+
+    case GBR_SPOT_MACRO:
+        drawApertureMacro( aItem, aFilled );
+        break;
+
+    default:
+        wxASSERT_MSG( false, wxT( "Unknown Gerber flashed shape!" ) );
+        break;
+    }
+}
+
+
+void GERBVIEW_PAINTER::drawApertureMacro( GERBER_DRAW_ITEM* aParent, bool aFilled )
+{
+    D_CODE* code = aParent->GetDcodeDescr();
+    APERTURE_MACRO* macro = code->GetMacro();
+
+    SHAPE_POLY_SET* macroShape = macro->GetApertureMacroShape( aParent, aParent->m_Start );
+
+    if( !m_gerbviewSettings.m_polygonFill )
+        m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
+
+    if( !aFilled )
+    {
+        for( int i = 0; i < macroShape->OutlineCount(); i++ )
+            m_gal->DrawPolyline( macroShape->COutline( i ) );
+    }
+    else
+        m_gal->DrawPolygon( *macroShape );
+}
+
+
+const double GERBVIEW_RENDER_SETTINGS::MAX_FONT_SIZE = Millimeter2iu( 10.0 );
diff --git a/gerbview/gerbview_painter.h b/gerbview/gerbview_painter.h
new file mode 100644
index 000000000..b167bb49a
--- /dev/null
+++ b/gerbview/gerbview_painter.h
@@ -0,0 +1,203 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GERBVIEW_PAINTER_H
+#define __GERBVIEW_PAINTER_H
+
+#include <layers_id_colors_and_visibility.h>
+#include <painter.h>
+#include <class_gbr_display_options.h>
+#include <geometry/shape_poly_set.h>
+
+#include <memory>
+
+
+class EDA_ITEM;
+class COLORS_DESIGN_SETTINGS;
+
+class GERBER_DRAW_ITEM;
+class GERBER_FILE_IMAGE;
+
+
+namespace KIGFX
+{
+class GAL;
+
+/**
+ * Class GERBVIEW_RENDER_SETTINGS
+ * Stores GerbView specific render settings.
+ */
+class GERBVIEW_RENDER_SETTINGS : public RENDER_SETTINGS
+{
+public:
+    friend class GERBVIEW_PAINTER;
+
+    GERBVIEW_RENDER_SETTINGS();
+
+    /// @copydoc RENDER_SETTINGS::ImportLegacyColors()
+    void ImportLegacyColors( const COLORS_DESIGN_SETTINGS* aSettings ) override;
+
+    /**
+     * Function LoadDisplayOptions
+     * Loads settings related to display options
+     * @param aOptions are settings that you want to use for displaying items.
+     */
+    void LoadDisplayOptions( const GBR_DISPLAY_OPTIONS* aOptions );
+
+    /// @copydoc RENDER_SETTINGS::GetColor()
+    virtual const COLOR4D& GetColor( const VIEW_ITEM* aItem, int aLayer ) const override;
+
+    /**
+     * Function GetLayerColor
+     * Returns the color used to draw a layer.
+     * @param aLayer is the layer number.
+     */
+    inline const COLOR4D& GetLayerColor( int aLayer ) const
+    {
+        return m_layerColors[aLayer];
+    }
+
+    /**
+     * Function SetLayerColor
+     * Changes the color used to draw a layer.
+     * @param aLayer is the layer number.
+     * @param aColor is the new color.
+     */
+    inline void SetLayerColor( int aLayer, const COLOR4D& aColor )
+    {
+        m_layerColors[aLayer] = aColor;
+
+        update();       // recompute other shades of the color
+    }
+
+    inline bool IsSpotFill() const
+    {
+        return m_spotFill;
+    }
+
+    inline bool IsLineFill() const
+    {
+        return m_lineFill;
+    }
+
+    inline bool IsPolygonFill() const
+    {
+        return m_polygonFill;
+    }
+
+    inline bool IsShowNegativeItems() const
+    {
+        return m_showNegativeItems;
+    }
+
+    inline bool IsShowCodes() const
+    {
+        return m_showCodes;
+    }
+
+    inline bool IsDiffMode() const
+    {
+        return m_diffMode;
+    }
+
+    /// If set to anything but an empty string, will highlight items with matching component
+    wxString m_componentHighlightString;
+
+    /// If set to anything but an empty string, will highlight items with matching net
+    wxString m_netHighlightString;
+
+    /// If set to anything but an empty string, will highlight items with matching attribute
+    wxString m_attributeHighlightString;
+
+protected:
+    /// Flag determining if spots should be drawn with fill
+    bool    m_spotFill;
+
+    /// Flag determining if lines should be drawn with fill
+    bool    m_lineFill;
+
+    /// Flag determining if polygons should be drawn with fill
+    bool    m_polygonFill;
+
+    /// Flag determining if negative items should be drawn with a "ghost" color
+    bool    m_showNegativeItems;
+
+    /// Flag determining if D-Codes should be drawn
+    bool    m_showCodes;
+
+    /// Flag determining if layers should be rendered in "diff" mode
+    bool    m_diffMode;
+
+    /// Maximum font size for D-Codes and other strings
+    static const double MAX_FONT_SIZE;
+};
+
+
+/**
+ * Class GERBVIEW_PAINTER
+ * Contains methods for drawing GerbView-specific items.
+ */
+class GERBVIEW_PAINTER : public PAINTER
+{
+public:
+    GERBVIEW_PAINTER( GAL* aGal );
+
+    /// @copydoc PAINTER::ApplySettings()
+    virtual void ApplySettings( const RENDER_SETTINGS* aSettings ) override
+    {
+        m_gerbviewSettings = *static_cast<const GERBVIEW_RENDER_SETTINGS*>( aSettings );
+    }
+
+    /// @copydoc PAINTER::GetSettings()
+    virtual GERBVIEW_RENDER_SETTINGS* GetSettings() override
+    {
+        return &m_gerbviewSettings;
+    }
+
+    /// @copydoc PAINTER::Draw()
+    virtual bool Draw( const VIEW_ITEM* aItem, int aLayer ) override;
+
+protected:
+    GERBVIEW_RENDER_SETTINGS m_gerbviewSettings;
+
+    // Drawing functions
+    void draw( /*const*/ GERBER_DRAW_ITEM* aVia, int aLayer );
+
+    /// Helper routine to draw a polygon
+    void drawPolygon( GERBER_DRAW_ITEM* aParent, SHAPE_POLY_SET aPolygon, bool aFilled );
+
+    /// Helper to draw a flashed shape (aka spot)
+    void drawFlashedShape( GERBER_DRAW_ITEM* aItem, bool aFilled );
+
+    /// Helper to draw an aperture macro shape
+    void drawApertureMacro( GERBER_DRAW_ITEM* aParent, bool aFilled );
+
+    /**
+     * Function getLineThickness()
+     * Get the thickness to draw for a line (e.g. 0 thickness lines
+     * get a minimum value).
+     * @param aActualThickness line own thickness
+     * @return the thickness to draw
+     */
+    int getLineThickness( int aActualThickness ) const;
+};
+} // namespace KIGFX
+
+#endif /* __GERBVIEW_PAINTER_H */
diff --git a/gerbview/tools/gerbview_actions.cpp b/gerbview/tools/gerbview_actions.cpp
new file mode 100644
index 000000000..577c84ea1
--- /dev/null
+++ b/gerbview/tools/gerbview_actions.cpp
@@ -0,0 +1,76 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <tool/tool_manager.h>
+#include <tool/common_tools.h>
+#include <tool/zoom_tool.h>
+#include <gerbview_id.h>
+
+#include "gerbview_actions.h"
+#include "selection_tool.h"
+#include "gerbview_control.h"
+
+
+void GERBVIEW_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager )
+{
+    aToolManager->RegisterTool( new COMMON_TOOLS );
+    aToolManager->RegisterTool( new GERBVIEW_SELECTION_TOOL );
+    aToolManager->RegisterTool( new GERBVIEW_CONTROL );
+    aToolManager->RegisterTool( new ZOOM_TOOL );
+}
+
+boost::optional<TOOL_EVENT> GERBVIEW_ACTIONS::TranslateLegacyId( int aId )
+{
+    switch( aId )
+    {
+    case ID_ZOOM_IN:        // toolbar button "Zoom In"
+        return ACTIONS::zoomInCenter.MakeEvent();
+
+    case ID_ZOOM_OUT:       // toolbar button "Zoom In"
+        return ACTIONS::zoomOutCenter.MakeEvent();
+
+    case ID_ZOOM_PAGE:      // toolbar button "Fit on Screen"
+        return ACTIONS::zoomFitScreen.MakeEvent();
+
+    case ID_ZOOM_SELECTION:
+        return ACTIONS::zoomTool.MakeEvent();
+
+    case ID_TB_MEASUREMENT_TOOL:
+        return GERBVIEW_ACTIONS::measureTool.MakeEvent();
+
+    case ID_NO_TOOL_SELECTED:
+        return GERBVIEW_ACTIONS::selectionTool.MakeEvent();
+
+    case ID_HIGHLIGHT_REMOVE_ALL:
+        return GERBVIEW_ACTIONS::highlightClear.MakeEvent();
+
+    case ID_HIGHLIGHT_CMP_ITEMS:
+        return GERBVIEW_ACTIONS::highlightComponent.MakeEvent();
+
+    case ID_HIGHLIGHT_NET_ITEMS:
+        return GERBVIEW_ACTIONS::highlightNet.MakeEvent();
+
+    case ID_HIGHLIGHT_APER_ATTRIBUTE_ITEMS:
+        return GERBVIEW_ACTIONS::highlightAttribute.MakeEvent();
+        break;
+    }
+
+    return boost::optional<TOOL_EVENT>();
+}
diff --git a/gerbview/tools/gerbview_actions.h b/gerbview/tools/gerbview_actions.h
new file mode 100644
index 000000000..facc333c4
--- /dev/null
+++ b/gerbview/tools/gerbview_actions.h
@@ -0,0 +1,139 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GERBVIEW_ACTIONS_H
+#define __GERBVIEW_ACTIONS_H
+
+#include <tool/tool_action.h>
+#include <tool/actions.h>
+#include <boost/optional.hpp>
+
+class TOOL_EVENT;
+class TOOL_MANAGER;
+
+/**
+ * Class GERBVIEW_ACTIONS
+ *
+ * Gathers all the actions that are shared by tools. The instance of GERBVIEW_ACTIONS is created
+ * inside of ACTION_MANAGER object that registers the actions.
+ */
+class GERBVIEW_ACTIONS : public ACTIONS
+{
+public:
+    // Selection Tool
+    /// Activation of the selection tool
+    static TOOL_ACTION selectionActivate;
+
+    /// Select a single item under the cursor position
+    static TOOL_ACTION selectionCursor;
+
+    /// Clears the current selection
+    static TOOL_ACTION selectionClear;
+
+    /// Selects an item (specified as the event parameter).
+    static TOOL_ACTION selectItem;
+
+    /// Unselects an item (specified as the event parameter).
+    static TOOL_ACTION unselectItem;
+
+    /// Activation of the edit tool
+    static TOOL_ACTION properties;
+
+    static TOOL_ACTION measureTool;
+
+    // View controls
+    static TOOL_ACTION zoomIn;
+    static TOOL_ACTION zoomOut;
+    static TOOL_ACTION zoomInCenter;
+    static TOOL_ACTION zoomOutCenter;
+    static TOOL_ACTION zoomCenter;
+    static TOOL_ACTION zoomFitScreen;
+    static TOOL_ACTION zoomPreset;
+
+    // Display modes
+    static TOOL_ACTION zoneDisplayEnable;
+    static TOOL_ACTION zoneDisplayDisable;
+    static TOOL_ACTION zoneDisplayOutlines;
+    static TOOL_ACTION highContrastMode;
+    static TOOL_ACTION highContrastInc;
+    static TOOL_ACTION highContrastDec;
+
+    // Layer control
+    static TOOL_ACTION layerPrev;
+    static TOOL_ACTION layerAlphaInc;
+    static TOOL_ACTION layerAlphaDec;
+    static TOOL_ACTION layerToggle;
+
+    static TOOL_ACTION layerChanged;        // notification
+
+    // Grid control
+    static TOOL_ACTION gridFast1;
+    static TOOL_ACTION gridFast2;
+    static TOOL_ACTION gridNext;
+    static TOOL_ACTION gridPrev;
+    static TOOL_ACTION gridSetOrigin;
+    static TOOL_ACTION gridResetOrigin;
+    static TOOL_ACTION gridPreset;
+
+    /// Cursor control with keyboard
+    static TOOL_ACTION cursorUp;
+    static TOOL_ACTION cursorDown;
+    static TOOL_ACTION cursorLeft;
+    static TOOL_ACTION cursorRight;
+
+    static TOOL_ACTION cursorUpFast;
+    static TOOL_ACTION cursorDownFast;
+    static TOOL_ACTION cursorLeftFast;
+    static TOOL_ACTION cursorRightFast;
+
+    static TOOL_ACTION cursorClick;
+    static TOOL_ACTION cursorDblClick;
+
+    // Panning with keyboard
+    static TOOL_ACTION panUp;
+    static TOOL_ACTION panDown;
+    static TOOL_ACTION panLeft;
+    static TOOL_ACTION panRight;
+
+    // Miscellaneous
+    static TOOL_ACTION selectionTool;
+    static TOOL_ACTION zoomTool;
+    static TOOL_ACTION panTool;
+    static TOOL_ACTION pickerTool;
+    static TOOL_ACTION resetCoords;
+    static TOOL_ACTION switchCursor;
+    static TOOL_ACTION switchUnits;
+    static TOOL_ACTION showHelp;
+    static TOOL_ACTION toBeDone;
+
+    // Highlighting
+    static TOOL_ACTION highlightClear;
+    static TOOL_ACTION highlightNet;
+    static TOOL_ACTION highlightComponent;
+    static TOOL_ACTION highlightAttribute;
+
+    ///> @copydoc COMMON_ACTIONS::TranslateLegacyId()
+    virtual boost::optional<TOOL_EVENT> TranslateLegacyId( int aId ) override;
+
+    ///> @copydoc COMMON_ACTIONS::RegisterAllTools()
+    virtual void RegisterAllTools( TOOL_MANAGER* aToolManager ) override;
+};
+
+#endif  // __GERBVIEW_ACTIONS_H
diff --git a/gerbview/tools/gerbview_control.cpp b/gerbview/tools/gerbview_control.cpp
new file mode 100644
index 000000000..511771ce4
--- /dev/null
+++ b/gerbview/tools/gerbview_control.cpp
@@ -0,0 +1,128 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <view/view.h>
+#include <gerbview_painter.h>
+#include <gerbview_frame.h>
+#include <tool/tool_manager.h>
+
+#include "gerbview_actions.h"
+#include "gerbview_control.h"
+#include "selection_tool.h"
+
+TOOL_ACTION GERBVIEW_ACTIONS::selectionTool( "gerbview.Control.selectionTool",
+        AS_GLOBAL, 0,
+        "", "", NULL, AF_ACTIVATE );
+
+TOOL_ACTION GERBVIEW_ACTIONS::layerChanged( "gerbview.Control.layerChanged",
+        AS_GLOBAL, 0,
+        "", "", NULL, AF_NOTIFY );
+
+TOOL_ACTION GERBVIEW_ACTIONS::highlightClear( "gerbview.Control.highlightClear",
+        AS_GLOBAL, 0,
+        _( "Clear Highlight" ), "" );
+
+TOOL_ACTION GERBVIEW_ACTIONS::highlightNet( "gerbview.Control.highlightNet",
+        AS_GLOBAL, 0,
+        _( "Highlight Net" ), "" );
+
+TOOL_ACTION GERBVIEW_ACTIONS::highlightComponent( "gerbview.Control.highlightComponent",
+        AS_GLOBAL, 0,
+        _( "Highlight Component" ), "" );
+
+TOOL_ACTION GERBVIEW_ACTIONS::highlightAttribute( "gerbview.Control.highlightAttribute",
+        AS_GLOBAL, 0,
+        _( "Highlight Attribute" ), "" );
+
+GERBVIEW_CONTROL::GERBVIEW_CONTROL() :
+    TOOL_INTERACTIVE( "gerbview.Control" ), m_frame( NULL )
+{
+}
+
+
+GERBVIEW_CONTROL::~GERBVIEW_CONTROL()
+{
+}
+
+
+void GERBVIEW_CONTROL::Reset( RESET_REASON aReason )
+{
+    m_frame = getEditFrame<GERBVIEW_FRAME>();
+}
+
+
+int GERBVIEW_CONTROL::HighlightControl( const TOOL_EVENT& aEvent )
+{
+    auto settings = static_cast<KIGFX::GERBVIEW_PAINTER*>( getView()->GetPainter() )->GetSettings();
+    const auto& selection = m_toolMgr->GetTool<GERBVIEW_SELECTION_TOOL>()->GetSelection();
+    GERBER_DRAW_ITEM* item = NULL;
+
+    if( selection.Size() == 1 )
+    {
+        item = static_cast<GERBER_DRAW_ITEM*>( selection[0] );
+    }
+
+    if( aEvent.IsAction( &GERBVIEW_ACTIONS::highlightClear ) )
+    {
+        m_frame->m_SelComponentBox->SetSelection( 0 );
+        m_frame->m_SelNetnameBox->SetSelection( 0 );
+        m_frame->m_SelAperAttributesBox->SetSelection( 0 );
+
+        settings->m_netHighlightString = "";
+        settings->m_componentHighlightString = "";
+        settings->m_attributeHighlightString = "";
+    }
+    else if( item && aEvent.IsAction( &GERBVIEW_ACTIONS::highlightNet ) )
+    {
+        auto string = item->GetNetAttributes().m_Netname;
+        settings->m_netHighlightString = string;
+        m_frame->m_SelNetnameBox->SetStringSelection( string );
+    }
+    else if( item && aEvent.IsAction( &GERBVIEW_ACTIONS::highlightComponent ) )
+    {
+        auto string = item->GetNetAttributes().m_Cmpref;
+        settings->m_componentHighlightString = string;
+        m_frame->m_SelComponentBox->SetStringSelection( string );
+    }
+    else if( item && aEvent.IsAction( &GERBVIEW_ACTIONS::highlightAttribute ) )
+    {
+        D_CODE* apertDescr = item->GetDcodeDescr();
+        if( apertDescr )
+        {
+            auto string = apertDescr->m_AperFunction;
+            settings->m_attributeHighlightString = string;
+            m_frame->m_SelAperAttributesBox->SetStringSelection( string );
+        }
+    }
+
+    m_frame->GetGalCanvas()->GetView()->RecacheAllItems();
+    m_frame->GetGalCanvas()->Refresh();
+
+    return 0;
+}
+
+
+void GERBVIEW_CONTROL::setTransitions()
+{
+    Go( &GERBVIEW_CONTROL::HighlightControl,   GERBVIEW_ACTIONS::highlightClear.MakeEvent() );
+    Go( &GERBVIEW_CONTROL::HighlightControl,   GERBVIEW_ACTIONS::highlightNet.MakeEvent() );
+    Go( &GERBVIEW_CONTROL::HighlightControl,   GERBVIEW_ACTIONS::highlightComponent.MakeEvent() );
+    Go( &GERBVIEW_CONTROL::HighlightControl,   GERBVIEW_ACTIONS::highlightAttribute.MakeEvent() );
+}
diff --git a/gerbview/tools/gerbview_control.h b/gerbview/tools/gerbview_control.h
new file mode 100644
index 000000000..c8d3496d7
--- /dev/null
+++ b/gerbview/tools/gerbview_control.h
@@ -0,0 +1,66 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GERBVIEW_CONTROL_H
+#define GERBVIEW_CONTROL_H
+
+#include <tool/tool_interactive.h>
+
+
+/**
+ * Class PCBNEW_CONTROL
+ *
+ * Handles actions that are shared between different frames in pcbnew.
+ */
+
+class GERBVIEW_CONTROL : public TOOL_INTERACTIVE
+{
+public:
+    GERBVIEW_CONTROL();
+    ~GERBVIEW_CONTROL();
+
+    /// @copydoc TOOL_INTERACTIVE::Reset()
+    void Reset( RESET_REASON aReason ) override;
+
+    // Display modes
+    int HighContrastMode( const TOOL_EVENT& aEvent );
+    int HighContrastInc( const TOOL_EVENT& aEvent );
+    int HighContrastDec( const TOOL_EVENT& aEvent );
+
+    // Layer control
+    int LayerSwitch( const TOOL_EVENT& aEvent );
+    int LayerNext( const TOOL_EVENT& aEvent );
+    int LayerPrev( const TOOL_EVENT& aEvent );
+    int LayerToggle( const TOOL_EVENT& aEvent );
+    int LayerAlphaInc( const TOOL_EVENT& aEvent );
+    int LayerAlphaDec( const TOOL_EVENT& aEvent );
+
+    // Highlight control
+    int HighlightControl( const TOOL_EVENT& aEvent );
+
+    ///> Sets up handlers for various events.
+    void setTransitions() override;
+
+private:
+    GERBVIEW_FRAME* m_frame;
+
+};
+
+#endif
diff --git a/gerbview/tools/selection_tool.cpp b/gerbview/tools/selection_tool.cpp
new file mode 100644
index 000000000..9e952f460
--- /dev/null
+++ b/gerbview/tools/selection_tool.cpp
@@ -0,0 +1,959 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <limits>
+
+#include <functional>
+using namespace std::placeholders;
+
+#include <base_struct.h>
+
+#include <gerber_collectors.h>
+//#include <confirm.h>
+
+#include <class_draw_panel_gal.h>
+#include <view/view.h>
+#include <view/view_controls.h>
+#include <view/view_group.h>
+#include <painter.h>
+#include <bitmaps.h>
+#include <hotkeys.h>
+
+#include <tool/tool_event.h>
+#include <tool/tool_manager.h>
+#include <preview_items/bright_box.h>
+#include <preview_items/ruler_item.h>
+#include <preview_items/selection_area.h>
+
+#include <gerbview_id.h>
+#include <gerbview_painter.h>
+
+#include "selection_tool.h"
+#include "gerbview_actions.h"
+
+// Selection tool actions
+TOOL_ACTION GERBVIEW_ACTIONS::selectionActivate( "gerbview.InteractiveSelection",
+        AS_GLOBAL, 0,
+        "", "", NULL, AF_ACTIVATE ); // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION GERBVIEW_ACTIONS::selectionCursor( "gerbview.InteractiveSelection.Cursor",
+        AS_GLOBAL, 0,
+        "", "" );    // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION GERBVIEW_ACTIONS::selectItem( "gerbview.InteractiveSelection.SelectItem",
+        AS_GLOBAL, 0,
+        "", "" );    // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION GERBVIEW_ACTIONS::unselectItem( "gerbview.InteractiveSelection.UnselectItem",
+        AS_GLOBAL, 0,
+        "", "" );    // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION GERBVIEW_ACTIONS::selectionClear( "gerbview.InteractiveSelection.Clear",
+        AS_GLOBAL, 0,
+        "", "" );    // No description, it is not supposed to be shown anywhere
+
+TOOL_ACTION GERBVIEW_ACTIONS::measureTool( "gerbview.InteractiveSelection.measureTool",
+        AS_GLOBAL, MD_CTRL + MD_SHIFT + 'M',
+        _( "Measure tool" ), _( "Interactively measure distance between points" ),
+        nullptr, AF_ACTIVATE );
+
+
+class HIGHLIGHT_MENU: public CONTEXT_MENU
+{
+public:
+    HIGHLIGHT_MENU()
+    {
+        SetTitle( _( "Highlight..." ) );
+    }
+
+private:
+
+    void update() override
+    {
+        const auto& selection = getToolManager()->GetTool<GERBVIEW_SELECTION_TOOL>()->GetSelection();
+
+        if( selection.Size() == 1 )
+        {
+            auto item = static_cast<GERBER_DRAW_ITEM*>( selection[0] );
+            const auto& net_attr = item->GetNetAttributes();
+
+            if( ( net_attr.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_PAD ) ||
+                ( net_attr.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_CMP ) )
+            {
+                auto menuEntry = Add( GERBVIEW_ACTIONS::highlightComponent );
+                menuEntry->SetItemLabel( wxString::Format( _( "Highlight items of component '%s'" ),
+                                         GetChars( net_attr.m_Cmpref ) ) );
+            }
+
+            if( ( net_attr.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_NET ) )
+            {
+                auto menuEntry = Add( GERBVIEW_ACTIONS::highlightNet );
+                menuEntry->SetItemLabel( wxString::Format( _( "Highlight items of net '%s'" ),
+                                         GetChars( net_attr.m_Netname ) ) );
+            }
+
+            D_CODE* apertDescr = item->GetDcodeDescr();
+
+            if( apertDescr && !apertDescr->m_AperFunction.IsEmpty() )
+            {
+                auto menuEntry = Add( GERBVIEW_ACTIONS::highlightAttribute );
+                menuEntry->SetItemLabel( wxString::Format( _( "Highlight aperture type '%s'" ),
+                                         GetChars( apertDescr->m_AperFunction ) ) );
+            }
+        }
+
+        Add( GERBVIEW_ACTIONS::highlightClear );
+    }
+
+    CONTEXT_MENU* create() const override
+    {
+        return new HIGHLIGHT_MENU();
+    }
+};
+
+
+GERBVIEW_SELECTION_TOOL::GERBVIEW_SELECTION_TOOL() :
+        TOOL_INTERACTIVE( "gerbview.InteractiveSelection" ),
+        m_frame( NULL ), m_additive( false ), m_subtractive( false ),
+        m_multiple( false ),
+        m_menu( *this )
+{
+}
+
+
+GERBVIEW_SELECTION_TOOL::~GERBVIEW_SELECTION_TOOL()
+{
+    getView()->Remove( &m_selection );
+}
+
+
+bool GERBVIEW_SELECTION_TOOL::Init()
+{
+    auto selectMenu = std::make_shared<HIGHLIGHT_MENU>();
+    selectMenu->SetTool( this );
+    m_menu.AddSubMenu( selectMenu );
+
+    auto& menu = m_menu.GetMenu();
+
+    menu.AddMenu( selectMenu.get(), false );
+    menu.AddSeparator( SELECTION_CONDITIONS::ShowAlways, 1000 );
+
+    m_menu.AddStandardSubMenus( *getEditFrame<GERBVIEW_FRAME>() );
+
+    return true;
+}
+
+
+void GERBVIEW_SELECTION_TOOL::Reset( RESET_REASON aReason )
+{
+    m_frame = getEditFrame<GERBVIEW_FRAME>();
+    m_preliminary = true;
+
+    if( aReason == TOOL_BASE::MODEL_RELOAD )
+    {
+        // Remove pointers to the selected items from containers
+        // without changing their properties (as they are already deleted
+        // while a new file is loaded)
+        m_selection.Clear();
+        getView()->GetPainter()->GetSettings()->SetHighlight( false );
+    }
+    else
+        // Restore previous properties of selected items and remove them from containers
+        clearSelection();
+
+    // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
+    getView()->Remove( &m_selection );
+    getView()->Add( &m_selection );
+}
+
+
+int GERBVIEW_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
+{
+    // Main loop: keep receiving events
+    while( OPT_TOOL_EVENT evt = Wait() )
+    {
+        // This is kind of hacky: activate RMB drag on any event.
+        // There doesn't seem to be any other good way to tell when another tool
+        // is canceled and control returns to the selection tool, except by the
+        // fact that the selection tool starts to get events again.
+        if( m_frame->GetToolId() == ID_NO_TOOL_SELECTED)
+        {
+            getViewControls()->SetAdditionalPanButtons( false, true );
+        }
+
+        // Disable RMB pan for other tools; they can re-enable if desired
+        if( evt->IsActivate() )
+        {
+            getViewControls()->SetAdditionalPanButtons( false, false );
+        }
+
+        // single click? Select single object
+        if( evt->IsClick( BUT_LEFT ) )
+        {
+            if( !m_additive )
+                clearSelection();
+
+            selectPoint( evt->Position() );
+        }
+
+        // right click? if there is any object - show the context menu
+        else if( evt->IsClick( BUT_RIGHT ) )
+        {
+            if( m_selection.Empty() )
+            {
+                selectPoint( evt->Position() );
+                m_selection.SetIsHover( true );
+            }
+
+            m_menu.ShowContextMenu( m_selection );
+        }
+
+        else if( evt->IsCancel() || evt->Action() == TA_UNDO_REDO_PRE )
+        {
+            clearSelection();
+        }
+
+        else if( evt->Action() == TA_CONTEXT_MENU_CLOSED )
+        {
+            m_menu.CloseContextMenu( evt );
+        }
+    }
+
+    // This tool is supposed to be active forever
+    assert( false );
+
+    return 0;
+}
+
+
+SELECTION& GERBVIEW_SELECTION_TOOL::GetSelection()
+{
+    return m_selection;
+}
+
+
+SELECTION& GERBVIEW_SELECTION_TOOL::RequestSelection( int aFlags )
+{
+    if( m_selection.Empty() )
+    {
+        m_toolMgr->RunAction( GERBVIEW_ACTIONS::selectionCursor, true, 0 );
+        m_selection.SetIsHover( true );
+    }
+
+    // Be careful with iterators: items can be removed from list
+    // that invalidate iterators.
+    for( unsigned ii = 0; ii < m_selection.GetSize(); ii++ )
+    {
+        EDA_ITEM* item = m_selection[ii];
+
+        if( ( aFlags & SELECTION_EDITABLE ) && item->Type() == PCB_MARKER_T )
+        {
+            unselect( static_cast<EDA_ITEM *>( item ) );
+        }
+    }
+
+    return m_selection;
+}
+
+
+void GERBVIEW_SELECTION_TOOL::toggleSelection( EDA_ITEM* aItem )
+{
+    if( aItem->IsSelected() )
+    {
+        unselect( aItem );
+
+        // Inform other potentially interested tools
+        m_toolMgr->ProcessEvent( UnselectedEvent );
+    }
+    else
+    {
+        if( !m_additive )
+            clearSelection();
+
+        // Prevent selection of invisible or inactive items
+        if( selectable( aItem ) )
+        {
+            select( aItem );
+
+            // Inform other potentially interested tools
+            m_toolMgr->ProcessEvent( SelectedEvent );
+        }
+    }
+
+    m_frame->GetGalCanvas()->ForceRefresh();
+}
+
+
+bool GERBVIEW_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag )
+{
+    EDA_ITEM* item = NULL;
+    GERBER_COLLECTOR collector;
+    EDA_ITEM* model = getModel<EDA_ITEM>();
+
+    collector.Collect( model, GERBER_COLLECTOR::AllItems, wxPoint( aWhere.x, aWhere.y ) );
+
+    bool anyCollected = collector.GetCount() != 0;
+
+    // Remove unselectable items
+    for( int i = collector.GetCount() - 1; i >= 0; --i )
+    {
+        if( !selectable( collector[i] ) )
+            collector.Remove( i );
+    }
+
+    switch( collector.GetCount() )
+    {
+    case 0:
+        if( !m_additive && anyCollected )
+            clearSelection();
+
+        return false;
+
+    case 1:
+        toggleSelection( collector[0] );
+
+        return true;
+
+    default:
+        // Let's see if there is still disambiguation in selection..
+        if( collector.GetCount() == 1 )
+        {
+            toggleSelection( collector[0] );
+
+            return true;
+        }
+        else if( collector.GetCount() > 1 )
+        {
+            if( aOnDrag )
+                Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) );
+
+            item = disambiguationMenu( &collector );
+
+            if( item )
+            {
+                toggleSelection( item );
+
+                return true;
+            }
+        }
+        break;
+    }
+
+    return false;
+}
+
+
+bool GERBVIEW_SELECTION_TOOL::selectCursor( bool aSelectAlways )
+{
+    if( aSelectAlways || m_selection.Empty() )
+    {
+        clearSelection();
+        selectPoint( getViewControls()->GetCursorPosition( false ) );
+    }
+
+    return !m_selection.Empty();
+}
+
+
+bool GERBVIEW_SELECTION_TOOL::selectMultiple()
+{
+    bool cancelled = false;     // Was the tool cancelled while it was running?
+    m_multiple = true;          // Multiple selection mode is active
+    KIGFX::VIEW* view = getView();
+    getViewControls()->SetAutoPan( true );
+
+    KIGFX::PREVIEW::SELECTION_AREA area;
+    view->Add( &area );
+
+    while( OPT_TOOL_EVENT evt = Wait() )
+    {
+        if( evt->IsCancel() )
+        {
+            cancelled = true;
+            break;
+        }
+
+        if( evt->IsDrag( BUT_LEFT ) )
+        {
+
+            // Start drawing a selection box
+            area.SetOrigin( evt->DragOrigin() );
+            area.SetEnd( evt->Position() );
+            area.SetAdditive( m_additive );
+            area.SetSubtractive( m_subtractive );
+
+            view->SetVisible( &area, true );
+            view->Update( &area );
+        }
+
+        if( evt->IsMouseUp( BUT_LEFT ) )
+        {
+            // End drawing the selection box
+            view->SetVisible( &area, false );
+
+            // Mark items within the selection box as selected
+            std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
+
+            // Filter the view items based on the selection box
+            BOX2I selectionBox = area.ViewBBox();
+            view->Query( selectionBox, selectedItems );         // Get the list of selected items
+
+            std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
+
+            int width = area.GetEnd().x - area.GetOrigin().x;
+            int height = area.GetEnd().y - area.GetOrigin().y;
+
+            // Construct an EDA_RECT to determine EDA_ITEM selection
+            EDA_RECT selectionRect( wxPoint( area.GetOrigin().x, area.GetOrigin().y ),
+                                    wxSize( width, height ) );
+
+            selectionRect.Normalize();
+
+            for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
+            {
+                auto item = static_cast<GERBER_DRAW_ITEM*>( it->first );
+
+                if( !item || !selectable( item ) )
+                    continue;
+
+                /* Selection mode depends on direction of drag-selection:
+                 * Left > Right : Select objects that are fully enclosed by selection
+                 * Right > Left : Select objects that are crossed by selection
+                 */
+
+                if( width >= 0 )
+                {
+                    if( selectionBox.Contains( item->ViewBBox() ) )
+                    {
+                        if( m_subtractive )
+                            unselect( item );
+                        else
+                            select( item );
+                    }
+                }
+                else
+                {
+                    if( item->HitTest( selectionRect ) )
+                    {
+                        if( m_subtractive )
+                            unselect( item );
+                        else
+                            select( item );
+                    }
+
+                }
+            }
+
+            if( m_selection.Size() == 1 )
+                m_frame->SetCurItem( static_cast<GERBER_DRAW_ITEM*>( m_selection.Front() ) );
+            else
+                m_frame->SetCurItem( NULL );
+
+            // Inform other potentially interested tools
+            if( !m_selection.Empty() )
+                m_toolMgr->ProcessEvent( SelectedEvent );
+
+            break;  // Stop waiting for events
+        }
+    }
+
+    // Stop drawing the selection box
+    view->Remove( &area );
+    m_multiple = false;         // Multiple selection mode is inactive
+    getViewControls()->SetAutoPan( false );
+
+    return cancelled;
+}
+
+
+void GERBVIEW_SELECTION_TOOL::setTransitions()
+{
+    Go( &GERBVIEW_SELECTION_TOOL::Main,             GERBVIEW_ACTIONS::selectionActivate.MakeEvent() );
+    Go( &GERBVIEW_SELECTION_TOOL::CursorSelection,  GERBVIEW_ACTIONS::selectionCursor.MakeEvent() );
+    Go( &GERBVIEW_SELECTION_TOOL::ClearSelection,   GERBVIEW_ACTIONS::selectionClear.MakeEvent() );
+    Go( &GERBVIEW_SELECTION_TOOL::SelectItem,       GERBVIEW_ACTIONS::selectItem.MakeEvent() );
+    Go( &GERBVIEW_SELECTION_TOOL::UnselectItem,     GERBVIEW_ACTIONS::unselectItem.MakeEvent() );
+    Go( &GERBVIEW_SELECTION_TOOL::MeasureTool,      GERBVIEW_ACTIONS::measureTool.MakeEvent() );
+}
+
+
+int GERBVIEW_SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
+{
+    if( m_selection.Empty() )                        // Try to find an item that could be modified
+    {
+        selectCursor( true );
+
+        clearSelection();
+        return 0;
+    }
+
+    return 0;
+}
+
+
+int GERBVIEW_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
+{
+    clearSelection();
+
+    return 0;
+}
+
+
+int GERBVIEW_SELECTION_TOOL::SelectItems( const TOOL_EVENT& aEvent )
+{
+    std::vector<EDA_ITEM*>* items = aEvent.Parameter<std::vector<EDA_ITEM*>*>();
+
+    if( items )
+    {
+        // Perform individual selection of each item
+        // before processing the event.
+        for( auto item : *items )
+        {
+            select( item );
+        }
+
+        m_toolMgr->ProcessEvent( SelectedEvent );
+    }
+
+    return 0;
+}
+
+
+int GERBVIEW_SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent )
+{
+    // Check if there is an item to be selected
+    EDA_ITEM* item = aEvent.Parameter<EDA_ITEM*>();
+
+    if( item )
+    {
+        select( item );
+
+        // Inform other potentially interested tools
+        m_toolMgr->ProcessEvent( SelectedEvent );
+    }
+
+    return 0;
+}
+
+
+int GERBVIEW_SELECTION_TOOL::UnselectItems( const TOOL_EVENT& aEvent )
+{
+    std::vector<EDA_ITEM*>* items = aEvent.Parameter<std::vector<EDA_ITEM*>*>();
+
+    if( items )
+    {
+        // Perform individual unselection of each item
+        // before processing the event
+        for( auto item : *items )
+        {
+            unselect( item );
+        }
+
+        m_toolMgr->ProcessEvent( UnselectedEvent );
+    }
+
+    return 0;
+}
+
+
+int GERBVIEW_SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent )
+{
+    // Check if there is an item to be selected
+    EDA_ITEM* item = aEvent.Parameter<EDA_ITEM*>();
+
+    if( item )
+    {
+        unselect( item );
+
+        // Inform other potentially interested tools
+        m_toolMgr->ProcessEvent( UnselectedEvent );
+    }
+
+    return 0;
+}
+
+
+void GERBVIEW_SELECTION_TOOL::clearSelection()
+{
+    if( m_selection.Empty() )
+        return;
+
+    for( auto item : m_selection )
+        unselectVisually( static_cast<EDA_ITEM*>( item ) );
+
+    m_selection.Clear();
+
+    m_frame->SetCurItem( NULL );
+
+    // Inform other potentially interested tools
+    m_toolMgr->ProcessEvent( ClearedEvent );
+}
+
+
+void GERBVIEW_SELECTION_TOOL::zoomFitSelection( void )
+{
+    //Should recalculate the view to zoom in on the selection
+    auto selectionBox = m_selection.ViewBBox();
+    auto canvas = m_frame->GetGalCanvas();
+    auto view = getView();
+
+    VECTOR2D screenSize = view->ToWorld( canvas->GetClientSize(), false );
+
+    if( !( selectionBox.GetWidth() == 0 ) || !( selectionBox.GetHeight() == 0 ) )
+    {
+        VECTOR2D vsize = selectionBox.GetSize();
+        double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
+                fabs( vsize.y / screenSize.y ) );
+        view->SetScale( scale );
+        view->SetCenter( selectionBox.Centre() );
+        view->Add( &m_selection );
+    }
+
+    m_frame->GetGalCanvas()->ForceRefresh();
+}
+
+
+EDA_ITEM* GERBVIEW_SELECTION_TOOL::disambiguationMenu( GERBER_COLLECTOR* aCollector )
+{
+    EDA_ITEM* current = NULL;
+    BRIGHT_BOX brightBox;
+    CONTEXT_MENU menu;
+
+    getView()->Add( &brightBox );
+
+    int limit = std::min( 10, aCollector->GetCount() );
+
+    for( int i = 0; i < limit; ++i )
+    {
+        wxString text;
+        EDA_ITEM* item = ( *aCollector )[i];
+        text = item->GetSelectMenuText();
+        menu.Add( text, i + 1 );
+    }
+
+    menu.SetTitle( _( "Clarify selection" ) );
+    menu.DisplayTitle( true );
+    SetContextMenu( &menu, CMENU_NOW );
+
+    while( OPT_TOOL_EVENT evt = Wait() )
+    {
+        if( evt->Action() == TA_CONTEXT_MENU_UPDATE )
+        {
+            if( current )
+                current->ClearBrightened();
+
+            int id = *evt->GetCommandId();
+
+            // User has pointed an item, so show it in a different way
+            if( id > 0 && id <= limit )
+            {
+                current = ( *aCollector )[id - 1];
+                current->SetBrightened();
+            }
+            else
+            {
+                current = NULL;
+            }
+        }
+        else if( evt->Action() == TA_CONTEXT_MENU_CHOICE )
+        {
+            boost::optional<int> id = evt->GetCommandId();
+
+            // User has selected an item, so this one will be returned
+            if( id && ( *id > 0 ) )
+                current = ( *aCollector )[*id - 1];
+            else
+                current = NULL;
+
+            break;
+        }
+
+        // Draw a mark to show which item is available to be selected
+        if( current && current->IsBrightened() )
+        {
+            brightBox.SetItem( current );
+            getView()->SetVisible( &brightBox, true );
+//          getView()->Hide( &brightBox, false );
+            getView()->Update( &brightBox, KIGFX::GEOMETRY );
+            getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
+        }
+    }
+
+    getView()->Remove( &brightBox );
+
+    return current;
+}
+
+
+bool GERBVIEW_SELECTION_TOOL::selectable( const EDA_ITEM* aItem ) const
+{
+    auto item = static_cast<const GERBER_DRAW_ITEM*>( aItem );
+
+    if( item->GetLayerPolarity() )
+    {
+        // Don't allow selection of invisible negative items
+        auto rs = static_cast<KIGFX::GERBVIEW_RENDER_SETTINGS*>( getView()->GetPainter()->GetSettings() );
+        if( !rs->IsShowNegativeItems() )
+            return false;
+    }
+
+    return getEditFrame<GERBVIEW_FRAME>()->IsLayerVisible( item->GetLayer() );
+}
+
+
+void GERBVIEW_SELECTION_TOOL::select( EDA_ITEM* aItem )
+{
+    if( aItem->IsSelected() )
+    {
+        return;
+    }
+
+    selectVisually( aItem );
+    m_selection.Add( aItem );
+    getView()->Add( &m_selection );
+
+    if( m_selection.Size() == 1 )
+    {
+        // Set as the current item, so the information about selection is displayed
+        m_frame->SetCurItem( static_cast<GERBER_DRAW_ITEM*>( aItem ), true );
+    }
+    else if( m_selection.Size() == 2 )  // Check only for 2, so it will not be
+    {                                   // called for every next selected item
+        // If multiple items are selected, do not show the information about the selected item
+        m_frame->SetCurItem( NULL, true );
+    }
+}
+
+
+void GERBVIEW_SELECTION_TOOL::unselect( EDA_ITEM* aItem )
+{
+    if( !aItem->IsSelected() )
+        return;
+
+    unselectVisually( aItem );
+    m_selection.Remove( aItem );
+
+    if( m_selection.Empty() )
+    {
+        m_frame->SetCurItem( NULL );
+        getView()->Remove( &m_selection );
+    }
+}
+
+
+void GERBVIEW_SELECTION_TOOL::selectVisually( EDA_ITEM* aItem ) const
+{
+    // Move the item's layer to the front
+    int layer = static_cast<GERBER_DRAW_ITEM*>( aItem )->GetLayer();
+    m_frame->GetGalCanvas()->SetTopLayer( GERBER_DRAW_LAYER( layer ) );
+
+    // Hide the original item, so it is shown only on overlay
+    aItem->SetSelected();
+    getView()->Hide( aItem, true );
+    getView()->Update( aItem, KIGFX::GEOMETRY );
+}
+
+
+void GERBVIEW_SELECTION_TOOL::unselectVisually( EDA_ITEM* aItem ) const
+{
+    // Restore original item visibility
+    aItem->ClearSelected();
+    getView()->Hide( aItem, false );
+    getView()->Update( aItem, KIGFX::ALL );
+}
+
+
+bool GERBVIEW_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
+{
+    const unsigned GRIP_MARGIN = 20;
+    VECTOR2D margin = getView()->ToWorld( VECTOR2D( GRIP_MARGIN, GRIP_MARGIN ), false );
+
+    // Check if the point is located within any of the currently selected items bounding boxes
+    for( auto item : m_selection )
+    {
+        BOX2I itemBox = item->ViewBBox();
+        itemBox.Inflate( margin.x, margin.y );    // Give some margin for gripping an item
+
+        if( itemBox.Contains( aPoint ) )
+            return true;
+    }
+
+    return false;
+}
+
+
+int GERBVIEW_SELECTION_TOOL::MeasureTool( const TOOL_EVENT& aEvent )
+{
+    auto& view = *getView();
+    auto& controls = *getViewControls();
+
+    Activate();
+    getEditFrame<GERBVIEW_FRAME>()->SetToolID( ID_TB_MEASUREMENT_TOOL,
+                        wxCURSOR_PENCIL, _( "Measure distance between two points" ) );
+
+    KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER twoPtMgr;
+    KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr );
+
+    view.Add( &ruler );
+    view.SetVisible( &ruler, false );
+
+    bool originSet = false;
+
+    controls.ShowCursor( true );
+    controls.SetSnapping( true );
+    getViewControls()->SetAdditionalPanButtons( false, true );
+
+    while( auto evt = Wait() )
+    {
+        const VECTOR2I cursorPos = controls.GetCursorPosition();
+
+        if( evt->IsCancel() || evt->IsActivate() )
+        {
+            break;
+        }
+
+        // click or drag starts
+        else if( !originSet &&
+                ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
+        {
+            if( !evt->IsDrag( BUT_LEFT ) )
+            {
+                twoPtMgr.SetOrigin( cursorPos );
+                twoPtMgr.SetEnd( cursorPos );
+            }
+
+            controls.CaptureCursor( true );
+            controls.SetAutoPan( true );
+
+            originSet = true;
+        }
+
+        else if( !originSet && evt->IsMotion() )
+        {
+            // make sure the origin is set before a drag starts
+            // otherwise you can miss a step
+            twoPtMgr.SetOrigin( cursorPos );
+            twoPtMgr.SetEnd( cursorPos );
+        }
+
+        // second click or mouse up after drag ends
+        else if( originSet &&
+                ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
+        {
+            originSet = false;
+
+            controls.SetAutoPan( false );
+            controls.CaptureCursor( false );
+
+            view.SetVisible( &ruler, false );
+        }
+
+        // move or drag when origin set updates rules
+        else if( originSet &&
+                ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
+        {
+            twoPtMgr.SetAngleSnap( evt->Modifier( MD_CTRL ) );
+            twoPtMgr.SetEnd( cursorPos );
+
+            view.SetVisible( &ruler, true );
+            view.Update( &ruler, KIGFX::GEOMETRY );
+        }
+
+        else if( evt->IsClick( BUT_RIGHT ) )
+        {
+            m_menu.ShowContextMenu( m_selection );
+        }
+    }
+
+    view.SetVisible( &ruler, false );
+    view.Remove( &ruler );
+    getViewControls()->SetAdditionalPanButtons( false, false );
+
+    getEditFrame<GERBVIEW_FRAME>()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+    return 0;
+}
+
+
+VECTOR2I SELECTION::GetCenter() const
+{
+    VECTOR2I centre;
+
+    if( Size() == 1 )
+    {
+        centre = static_cast<GERBER_DRAW_ITEM*>( Front() )->GetPosition();
+    }
+    else
+    {
+        EDA_RECT bbox = Front()->GetBoundingBox();
+        auto i = m_items.begin();
+        ++i;
+
+        for( ; i != m_items.end(); ++i )
+        {
+            bbox.Merge( (*i)->GetBoundingBox() );
+        }
+
+        centre = bbox.Centre();
+    }
+
+    return centre;
+}
+
+
+const BOX2I SELECTION::ViewBBox() const
+{
+    EDA_RECT eda_bbox;
+
+    if( Size() == 1 )
+    {
+        eda_bbox = Front()->GetBoundingBox();
+    }
+    else if( Size() > 1 )
+    {
+        eda_bbox = Front()->GetBoundingBox();
+        auto i = m_items.begin();
+        ++i;
+
+        for( ; i != m_items.end(); ++i )
+        {
+            eda_bbox.Merge( (*i)->GetBoundingBox() );
+        }
+    }
+
+    return BOX2I( eda_bbox.GetOrigin(), eda_bbox.GetSize() );
+}
+
+
+const KIGFX::VIEW_GROUP::ITEMS SELECTION::updateDrawList() const
+{
+    std::vector<VIEW_ITEM*> items;
+
+    for( auto item : m_items )
+        items.push_back( item );
+
+    return items;
+}
+
+
+
+const TOOL_EVENT GERBVIEW_SELECTION_TOOL::SelectedEvent( TC_MESSAGE, TA_ACTION, "gerbview.InteractiveSelection.selected" );
+const TOOL_EVENT GERBVIEW_SELECTION_TOOL::UnselectedEvent( TC_MESSAGE, TA_ACTION, "gerbview.InteractiveSelection.unselected" );
+const TOOL_EVENT GERBVIEW_SELECTION_TOOL::ClearedEvent( TC_MESSAGE, TA_ACTION, "gerbview.InteractiveSelection.cleared" );
diff --git a/gerbview/tools/selection_tool.h b/gerbview/tools/selection_tool.h
new file mode 100644
index 000000000..dc0956f96
--- /dev/null
+++ b/gerbview/tools/selection_tool.h
@@ -0,0 +1,253 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Jon Evans <jon@xxxxxxxxxxxxx>
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GERBVIEW_SELECTION_TOOL_H
+#define __GERBVIEW_SELECTION_TOOL_H
+
+#include <memory>
+#include <math/vector2d.h>
+
+#include <tool/tool_interactive.h>
+#include <tool/context_menu.h>
+#include <tool/selection.h>
+#include <tool/selection_conditions.h>
+#include <tool/tool_menu.h>
+
+#include <gerbview_frame.h>
+
+class SELECTION_AREA;
+class GERBER_COLLECTOR;
+
+namespace KIGFX
+{
+    class GAL;
+}
+
+
+/**
+ * Class GERBVIEW_SELECTION_TOOL
+ *
+ * Selection tool for GerbView, based on the one in PcbNew
+ */
+class GERBVIEW_SELECTION_TOOL : public TOOL_INTERACTIVE
+{
+public:
+    GERBVIEW_SELECTION_TOOL();
+    ~GERBVIEW_SELECTION_TOOL();
+
+    /// @copydoc TOOL_BASE::Init()
+    bool Init() override;
+
+    /// @copydoc TOOL_BASE::Reset()
+    void Reset( RESET_REASON aReason ) override;
+
+    /**
+     * Function Main()
+     *
+     * The main loop.
+     */
+    int Main( const TOOL_EVENT& aEvent );
+
+    /**
+     * Function GetSelection()
+     *
+     * Returns the set of currently selected items.
+     */
+    SELECTION& GetSelection();
+
+    /**
+     * Function RequestSelection()
+     *
+     * Returns the current selection set, filtered according to aFlags.
+     * If the set is empty, performs the legacy-style hover selection.
+     */
+    SELECTION& RequestSelection( int aFlags = SELECTION_DEFAULT );
+
+    inline TOOL_MENU& GetToolMenu()
+    {
+        return m_menu;
+    }
+
+    ///> Select a single item under cursor event handler.
+    int CursorSelection( const TOOL_EVENT& aEvent );
+
+    ///> Clear current selection event handler.
+    int ClearSelection( const TOOL_EVENT& aEvent );
+
+    ///> Item selection event handler.
+    int SelectItem( const TOOL_EVENT& aEvent );
+
+    ///> Multiple item selection event handler
+    int SelectItems( const TOOL_EVENT& aEvent );
+
+    ///> Item unselection event handler.
+    int UnselectItem( const TOOL_EVENT& aEvent );
+
+    ///> Multiple item unselection event handler
+    int UnselectItems( const TOOL_EVENT& aEvent );
+
+    ///> Launches a tool to measure between points
+    int MeasureTool( const TOOL_EVENT& aEvent );
+
+    ///> Event sent after an item is selected.
+    static const TOOL_EVENT SelectedEvent;
+
+    ///> Event sent after an item is unselected.
+    static const TOOL_EVENT UnselectedEvent;
+
+    ///> Event sent after selection is cleared.
+    static const TOOL_EVENT ClearedEvent;
+
+    ///> Sets up handlers for various events.
+    void setTransitions() override;
+
+    ///> Zooms the screen to center and fit the current selection.
+    void zoomFitSelection( void );
+
+private:
+    /**
+     * Function selectPoint()
+     * Selects an item pointed by the parameter aWhere. If there is more than one item at that
+     * place, there is a menu displayed that allows to choose the item.
+     *
+     * @param aWhere is the place where the item should be selected.
+     * @param aAllowDisambiguation decides what to do in case of disambiguation. If true, then
+     * a menu is shown, otherise function finishes without selecting anything.
+     * @return True if an item was selected, false otherwise.
+     */
+    bool selectPoint( const VECTOR2I& aWhere, bool aOnDrag = false );
+
+    /**
+     * Function selectCursor()
+     * Selects an item under the cursor unless there is something already selected or aSelectAlways
+     * is true.
+     * @param aSelectAlways forces to select an item even if there is an item already selected.
+     * @return true if eventually there is an item selected, false otherwise.
+     */
+    bool selectCursor( bool aSelectAlways = false );
+
+    /**
+     * Function selectMultiple()
+     * Handles drawing a selection box that allows to select many items at the same time.
+     *
+     * @return true if the function was cancelled (i.e. CancelEvent was received).
+     */
+    bool selectMultiple();
+
+    /**
+     * Function clearSelection()
+     * Clears the current selection.
+     */
+    void clearSelection();
+
+    /**
+     * Function disambiguationMenu()
+     * Handles the menu that allows to select one of many items in case there is more than one
+     * item at the selected point (@see selectCursor()).
+     *
+     * @param aItems contains list of items that are displayed to the user.
+     */
+    EDA_ITEM* disambiguationMenu( GERBER_COLLECTOR* aItems );
+
+    /**
+     * Function toggleSelection()
+     * Changes selection status of a given item.
+     *
+     * @param aItem is the item to have selection status changed.
+     */
+    void toggleSelection( EDA_ITEM* aItem );
+
+    /**
+     * Function selectable()
+     * Checks conditions for an item to be selected.
+     *
+     * @return True if the item fulfills conditions to be selected.
+     */
+    bool selectable( const EDA_ITEM* aItem ) const;
+
+    /**
+     * Function select()
+     * Takes necessary action mark an item as selected.
+     *
+     * @param aItem is an item to be selected.
+     */
+    void select( EDA_ITEM* aItem );
+
+    /**
+     * Function unselect()
+     * Takes necessary action mark an item as unselected.
+     *
+     * @param aItem is an item to be unselected.
+     */
+    void unselect( EDA_ITEM* aItem );
+
+    /**
+     * Function selectVisually()
+     * Marks item as selected, but does not add it to the ITEMS_PICKED_LIST.
+     * @param aItem is an item to be be marked.
+     */
+    void selectVisually( EDA_ITEM* aItem ) const;
+
+    /**
+     * Function unselectVisually()
+     * Marks item as selected, but does not add it to the ITEMS_PICKED_LIST.
+     * @param aItem is an item to be be marked.
+     */
+    void unselectVisually( EDA_ITEM* aItem ) const;
+
+    /**
+     * Function selectionContains()
+     * Checks if the given point is placed within any of selected items' bounding box.
+     *
+     * @return True if the given point is contained in any of selected items' bouding box.
+     */
+    bool selectionContains( const VECTOR2I& aPoint ) const;
+
+    /**
+     * Function guessSelectionCandidates()
+     * Tries to guess best selection candidates in case multiple items are clicked, by
+     * doing some braindead heuristics.
+     * @param aCollector is the collector that has a list of items to be queried.
+     */
+    void guessSelectionCandidates( GERBER_COLLECTOR& aCollector ) const;
+
+    /// Pointer to the parent frame.
+    GERBVIEW_FRAME* m_frame;
+
+    /// Current state of selection.
+    SELECTION m_selection;
+
+    /// Flag saying if items should be added to the current selection or rather replace it.
+    bool m_additive;
+
+    /// Flag saying if items should be removed from the current selection
+    bool m_subtractive;
+
+    /// Flag saying if multiple selection mode is active.
+    bool m_multiple;
+
+    /// Determines if the selection is preliminary or final.
+    bool m_preliminary;
+
+    /// Menu model displayed by the tool.
+    TOOL_MENU m_menu;
+};
+
+#endif
-- 
2.11.0


Follow ups