Bläddra i källkod

- moved all the UI stuff out of the Gerber Editor class in its own class

Marius Stanciu 5 år sedan
förälder
incheckning
c5217e1781
2 ändrade filer med 2438 tillägg och 2418 borttagningar
  1. 1 0
      CHANGELOG.md
  2. 2437 2418
      appEditors/AppGerberEditor.py

+ 1 - 0
CHANGELOG.md

@@ -23,6 +23,7 @@ CHANGELOG for FlatCAM beta
 - trimmed the application strings
 - updated the Italian translation (by Massimiliano Golfetto)
 - fixed a series of issues in Gerber Editor tools when the user is trying to use the tools by preselecting a aperture without size (aperture macro)
+- moved all the UI stuff out of the Gerber Editor class in its own class
 
 2.11.2020
 

+ 2437 - 2418
appEditors/AppGerberEditor.py

@@ -392,7 +392,7 @@ class FCPad(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
         try:
             self.draw_app.app.jump_signal.disconnect()
@@ -419,7 +419,7 @@ class FCPadArray(FCShapeTool):
             self.complete = True
             self.dont_execute = True
             self.draw_app.in_action = False
-            self.draw_app.array_frame.hide()
+            self.draw_app.ui.array_frame.hide()
             self.draw_app.select_tool('select')
             return
 
@@ -451,7 +451,7 @@ class FCPadArray(FCShapeTool):
         except KeyError:
             pass
 
-        self.draw_app.array_frame.show()
+        self.draw_app.ui.array_frame.show()
 
         self.selected_size = None
         self.pad_axis = 'X'
@@ -512,15 +512,15 @@ class FCPadArray(FCShapeTool):
             self.draw_app.select_tool('select')
             return
 
-        self.pad_axis = self.draw_app.pad_axis_radio.get_value()
-        self.pad_direction = self.draw_app.pad_direction_radio.get_value()
-        self.pad_array = self.draw_app.array_type_combo.get_value()
+        self.pad_axis = self.draw_app.ui.pad_axis_radio.get_value()
+        self.pad_direction = self.draw_app.ui.pad_direction_radio.get_value()
+        self.pad_array = self.draw_app.ui.array_type_combo.get_value()
         try:
-            self.pad_array_size = int(self.draw_app.pad_array_size_entry.get_value())
+            self.pad_array_size = int(self.draw_app.ui.pad_array_size_entry.get_value())
             try:
-                self.pad_pitch = float(self.draw_app.pad_pitch_entry.get_value())
-                self.pad_linear_angle = float(self.draw_app.linear_angle_spinner.get_value())
-                self.pad_angle = float(self.draw_app.pad_angle_entry.get_value())
+                self.pad_pitch = float(self.draw_app.ui.pad_pitch_entry.get_value())
+                self.pad_linear_angle = float(self.draw_app.ui.linear_angle_spinner.get_value())
+                self.pad_angle = float(self.draw_app.ui.pad_angle_entry.get_value())
             except TypeError:
                 self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
                                               _("The value is not Float. Check for comma instead of dot separator."))
@@ -741,12 +741,12 @@ class FCPadArray(FCShapeTool):
         self.complete = True
         self.draw_app.app.inform.emit('[success] %s' % _("Done."))
         self.draw_app.in_action = False
-        self.draw_app.array_frame.hide()
+        self.draw_app.ui.array_frame.hide()
         self.draw_app.app.jump_signal.disconnect()
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
         try:
             self.draw_app.app.jump_signal.disconnect()
@@ -860,7 +860,7 @@ class FCPoligonize(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
 
 
@@ -1172,7 +1172,7 @@ class FCRegion(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
         try:
             self.draw_app.app.jump_signal.disconnect()
@@ -1445,7 +1445,7 @@ class FCTrack(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
         try:
             self.draw_app.app.jump_signal.disconnect()
@@ -1558,7 +1558,7 @@ class FCDisc(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
         try:
             self.draw_app.app.jump_signal.disconnect()
@@ -1613,10 +1613,11 @@ class FCSemiDisc(FCShapeTool):
         if '0' in self.draw_app.storage_dict:
             self.storage_obj = self.draw_app.storage_dict['0']['geometry']
         else:
-            self.draw_app.storage_dict['0'] = {}
-            self.draw_app.storage_dict['0']['type'] = 'C'
-            self.draw_app.storage_dict['0']['size'] = 0.0
-            self.draw_app.storage_dict['0']['geometry'] = []
+            self.draw_app.storage_dict['0'] = {
+                'type': 'C',
+                'size': 0.0,
+                'geometry': []
+            }
             self.storage_obj = self.draw_app.storage_dict['0']['geometry']
 
         self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"]
@@ -1846,7 +1847,7 @@ class FCSemiDisc(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
         try:
             self.draw_app.app.jump_signal.disconnect()
@@ -1872,16 +1873,16 @@ class FCScale(FCShapeTool):
 
     def activate_scale(self):
         self.draw_app.hide_tool('all')
-        self.draw_app.scale_tool_frame.show()
+        self.draw_app.ui.scale_tool_frame.show()
 
         try:
-            self.draw_app.scale_button.clicked.disconnect()
+            self.draw_app.ui.scale_button.clicked.disconnect()
         except (TypeError, AttributeError):
             pass
-        self.draw_app.scale_button.clicked.connect(self.on_scale_click)
+        self.draw_app.ui.scale_button.clicked.connect(self.on_scale_click)
 
     def deactivate_scale(self):
-        self.draw_app.scale_button.clicked.disconnect()
+        self.draw_app.ui.scale_button.clicked.disconnect()
         self.complete = True
         self.draw_app.select_tool("select")
         self.draw_app.hide_tool(self.name)
@@ -1892,7 +1893,7 @@ class FCScale(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
 
 
@@ -1914,16 +1915,16 @@ class FCBuffer(FCShapeTool):
 
     def activate_buffer(self):
         self.draw_app.hide_tool('all')
-        self.draw_app.buffer_tool_frame.show()
+        self.draw_app.ui.buffer_tool_frame.show()
 
         try:
-            self.draw_app.buffer_button.clicked.disconnect()
+            self.draw_app.ui.buffer_button.clicked.disconnect()
         except (TypeError, AttributeError):
             pass
-        self.draw_app.buffer_button.clicked.connect(self.on_buffer_click)
+        self.draw_app.ui.buffer_button.clicked.connect(self.on_buffer_click)
 
     def deactivate_buffer(self):
-        self.draw_app.buffer_button.clicked.disconnect()
+        self.draw_app.ui.buffer_button.clicked.disconnect()
         self.complete = True
         self.draw_app.select_tool("select")
         self.draw_app.hide_tool(self.name)
@@ -1934,7 +1935,7 @@ class FCBuffer(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
 
 
@@ -1955,31 +1956,31 @@ class FCMarkArea(FCShapeTool):
         self.activate_markarea()
 
     def activate_markarea(self):
-        self.draw_app.ma_tool_frame.show()
+        self.draw_app.ui.ma_tool_frame.show()
 
         # clear previous marking
-        self.draw_app.ma_annotation.clear(update=True)
+        self.draw_app.ui.ma_annotation.clear(update=True)
 
         try:
-            self.draw_app.ma_threshold_button.clicked.disconnect()
+            self.draw_app.ui.ma_threshold_button.clicked.disconnect()
         except (TypeError, AttributeError):
             pass
-        self.draw_app.ma_threshold_button.clicked.connect(self.on_markarea_click)
+        self.draw_app.ui.ma_threshold_button.clicked.connect(self.on_markarea_click)
 
         try:
-            self.draw_app.ma_delete_button.clicked.disconnect()
+            self.draw_app.ui.ma_delete_button.clicked.disconnect()
         except TypeError:
             pass
-        self.draw_app.ma_delete_button.clicked.connect(self.on_markarea_delete)
+        self.draw_app.ui.ma_delete_button.clicked.connect(self.on_markarea_delete)
 
         try:
-            self.draw_app.ma_clear_button.clicked.disconnect()
+            self.draw_app.ui.ma_clear_button.clicked.disconnect()
         except TypeError:
             pass
-        self.draw_app.ma_clear_button.clicked.connect(self.on_markarea_clear)
+        self.draw_app.ui.ma_clear_button.clicked.connect(self.on_markarea_clear)
 
     def deactivate_markarea(self):
-        self.draw_app.ma_threshold_button.clicked.disconnect()
+        self.draw_app.ui.ma_threshold_button.clicked.disconnect()
         self.complete = True
         self.draw_app.select_tool("select")
         self.draw_app.hide_tool(self.name)
@@ -1997,7 +1998,7 @@ class FCMarkArea(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
 
 
@@ -2027,10 +2028,10 @@ class FCApertureMove(FCShapeTool):
         self.current_storage = None
         self.geometry = []
 
-        for index in self.draw_app.apertures_table.selectedIndexes():
+        for index in self.draw_app.ui.apertures_table.selectedIndexes():
             row = index.row()
             # on column 1 in tool tables we hold the aperture codes, and we retrieve them as strings
-            aperture_on_row = self.draw_app.apertures_table.item(row, 1).text()
+            aperture_on_row = self.draw_app.ui.apertures_table.item(row, 1).text()
             self.selected_apertures.append(aperture_on_row)
 
         # Switch notebook to Properties page
@@ -2143,7 +2144,7 @@ class FCApertureMove(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
 
         try:
@@ -2242,10 +2243,10 @@ class FCEraser(FCShapeTool):
         self.current_storage = None
         self.geometry = []
 
-        for index in self.draw_app.apertures_table.selectedIndexes():
+        for index in self.draw_app.ui.apertures_table.selectedIndexes():
             row = index.row()
             # on column 1 in tool tables we hold the aperture codes, and we retrieve them as strings
-            aperture_on_row = self.draw_app.apertures_table.item(row, 1).text()
+            aperture_on_row = self.draw_app.ui.apertures_table.item(row, 1).text()
             self.selected_apertures.append(aperture_on_row)
 
         # Switch notebook to Properties page
@@ -2260,7 +2261,7 @@ class FCEraser(FCShapeTool):
 
     def click(self, point):
         if len(self.draw_app.get_selected()) == 0:
-            self.draw_app.apertures_table.clearSelection()
+            self.draw_app.ui.apertures_table.clearSelection()
             sel_aperture = set()
 
             for storage in self.draw_app.storage_dict:
@@ -2277,19 +2278,19 @@ class FCEraser(FCShapeTool):
 
             # select the aperture in the Apertures Table that is associated with the selected shape
             try:
-                self.draw_app.apertures_table.cellPressed.disconnect()
+                self.draw_app.ui.apertures_table.cellPressed.disconnect()
             except Exception as e:
                 log.debug("AppGerberEditor.FCEraser.click_release() --> %s" % str(e))
 
-            self.draw_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+            self.draw_app.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
             for aper in sel_aperture:
-                for row in range(self.draw_app.apertures_table.rowCount()):
-                    if str(aper) == self.draw_app.apertures_table.item(row, 1).text():
-                        self.draw_app.apertures_table.selectRow(row)
+                for row in range(self.draw_app.ui.apertures_table.rowCount()):
+                    if str(aper) == self.draw_app.ui.apertures_table.item(row, 1).text():
+                        self.draw_app.ui.apertures_table.selectRow(row)
                         self.draw_app.last_aperture_selected = aper
-            self.draw_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+            self.draw_app.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
 
-            self.draw_app.apertures_table.cellPressed.connect(self.draw_app.on_row_selected)
+            self.draw_app.ui.apertures_table.cellPressed.connect(self.draw_app.on_row_selected)
 
         if len(self.draw_app.get_selected()) == 0:
             return "Nothing to ersase."
@@ -2334,7 +2335,7 @@ class FCEraser(FCShapeTool):
 
     def clean_up(self):
         self.draw_app.selected = []
-        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         self.draw_app.plot_all()
         try:
             self.draw_app.app.jump_signal.disconnect()
@@ -2372,33 +2373,33 @@ class FCEraser(FCShapeTool):
 
 
 class FCApertureSelect(DrawTool):
-    def __init__(self, grb_editor_app):
-        DrawTool.__init__(self, grb_editor_app)
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
         self.name = 'select'
         self.origin = None
 
-        self.grb_editor_app = grb_editor_app
-        self.storage = self.grb_editor_app.storage_dict
-        # self.selected = self.grb_editor_app.selected
+        self.draw_app = draw_app
+        self.storage = self.draw_app.storage_dict
+        # self.selected = self.draw_app.selected
 
         # here we store all shapes that were selected
         self.sel_storage = []
 
         # since FCApertureSelect tool is activated whenever a tool is exited I place here the reinitialization of the
         # bending modes using in FCRegion and FCTrack
-        self.grb_editor_app.bend_mode = 1
+        self.draw_app.bend_mode = 1
 
         # here store the selected apertures
         self.sel_aperture = []
 
         try:
-            self.grb_editor_app.apertures_table.clearSelection()
+            self.draw_app.ui.apertures_table.clearSelection()
         except Exception as e:
             log.error("FlatCAMGerbEditor.FCApertureSelect.__init__() --> %s" % str(e))
 
-        self.grb_editor_app.hide_tool('all')
-        self.grb_editor_app.hide_tool('select')
-        self.grb_editor_app.array_frame.hide()
+        self.draw_app.hide_tool('all')
+        self.draw_app.hide_tool('select')
+        self.draw_app.ui.array_frame.hide()
 
         try:
             QtGui.QGuiApplication.restoreOverrideCursor()
@@ -2406,16 +2407,16 @@ class FCApertureSelect(DrawTool):
             log.debug("AppGerberEditor.FCApertureSelect --> %s" % str(e))
 
         try:
-            self.grb_editor_app.selection_triggered.disconnect()
+            self.draw_app.selection_triggered.disconnect()
         except (TypeError, AttributeError):
             pass
-        self.grb_editor_app.selection_triggered.connect(self.selection_worker)
+        self.draw_app.selection_triggered.connect(self.selection_worker)
 
         try:
-            self.grb_editor_app.plot_object.disconnect()
+            self.draw_app.plot_object.disconnect()
         except (TypeError, AttributeError):
             pass
-        self.grb_editor_app.plot_object.connect(self.clean_up)
+        self.draw_app.plot_object.connect(self.clean_up)
 
     def set_origin(self, origin):
         self.origin = origin
@@ -2430,13 +2431,13 @@ class FCApertureSelect(DrawTool):
         else:
             mod_key = None
 
-        if mod_key == self.grb_editor_app.app.defaults["global_mselect_key"]:
+        if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
             pass
         else:
-            self.grb_editor_app.selected = []
+            self.draw_app.selected = []
 
     def click_release(self, point):
-        self.grb_editor_app.apertures_table.clearSelection()
+        self.draw_app.ui.apertures_table.clearSelection()
         key_modifier = QtWidgets.QApplication.keyboardModifiers()
 
         if key_modifier == QtCore.Qt.ShiftModifier:
@@ -2446,11 +2447,11 @@ class FCApertureSelect(DrawTool):
         else:
             mod_key = None
 
-        if mod_key != self.grb_editor_app.app.defaults["global_mselect_key"]:
-            self.grb_editor_app.selected.clear()
+        if mod_key != self.draw_app.app.defaults["global_mselect_key"]:
+            self.draw_app.selected.clear()
             self.sel_aperture.clear()
 
-        self.grb_editor_app.selection_triggered.emit(point)
+        self.draw_app.selection_triggered.emit(point)
 
     def selection_worker(self, point):
         def job_thread(editor_obj):
@@ -2471,15 +2472,15 @@ class FCApertureSelect(DrawTool):
                     if brake_flag is True:
                         break
 
-                # #############################################################################################################
+                # ######################################################################################################
                 # select the aperture in the Apertures Table that is associated with the selected shape
-                # #############################################################################################################
+                # ######################################################################################################
                 self.sel_aperture.clear()
-                editor_obj.apertures_table.clearSelection()
+                editor_obj.ui.apertures_table.clearSelection()
 
                 # disconnect signal when clicking in the table
                 try:
-                    editor_obj.apertures_table.cellPressed.disconnect()
+                    editor_obj.ui.apertures_table.cellPressed.disconnect()
                 except Exception as e:
                     log.debug("AppGerberEditor.FCApertureSelect.click_release() --> %s" % str(e))
 
@@ -2495,21 +2496,21 @@ class FCApertureSelect(DrawTool):
 
                 # actual row selection is done here
                 for aper in self.sel_aperture:
-                    for row in range(editor_obj.apertures_table.rowCount()):
-                        if str(aper) == editor_obj.apertures_table.item(row, 1).text():
-                            if not editor_obj.apertures_table.item(row, 0).isSelected():
-                                editor_obj.apertures_table.selectRow(row)
+                    for row in range(editor_obj.ui.apertures_table.rowCount()):
+                        if str(aper) == editor_obj.ui.apertures_table.item(row, 1).text():
+                            if not editor_obj.ui.apertures_table.item(row, 0).isSelected():
+                                editor_obj.ui.apertures_table.selectRow(row)
                                 editor_obj.last_aperture_selected = aper
 
                 # reconnect signal when clicking in the table
-                editor_obj.apertures_table.cellPressed.connect(editor_obj.on_row_selected)
+                editor_obj.ui.apertures_table.cellPressed.connect(editor_obj.on_row_selected)
 
                 editor_obj.plot_object.emit(None)
 
-        self.grb_editor_app.app.worker_task.emit({'fcn': job_thread, 'params': [self.grb_editor_app]})
+        self.draw_app.app.worker_task.emit({'fcn': job_thread, 'params': [self.draw_app]})
 
     def clean_up(self):
-        self.grb_editor_app.plot_all()
+        self.draw_app.plot_all()
 
 
 class FCTransform(FCShapeTool):
@@ -2553,2847 +2554,2865 @@ class AppGerberEditor(QtCore.QObject):
         # Current application units in Upper Case
         self.units = self.app.defaults['units'].upper()
 
-        self.grb_edit_widget = QtWidgets.QWidget()
-
-        layout = QtWidgets.QVBoxLayout()
-        self.grb_edit_widget.setLayout(layout)
+        self.ui = AppGerberEditorUI(self.app)
 
-        # Page Title box (spacing between children)
-        self.title_box = QtWidgets.QHBoxLayout()
-        layout.addLayout(self.title_box)
+        # Toolbar events and properties
+        self.tools_gerber = {}
 
-        # Page Title icon
-        pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
-        self.icon = QtWidgets.QLabel()
-        self.icon.setPixmap(pixmap)
-        self.title_box.addWidget(self.icon, stretch=0)
+        # # ## Data
+        self.active_tool = None
 
-        # Title label
-        self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Gerber Editor'))
-        self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-        self.title_box.addWidget(self.title_label, stretch=1)
+        self.storage_dict = {}
+        self.current_storage = []
 
-        # Object name
-        self.name_box = QtWidgets.QHBoxLayout()
-        layout.addLayout(self.name_box)
-        name_label = QtWidgets.QLabel(_("Name:"))
-        self.name_box.addWidget(name_label)
-        self.name_entry = FCEntry()
-        self.name_box.addWidget(self.name_entry)
+        self.sorted_apcode = []
 
-        # Box for custom widgets
-        # This gets populated in offspring implementations.
-        self.custom_box = QtWidgets.QVBoxLayout()
-        layout.addLayout(self.custom_box)
+        self.new_apertures = {}
+        self.new_aperture_macros = {}
 
-        # #########################
-        # ### Gerber Apertures ####
-        # #########################
-        self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
-        self.apertures_table_label.setToolTip(
-            _("Apertures Table for the Gerber Object.")
-        )
-        self.custom_box.addWidget(self.apertures_table_label)
+        # store here the plot promises, if empty the delayed plot will be activated
+        self.grb_plot_promises = []
 
-        self.apertures_table = FCTable()
-        # delegate = SpinBoxDelegate(units=self.units)
-        # self.apertures_table.setItemDelegateForColumn(1, delegate)
+        # dictionary to store the tool_row and aperture codes in Tool_table
+        # it will be updated everytime self.build_ui() is called
+        self.oldapcode_newapcode = {}
 
-        self.custom_box.addWidget(self.apertures_table)
+        self.tid2apcode = {}
 
-        self.apertures_table.setColumnCount(5)
-        self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
-        self.apertures_table.setSortingEnabled(False)
-        self.apertures_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+        # this will store the value for the last selected tool, for use after clicking on canvas when the selection
+        # is cleared but as a side effect also the selected tool is cleared
+        self.last_aperture_selected = None
+        self.utility = []
 
-        self.apertures_table.horizontalHeaderItem(0).setToolTip(
-            _("Index"))
-        self.apertures_table.horizontalHeaderItem(1).setToolTip(
-            _("Aperture Code"))
-        self.apertures_table.horizontalHeaderItem(2).setToolTip(
-            _("Type of aperture: circular, rectangle, macros etc"))
-        self.apertures_table.horizontalHeaderItem(4).setToolTip(
-            _("Aperture Size:"))
-        self.apertures_table.horizontalHeaderItem(4).setToolTip(
-            _("Aperture Dimensions:\n"
-              " - (width, height) for R, O type.\n"
-              " - (dia, nVertices) for P type"))
+        # this will store the polygons marked by mark are to be perhaps deleted
+        self.geo_to_delete = []
 
-        self.empty_label = QtWidgets.QLabel('')
-        self.custom_box.addWidget(self.empty_label)
+        # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
+        self.launched_from_shortcuts = False
 
-        # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Apertures widgets
-        # this way I can hide/show the frame
-        self.apertures_frame = QtWidgets.QFrame()
-        self.apertures_frame.setContentsMargins(0, 0, 0, 0)
-        self.custom_box.addWidget(self.apertures_frame)
-        self.apertures_box = QtWidgets.QVBoxLayout()
-        self.apertures_box.setContentsMargins(0, 0, 0, 0)
-        self.apertures_frame.setLayout(self.apertures_box)
+        # this var will store the state of the toolbar before starting the editor
+        self.toolbar_old_state = False
 
-        # # ## Add/Delete an new Aperture ## ##
+        # #############################################################################################################
+        # ######################### Init appGUI #######################################################################
+        # #############################################################################################################
+        self.ui.apdim_lbl.hide()
+        self.ui.apdim_entry.hide()
 
-        grid1 = QtWidgets.QGridLayout()
-        self.apertures_box.addLayout(grid1)
-        grid1.setColumnStretch(0, 0)
-        grid1.setColumnStretch(1, 1)
+        self.gerber_obj = None
+        self.gerber_obj_options = {}
 
-        apcode_lbl = QtWidgets.QLabel('%s:' % _('Aperture Code'))
-        apcode_lbl.setToolTip(_("Code for the new aperture"))
-        grid1.addWidget(apcode_lbl, 1, 0)
+        # VisPy Visuals
+        if self.app.is_legacy is False:
+            self.shapes = self.canvas.new_shape_collection(layers=1)
+            self.tool_shape = self.canvas.new_shape_collection(layers=1)
+            self.ma_annotation = self.canvas.new_text_group()
+        else:
+            from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor')
+            self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor')
+            self.ma_annotation = ShapeCollectionLegacy(
+                obj=self,
+                app=self.app,
+                name='ma_anno_grb_editor',
+                annotation_job=True)
 
-        self.apcode_entry = FCSpinner()
-        self.apcode_entry.set_range(0, 999)
-        self.apcode_entry.setWrapping(True)
+        # Event signals disconnect id holders
+        self.mp = None
+        self.mm = None
+        self.mr = None
 
-        grid1.addWidget(self.apcode_entry, 1, 1)
+        # Remove from scene
+        self.shapes.enabled = False
+        self.tool_shape.enabled = False
 
-        apsize_lbl = QtWidgets.QLabel('%s' % _('Aperture Size:'))
-        apsize_lbl.setToolTip(
-            _("Size for the new aperture.\n"
-              "If aperture type is 'R' or 'O' then\n"
-              "this value is automatically\n"
-              "calculated as:\n"
-              "sqrt(width**2 + height**2)")
-        )
-        grid1.addWidget(apsize_lbl, 2, 0)
+        # List of selected geometric elements.
+        self.selected = []
 
-        self.apsize_entry = FCDoubleSpinner()
-        self.apsize_entry.set_precision(self.decimals)
-        self.apsize_entry.set_range(0.0, 9999)
+        self.key = None  # Currently pressed key
+        self.modifiers = None
+        self.x = None  # Current mouse cursor pos
+        self.y = None
+        # Current snapped mouse pos
+        self.snap_x = None
+        self.snap_y = None
+        self.pos = None
 
-        grid1.addWidget(self.apsize_entry, 2, 1)
+        # used in FCRegion and FCTrack. Will store the bending mode
+        self.bend_mode = 1
 
-        aptype_lbl = QtWidgets.QLabel('%s:' % _('Aperture Type'))
-        aptype_lbl.setToolTip(
-            _("Select the type of new aperture. Can be:\n"
-              "C = circular\n"
-              "R = rectangular\n"
-              "O = oblong")
-        )
-        grid1.addWidget(aptype_lbl, 3, 0)
+        # signal that there is an action active like polygon or path
+        self.in_action = False
+        # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
+        self.launched_from_shortcuts = False
 
-        self.aptype_cb = FCComboBox()
-        self.aptype_cb.addItems(['C', 'R', 'O'])
-        grid1.addWidget(self.aptype_cb, 3, 1)
+        def_tol_val = float(self.app.defaults["global_tolerance"])
+        self.tolerance = def_tol_val if self.units == 'MM'else def_tol_val / 20
 
-        self.apdim_lbl = QtWidgets.QLabel('%s:' % _('Aperture Dim'))
-        self.apdim_lbl.setToolTip(
-            _("Dimensions for the new aperture.\n"
-              "Active only for rectangular apertures (type R).\n"
-              "The format is (width, height)")
-        )
-        grid1.addWidget(self.apdim_lbl, 4, 0)
+        # options of this widget (AppGerberEditor class is a widget)
+        self.options = {
+            "global_gridx":     0.1,
+            "global_gridy":     0.1,
+            "snap_max":         0.05,
+            "grid_snap":        True,
+            "corner_snap":      False,
+            "grid_gap_link":    True
+        }
+        # fill it with the application options (application preferences)
+        self.options.update(self.app.options)
 
-        self.apdim_entry = EvalEntry2()
-        grid1.addWidget(self.apdim_entry, 4, 1)
+        for option in self.options:
+            if option in self.app.options:
+                self.options[option] = self.app.options[option]
 
-        apadd_del_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Add/Delete Aperture'))
-        apadd_del_lbl.setToolTip(
-            _("Add/Delete an aperture in the aperture table")
-        )
-        self.apertures_box.addWidget(apadd_del_lbl)
+        # flag to show if the object was modified
+        self.is_modified = False
+        self.edited_obj_name = ""
+        self.tool_row = 0
 
-        hlay_ad = QtWidgets.QHBoxLayout()
-        self.apertures_box.addLayout(hlay_ad)
+        # Multiprocessing pool
+        self.pool = self.app.pool
 
-        self.addaperture_btn = QtWidgets.QPushButton(_('Add'))
-        self.addaperture_btn.setToolTip(
-           _("Add a new aperture to the aperture list.")
-        )
+        # Multiprocessing results
+        self.results = []
 
-        self.delaperture_btn = QtWidgets.QPushButton(_('Delete'))
-        self.delaperture_btn.setToolTip(
-           _("Delete a aperture in the aperture list")
-        )
-        hlay_ad.addWidget(self.addaperture_btn)
-        hlay_ad.addWidget(self.delaperture_btn)
+        # A QTimer
+        self.plot_thread = None
 
-        # ###################
-        # ### BUFFER TOOL ###
-        # ###################
-        self.buffer_tool_frame = QtWidgets.QFrame()
-        self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
-        self.custom_box.addWidget(self.buffer_tool_frame)
-        self.buffer_tools_box = QtWidgets.QVBoxLayout()
-        self.buffer_tools_box.setContentsMargins(0, 0, 0, 0)
-        self.buffer_tool_frame.setLayout(self.buffer_tools_box)
-        self.buffer_tool_frame.hide()
+        # a QThread for the edit process
+        self.thread = QtCore.QThread()
 
-        # Title
-        buf_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Buffer Aperture'))
-        buf_title_lbl.setToolTip(
-            _("Buffer a aperture in the aperture list")
-        )
-        self.buffer_tools_box.addWidget(buf_title_lbl)
+        # def entry2option(option, entry):
+        #     self.options[option] = float(entry.text())
 
-        # Form Layout
-        buf_form_layout = QtWidgets.QFormLayout()
-        self.buffer_tools_box.addLayout(buf_form_layout)
+        self.transform_tool = TransformEditorTool(self.app, self)
 
-        # Buffer distance
-        self.buffer_distance_entry = FCDoubleSpinner()
-        self.buffer_distance_entry.set_precision(self.decimals)
-        self.buffer_distance_entry.set_range(-10000.0000, 10000.0000)
-
-        buf_form_layout.addRow('%s:' % _("Buffer distance"), self.buffer_distance_entry)
-        self.buffer_corner_lbl = QtWidgets.QLabel('%s:' % _("Buffer corner"))
-        self.buffer_corner_lbl.setToolTip(
-            _("There are 3 types of corners:\n"
-              " - 'Round': the corner is rounded.\n"
-              " - 'Square': the corner is met in a sharp angle.\n"
-              " - 'Beveled': the corner is a line that directly connects the features meeting in the corner")
-        )
-        self.buffer_corner_cb = FCComboBox()
-        self.buffer_corner_cb.addItem(_("Round"))
-        self.buffer_corner_cb.addItem(_("Square"))
-        self.buffer_corner_cb.addItem(_("Beveled"))
-        buf_form_layout.addRow(self.buffer_corner_lbl, self.buffer_corner_cb)
+        # #############################################################################################################
+        # ######################### Gerber Editor Signals #############################################################
+        # #############################################################################################################
+        self.app.pool_recreated.connect(self.pool_recreated)
+        self.mp_finished.connect(self.on_multiprocessing_finished)
 
-        # Buttons
-        hlay_buf = QtWidgets.QHBoxLayout()
-        self.buffer_tools_box.addLayout(hlay_buf)
+        # connect the toolbar signals
+        self.connect_grb_toolbar_signals()
 
-        self.buffer_button = QtWidgets.QPushButton(_("Buffer"))
-        hlay_buf.addWidget(self.buffer_button)
+        self.app.ui.grb_add_pad_menuitem.triggered.connect(self.on_pad_add)
+        self.app.ui.grb_add_pad_array_menuitem.triggered.connect(self.on_pad_add_array)
 
-        # ##################
-        # ### SCALE TOOL ###
-        # ##################
-        self.scale_tool_frame = QtWidgets.QFrame()
-        self.scale_tool_frame.setContentsMargins(0, 0, 0, 0)
-        self.custom_box.addWidget(self.scale_tool_frame)
-        self.scale_tools_box = QtWidgets.QVBoxLayout()
-        self.scale_tools_box.setContentsMargins(0, 0, 0, 0)
-        self.scale_tool_frame.setLayout(self.scale_tools_box)
-        self.scale_tool_frame.hide()
+        self.app.ui.grb_add_track_menuitem.triggered.connect(self.on_track_add)
+        self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add)
 
-        # Title
-        scale_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Scale Aperture'))
-        scale_title_lbl.setToolTip(
-            _("Scale a aperture in the aperture list")
-        )
-        self.scale_tools_box.addWidget(scale_title_lbl)
+        self.app.ui.grb_convert_poly_menuitem.triggered.connect(self.on_poligonize)
+        self.app.ui.grb_add_semidisc_menuitem.triggered.connect(self.on_add_semidisc)
+        self.app.ui.grb_add_disc_menuitem.triggered.connect(self.on_disc_add)
+        self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer)
+        self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale)
+        self.app.ui.grb_add_eraser_menuitem.triggered.connect(self.on_eraser)
+        self.app.ui.grb_add_markarea_menuitem.triggered.connect(self.on_markarea)
 
-        # Form Layout
-        scale_form_layout = QtWidgets.QFormLayout()
-        self.scale_tools_box.addLayout(scale_form_layout)
+        self.app.ui.grb_transform_menuitem.triggered.connect(self.transform_tool.run)
 
-        self.scale_factor_lbl = QtWidgets.QLabel('%s:' % _("Scale factor"))
-        self.scale_factor_lbl.setToolTip(
-            _("The factor by which to scale the selected aperture.\n"
-              "Values can be between 0.0000 and 999.9999")
-        )
-        self.scale_factor_entry = FCDoubleSpinner()
-        self.scale_factor_entry.set_precision(self.decimals)
-        self.scale_factor_entry.set_range(0.0000, 10000.0000)
+        self.app.ui.grb_copy_menuitem.triggered.connect(self.on_copy_button)
+        self.app.ui.grb_delete_menuitem.triggered.connect(self.on_delete_btn)
 
-        scale_form_layout.addRow(self.scale_factor_lbl, self.scale_factor_entry)
+        self.app.ui.grb_move_menuitem.triggered.connect(self.on_move_button)
 
-        # Buttons
-        hlay_scale = QtWidgets.QHBoxLayout()
-        self.scale_tools_box.addLayout(hlay_scale)
+        self.ui.buffer_button.clicked.connect(self.on_buffer)
+        self.ui.scale_button.clicked.connect(self.on_scale)
 
-        self.scale_button = QtWidgets.QPushButton(_("Scale"))
-        hlay_scale.addWidget(self.scale_button)
+        self.app.ui.aperture_delete_btn.triggered.connect(self.on_delete_btn)
+        self.ui.name_entry.returnPressed.connect(self.on_name_activate)
 
-        # ######################
-        # ### Mark Area TOOL ###
-        # ######################
-        self.ma_tool_frame = QtWidgets.QFrame()
-        self.ma_tool_frame.setContentsMargins(0, 0, 0, 0)
-        self.custom_box.addWidget(self.ma_tool_frame)
-        self.ma_tools_box = QtWidgets.QVBoxLayout()
-        self.ma_tools_box.setContentsMargins(0, 0, 0, 0)
-        self.ma_tool_frame.setLayout(self.ma_tools_box)
-        self.ma_tool_frame.hide()
+        self.ui.aptype_cb.currentIndexChanged[str].connect(self.on_aptype_changed)
 
-        # Title
-        ma_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Mark polygons'))
-        ma_title_lbl.setToolTip(
-            _("Mark the polygon areas.")
-        )
-        self.ma_tools_box.addWidget(ma_title_lbl)
+        self.ui.addaperture_btn.clicked.connect(self.on_aperture_add)
+        self.ui.apsize_entry.returnPressed.connect(self.on_aperture_add)
+        self.ui.apdim_entry.returnPressed.connect(self.on_aperture_add)
 
-        # Form Layout
-        ma_form_layout = QtWidgets.QFormLayout()
-        self.ma_tools_box.addLayout(ma_form_layout)
+        self.ui.delaperture_btn.clicked.connect(self.on_aperture_delete)
+        self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
 
-        self.ma_upper_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area UPPER threshold"))
-        self.ma_upper_threshold_lbl.setToolTip(
-            _("The threshold value, all areas less than this are marked.\n"
-              "Can have a value between 0.0000 and 10000.0000")
-        )
-        self.ma_upper_threshold_entry = FCDoubleSpinner()
-        self.ma_upper_threshold_entry.set_precision(self.decimals)
-        self.ma_upper_threshold_entry.set_range(0, 10000)
+        self.ui.array_type_combo.currentIndexChanged.connect(self.on_array_type_combo)
+        self.ui.pad_axis_radio.activated_custom.connect(self.on_linear_angle_radio)
 
-        self.ma_lower_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area LOWER threshold"))
-        self.ma_lower_threshold_lbl.setToolTip(
-            _("The threshold value, all areas more than this are marked.\n"
-              "Can have a value between 0.0000 and 10000.0000")
-        )
-        self.ma_lower_threshold_entry = FCDoubleSpinner()
-        self.ma_lower_threshold_entry.set_precision(self.decimals)
-        self.ma_lower_threshold_entry.set_range(0, 10000)
+        self.ui.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
 
-        ma_form_layout.addRow(self.ma_lower_threshold_lbl, self.ma_lower_threshold_entry)
-        ma_form_layout.addRow(self.ma_upper_threshold_lbl, self.ma_upper_threshold_entry)
+        self.conversion_factor = 1
 
-        # Buttons
-        hlay_ma = QtWidgets.QHBoxLayout()
-        self.ma_tools_box.addLayout(hlay_ma)
+        self.apertures_row = 0
 
-        self.ma_threshold_button = QtWidgets.QPushButton(_("Mark"))
-        self.ma_threshold_button.setToolTip(
-            _("Mark the polygons that fit within limits.")
-        )
-        hlay_ma.addWidget(self.ma_threshold_button)
+        self.complete = True
 
-        self.ma_delete_button = QtWidgets.QPushButton(_("Delete"))
-        self.ma_delete_button.setToolTip(
-            _("Delete all the marked polygons.")
-        )
-        hlay_ma.addWidget(self.ma_delete_button)
+        self.set_ui()
+        log.debug("Initialization of the Gerber Editor is finished ...")
 
-        self.ma_clear_button = QtWidgets.QPushButton(_("Clear"))
-        self.ma_clear_button.setToolTip(
-            _("Clear all the markings.")
-        )
-        hlay_ma.addWidget(self.ma_clear_button)
+    def make_callback(self, the_tool):
+        def f():
+            self.on_tool_select(the_tool)
 
-        # ######################
-        # ### Add Pad Array ####
-        # ######################
-        # add a frame and inside add a vertical box layout. Inside this vbox layout I add
-        # all the add Pad array  widgets
-        # this way I can hide/show the frame
-        self.array_frame = QtWidgets.QFrame()
-        self.array_frame.setContentsMargins(0, 0, 0, 0)
-        self.custom_box.addWidget(self.array_frame)
-        self.array_box = QtWidgets.QVBoxLayout()
-        self.array_box.setContentsMargins(0, 0, 0, 0)
-        self.array_frame.setLayout(self.array_box)
+        return f
 
-        self.emptyarray_label = QtWidgets.QLabel('')
-        self.array_box.addWidget(self.emptyarray_label)
+    def connect_grb_toolbar_signals(self):
+        self.tools_gerber.update({
+            "select": {"button": self.app.ui.grb_select_btn,            "constructor": FCApertureSelect},
+            "pad": {"button": self.app.ui.grb_add_pad_btn,              "constructor": FCPad},
+            "array": {"button": self.app.ui.add_pad_ar_btn,             "constructor": FCPadArray},
+            "track": {"button": self.app.ui.grb_add_track_btn,          "constructor": FCTrack},
+            "region": {"button": self.app.ui.grb_add_region_btn,        "constructor": FCRegion},
+            "poligonize": {"button": self.app.ui.grb_convert_poly_btn,  "constructor": FCPoligonize},
+            "semidisc": {"button": self.app.ui.grb_add_semidisc_btn,    "constructor": FCSemiDisc},
+            "disc": {"button": self.app.ui.grb_add_disc_btn,            "constructor": FCDisc},
+            "buffer": {"button": self.app.ui.aperture_buffer_btn,       "constructor": FCBuffer},
+            "scale": {"button": self.app.ui.aperture_scale_btn,         "constructor": FCScale},
+            "markarea": {"button": self.app.ui.aperture_markarea_btn,   "constructor": FCMarkArea},
+            "eraser": {"button": self.app.ui.aperture_eraser_btn,       "constructor": FCEraser},
+            "copy": {"button": self.app.ui.aperture_copy_btn,           "constructor": FCApertureCopy},
+            "transform": {"button": self.app.ui.grb_transform_btn,      "constructor": FCTransform},
+            "move": {"button": self.app.ui.aperture_move_btn,           "constructor": FCApertureMove},
+        })
 
-        self.padarray_label = QtWidgets.QLabel('<b>%s</b>' % _("Add Pad Array"))
-        self.padarray_label.setToolTip(
-            _("Add an array of pads (linear or circular array)")
-        )
-        self.array_box.addWidget(self.padarray_label)
+        for tool in self.tools_gerber:
+            self.tools_gerber[tool]["button"].triggered.connect(self.make_callback(tool))  # Events
+            self.tools_gerber[tool]["button"].setCheckable(True)
 
-        self.array_type_combo = FCComboBox()
-        self.array_type_combo.setToolTip(
-           _("Select the type of pads array to create.\n"
-             "It can be Linear X(Y) or Circular")
-        )
-        self.array_type_combo.addItem(_("Linear"))
-        self.array_type_combo.addItem(_("Circular"))
+    def pool_recreated(self, pool):
+        self.shapes.pool = pool
+        self.tool_shape.pool = pool
+        self.pool = pool
 
-        self.array_box.addWidget(self.array_type_combo)
+    def set_ui(self):
+        # updated units
+        self.units = self.app.defaults['units'].upper()
+        self.decimals = self.app.decimals
 
-        self.array_form = QtWidgets.QFormLayout()
-        self.array_box.addLayout(self.array_form)
+        self.oldapcode_newapcode.clear()
+        self.tid2apcode.clear()
 
-        self.pad_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of pads'))
-        self.pad_array_size_label.setToolTip(
-            _("Specify how many pads to be in the array.")
-        )
-        self.pad_array_size_label.setMinimumWidth(100)
+        # update the oldapcode_newapcode dict to make sure we have an updated state of the tool_table
+        for key in self.storage_dict:
+            self.oldapcode_newapcode[key] = key
 
-        self.pad_array_size_entry = FCSpinner()
-        self.pad_array_size_entry.set_range(1, 9999)
+        sort_temp = []
+        for aperture in self.oldapcode_newapcode:
+            sort_temp.append(int(aperture))
+        self.sorted_apcode = sorted(sort_temp)
 
-        self.array_form.addRow(self.pad_array_size_label, self.pad_array_size_entry)
+        # populate self.intial_table_rows dict with the tool number as keys and aperture codes as values
+        for i in range(len(self.sorted_apcode)):
+            tt_aperture = self.sorted_apcode[i]
+            self.tid2apcode[i + 1] = tt_aperture
 
-        self.array_linear_frame = QtWidgets.QFrame()
-        self.array_linear_frame.setContentsMargins(0, 0, 0, 0)
-        self.array_box.addWidget(self.array_linear_frame)
-        self.linear_box = QtWidgets.QVBoxLayout()
-        self.linear_box.setContentsMargins(0, 0, 0, 0)
-        self.array_linear_frame.setLayout(self.linear_box)
+        # #############################################################################################################
+        # Init appGUI
+        # #############################################################################################################
+        self.ui.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
+        self.ui.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
+        self.ui.ma_upper_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_high"])
+        self.ui.ma_lower_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_low"])
 
-        self.linear_form = QtWidgets.QFormLayout()
-        self.linear_box.addLayout(self.linear_form)
+        self.ui.apsize_entry.set_value(self.app.defaults["gerber_editor_newsize"])
+        self.ui.aptype_cb.set_value(self.app.defaults["gerber_editor_newtype"])
+        self.ui.apdim_entry.set_value(self.app.defaults["gerber_editor_newdim"])
 
-        self.pad_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
-        self.pad_axis_label.setToolTip(
-            _("Direction on which the linear array is oriented:\n"
-              "- 'X' - horizontal axis \n"
-              "- 'Y' - vertical axis or \n"
-              "- 'Angle' - a custom angle for the array inclination")
-        )
-        self.pad_axis_label.setMinimumWidth(100)
+        self.ui.pad_array_size_entry.set_value(int(self.app.defaults["gerber_editor_array_size"]))
+        # linear array
+        self.ui.pad_axis_radio.set_value(self.app.defaults["gerber_editor_lin_axis"])
+        self.ui.pad_pitch_entry.set_value(float(self.app.defaults["gerber_editor_lin_pitch"]))
+        self.ui.linear_angle_spinner.set_value(self.app.defaults["gerber_editor_lin_angle"])
+        # circular array
+        self.ui.pad_direction_radio.set_value(self.app.defaults["gerber_editor_circ_dir"])
+        self.ui.pad_angle_entry.set_value(float(self.app.defaults["gerber_editor_circ_angle"]))
 
-        self.pad_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
-                                        {'label': _('Y'), 'value': 'Y'},
-                                        {'label': _('Angle'), 'value': 'A'}])
-        self.pad_axis_radio.set_value('X')
-        self.linear_form.addRow(self.pad_axis_label, self.pad_axis_radio)
+    def build_ui(self, first_run=None):
 
-        self.pad_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
-        self.pad_pitch_label.setToolTip(
-            _("Pitch = Distance between elements of the array.")
-        )
-        self.pad_pitch_label.setMinimumWidth(100)
+        try:
+            # if connected, disconnect the signal from the slot on item_changed as it creates issues
+            self.ui.apertures_table.itemChanged.disconnect()
+        except (TypeError, AttributeError):
+            pass
 
-        self.pad_pitch_entry = FCDoubleSpinner()
-        self.pad_pitch_entry.set_precision(self.decimals)
-        self.pad_pitch_entry.set_range(0.0000, 10000.0000)
-        self.pad_pitch_entry.setSingleStep(0.1)
-
-        self.linear_form.addRow(self.pad_pitch_label, self.pad_pitch_entry)
+        try:
+            self.ui.apertures_table.cellPressed.disconnect()
+        except (TypeError, AttributeError):
+            pass
 
-        self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
-        self.linear_angle_label.setToolTip(
-           _("Angle at which the linear array is placed.\n"
-             "The precision is of max 2 decimals.\n"
-             "Min value is: -360.00 degrees.\n"
-             "Max value is: 360.00 degrees.")
-        )
-        self.linear_angle_label.setMinimumWidth(100)
+        # updated units
+        self.units = self.app.defaults['units'].upper()
 
-        self.linear_angle_spinner = FCDoubleSpinner()
-        self.linear_angle_spinner.set_precision(self.decimals)
-        self.linear_angle_spinner.setRange(-360.00, 360.00)
-        self.linear_form.addRow(self.linear_angle_label, self.linear_angle_spinner)
+        # make a new name for the new Excellon object (the one with edited content)
+        self.edited_obj_name = self.gerber_obj.options['name']
+        self.ui.name_entry.set_value(self.edited_obj_name)
 
-        self.array_circular_frame = QtWidgets.QFrame()
-        self.array_circular_frame.setContentsMargins(0, 0, 0, 0)
-        self.array_box.addWidget(self.array_circular_frame)
-        self.circular_box = QtWidgets.QVBoxLayout()
-        self.circular_box.setContentsMargins(0, 0, 0, 0)
-        self.array_circular_frame.setLayout(self.circular_box)
+        self.apertures_row = 0
+        # aper_no = self.apertures_row + 1
 
-        self.pad_direction_label = QtWidgets.QLabel('%s:' % _('Direction'))
-        self.pad_direction_label.setToolTip(
-           _("Direction for circular array.\n"
-             "Can be CW = clockwise or CCW = counter clockwise.")
-        )
-        self.pad_direction_label.setMinimumWidth(100)
+        sort = []
+        for k, v in list(self.storage_dict.items()):
+            sort.append(int(k))
 
-        self.circular_form = QtWidgets.QFormLayout()
-        self.circular_box.addLayout(self.circular_form)
+        sorted_apertures = sorted(sort)
 
-        self.pad_direction_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
-                                             {'label': _('CCW'), 'value': 'CCW'}])
-        self.pad_direction_radio.set_value('CW')
-        self.circular_form.addRow(self.pad_direction_label, self.pad_direction_radio)
+        # sort = []
+        # for k, v in list(self.gerber_obj.aperture_macros.items()):
+        #     sort.append(k)
+        # sorted_macros = sorted(sort)
 
-        self.pad_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
-        self.pad_angle_label.setToolTip(
-            _("Angle at which each element in circular array is placed.")
-        )
-        self.pad_angle_label.setMinimumWidth(100)
+        # n = len(sorted_apertures) + len(sorted_macros)
+        n = len(sorted_apertures)
+        self.ui.apertures_table.setRowCount(n)
 
-        self.pad_angle_entry = FCDoubleSpinner()
-        self.pad_angle_entry.set_precision(self.decimals)
-        self.pad_angle_entry.set_range(-360.00, 360.00)
-        self.pad_angle_entry.setSingleStep(0.1)
+        for ap_code in sorted_apertures:
+            ap_code = str(ap_code)
 
-        self.circular_form.addRow(self.pad_angle_label, self.pad_angle_entry)
+            ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
+            ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            self.ui.apertures_table.setItem(self.apertures_row, 0, ap_code_item)  # Tool name/id
 
-        self.array_circular_frame.hide()
+            ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
+            ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
-        self.linear_angle_spinner.hide()
-        self.linear_angle_label.hide()
+            ap_type_item = QtWidgets.QTableWidgetItem(str(self.storage_dict[ap_code]['type']))
+            ap_type_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
-        self.array_frame.hide()
-        self.custom_box.addStretch()
+            if str(self.storage_dict[ap_code]['type']) == 'R' or str(self.storage_dict[ap_code]['type']) == 'O':
+                ap_dim_item = QtWidgets.QTableWidgetItem(
+                    '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['width'],
+                                    self.decimals, self.storage_dict[ap_code]['height']
+                                    )
+                )
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
+            elif str(self.storage_dict[ap_code]['type']) == 'P':
+                ap_dim_item = QtWidgets.QTableWidgetItem(
+                    '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['diam'],
+                                    self.decimals, self.storage_dict[ap_code]['nVertices'])
+                )
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
+            else:
+                ap_dim_item = QtWidgets.QTableWidgetItem('')
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
-        layout.addStretch()
+            try:
+                if self.storage_dict[ap_code]['size'] is not None:
+                    ap_size_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals,
+                                                                        float(self.storage_dict[ap_code]['size'])))
+                else:
+                    ap_size_item = QtWidgets.QTableWidgetItem('')
+            except KeyError:
+                ap_size_item = QtWidgets.QTableWidgetItem('')
 
-        # Editor
-        self.exit_editor_button = QtWidgets.QPushButton(_('Exit Editor'))
-        self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
-        self.exit_editor_button.setToolTip(
-            _("Exit from Editor.")
-        )
-        self.exit_editor_button.setStyleSheet("""
-                                      QPushButton
-                                      {
-                                          font-weight: bold;
-                                      }
-                                      """)
-        layout.addWidget(self.exit_editor_button)
+            if str(self.storage_dict[ap_code]['type']) == 'C':
+                ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
+            else:
+                ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
-        self.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
+            self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
+            self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
+            self.ui.apertures_table.setItem(self.apertures_row, 3, ap_size_item)  # Aperture Size
+            self.ui.apertures_table.setItem(self.apertures_row, 4, ap_dim_item)  # Aperture Dimensions
 
-        # Toolbar events and properties
-        self.tools_gerber = {}
+            self.apertures_row += 1
+            if first_run is True:
+                # set now the last aperture selected
+                self.last_aperture_selected = ap_code
 
-        # # ## Data
-        self.active_tool = None
+        # for ap_code in sorted_macros:
+        #     ap_code = str(ap_code)
+        #
+        #     ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
+        #     ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+        #     self.ui.apertures_table.setItem(self.apertures_row, 0, ap_code_item)  # Tool name/id
+        #
+        #     ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
+        #
+        #     ap_type_item = QtWidgets.QTableWidgetItem('AM')
+        #     ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
+        #
+        #     self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
+        #     self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
+        #
+        #     self.apertures_row += 1
+        #     if first_run is True:
+        #         # set now the last aperture selected
+        #         self.last_aperture_selected = ap_code
 
-        self.storage_dict = {}
-        self.current_storage = []
+        self.ui.apertures_table.selectColumn(0)
+        self.ui.apertures_table.resizeColumnsToContents()
+        self.ui.apertures_table.resizeRowsToContents()
 
-        self.sorted_apcode = []
+        vertical_header = self.ui.apertures_table.verticalHeader()
+        # vertical_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
+        vertical_header.hide()
+        self.ui.apertures_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
 
-        self.new_apertures = {}
-        self.new_aperture_macros = {}
+        horizontal_header = self.ui.apertures_table.horizontalHeader()
+        horizontal_header.setMinimumSectionSize(10)
+        horizontal_header.setDefaultSectionSize(70)
+        horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
+        horizontal_header.resizeSection(0, 27)
+        horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch)
 
-        # store here the plot promises, if empty the delayed plot will be activated
-        self.grb_plot_promises = []
+        self.ui.apertures_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        self.ui.apertures_table.setSortingEnabled(False)
+        self.ui.apertures_table.setMinimumHeight(self.ui.apertures_table.getHeight())
+        self.ui.apertures_table.setMaximumHeight(self.ui.apertures_table.getHeight())
 
-        # dictionary to store the tool_row and aperture codes in Tool_table
-        # it will be updated everytime self.build_ui() is called
-        self.oldapcode_newapcode = {}
+        # make sure no rows are selected so the user have to click the correct row, meaning selecting the correct tool
+        self.ui.apertures_table.clearSelection()
 
-        self.tid2apcode = {}
+        # Remove anything else in the GUI Properties Tab
+        self.app.ui.properties_scroll_area.takeWidget()
+        # Put ourselves in the GUI Properties Tab
+        self.app.ui.properties_scroll_area.setWidget(self.ui.grb_edit_widget)
+        # Switch notebook to Properties page
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
 
-        # this will store the value for the last selected tool, for use after clicking on canvas when the selection
-        # is cleared but as a side effect also the selected tool is cleared
-        self.last_aperture_selected = None
-        self.utility = []
+        # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
+        self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
+        self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
 
-        # this will store the polygons marked by mark are to be perhaps deleted
-        self.geo_to_delete = []
+        # for convenience set the next aperture code in the apcode field
+        try:
+            self.ui.apcode_entry.set_value(max(self.tid2apcode.values()) + 1)
+        except ValueError:
+            # this means that the edited object has no apertures so we start with 10 (Gerber specifications)
+            self.ui.apcode_entry.set_value(self.app.defaults["gerber_editor_newcode"])
 
-        # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
-        self.launched_from_shortcuts = False
+    def on_aperture_add(self, apcode=None):
+        self.is_modified = True
+        if apcode:
+            ap_code = apcode
+        else:
+            try:
+                ap_code = str(self.ui.apcode_entry.get_value())
+            except ValueError:
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Aperture code value is missing or wrong format. Add it and retry."))
+                return
+            if ap_code == '':
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Aperture code value is missing or wrong format. Add it and retry."))
+                return
 
-        # this var will store the state of the toolbar before starting the editor
-        self.toolbar_old_state = False
+        if ap_code == '0':
+            if ap_code not in self.tid2apcode:
+                self.storage_dict[ap_code] = {}
+                self.storage_dict[ap_code]['type'] = 'REG'
+                size_val = 0
+                self.ui.apsize_entry.set_value(size_val)
+                self.storage_dict[ap_code]['size'] = size_val
 
-        # Init appGUI
-        self.apdim_lbl.hide()
-        self.apdim_entry.hide()
-        self.gerber_obj = None
-        self.gerber_obj_options = {}
+                self.storage_dict[ap_code]['geometry'] = []
 
-        # VisPy Visuals
-        if self.app.is_legacy is False:
-            self.shapes = self.canvas.new_shape_collection(layers=1)
-            self.tool_shape = self.canvas.new_shape_collection(layers=1)
-            self.ma_annotation = self.canvas.new_text_group()
+                # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and
+                # gets updated on values each time a aperture code is edited or added
+                self.oldapcode_newapcode[ap_code] = ap_code
         else:
-            from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy
-            self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor')
-            self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor')
-            self.ma_annotation = ShapeCollectionLegacy(
-                obj=self,
-                app=self.app,
-                name='ma_anno_grb_editor',
-                annotation_job=True)
+            if ap_code not in self.oldapcode_newapcode:
+                self.storage_dict[ap_code] = {}
 
-        self.app.pool_recreated.connect(self.pool_recreated)
+                type_val = self.aptype_cb.currentText()
+                self.storage_dict[ap_code]['type'] = type_val
 
-        # Event signals disconnect id holders
-        self.mp = None
-        self.mm = None
-        self.mr = None
+                if type_val == 'R' or type_val == 'O':
+                    try:
+                        dims = self.ui.apdim_entry.get_value()
+                        self.storage_dict[ap_code]['width'] = dims[0]
+                        self.storage_dict[ap_code]['height'] = dims[1]
 
-        # Remove from scene
-        self.shapes.enabled = False
-        self.tool_shape.enabled = False
+                        size_val = np.sqrt((dims[0] ** 2) + (dims[1] ** 2))
+                        self.ui.apsize_entry.set_value(size_val)
 
-        # List of selected geometric elements.
-        self.selected = []
+                    except Exception as e:
+                        log.error("AppGerberEditor.on_aperture_add() --> the R or O aperture dims has to be in a "
+                                  "tuple format (x,y)\nError: %s" % str(e))
+                        self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                             _("Aperture dimensions value is missing or wrong format. "
+                                               "Add it in format (width, height) and retry."))
+                        return
+                else:
+                    try:
+                        size_val = float(self.ui.apsize_entry.get_value())
+                    except ValueError:
+                        # try to convert comma to decimal point. if it's still not working error message and return
+                        try:
+                            size_val = float(self.ui.apsize_entry.get_value().replace(',', '.'))
+                            self.ui.apsize_entry.set_value(size_val)
+                        except ValueError:
+                            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                                 _("Aperture size value is missing or wrong format. Add it and retry."))
+                            return
+                self.storage_dict[ap_code]['size'] = size_val
 
-        self.key = None  # Currently pressed key
-        self.modifiers = None
-        self.x = None  # Current mouse cursor pos
-        self.y = None
-        # Current snapped mouse pos
-        self.snap_x = None
-        self.snap_y = None
-        self.pos = None
-
-        # used in FCRegion and FCTrack. Will store the bending mode
-        self.bend_mode = 1
-
-        # signal that there is an action active like polygon or path
-        self.in_action = False
-        # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
-        self.launched_from_shortcuts = False
+                self.storage_dict[ap_code]['geometry'] = []
 
-        def_tol_val = float(self.app.defaults["global_tolerance"])
-        self.tolerance = def_tol_val if self.units == 'MM'else def_tol_val / 20
+                # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and gets updated on
+                # values  each time a aperture code is edited or added
+                self.oldapcode_newapcode[ap_code] = ap_code
+            else:
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Aperture already in the aperture table."))
+                return
 
-        self.options = {
-            "global_gridx": 0.1,
-            "global_gridy": 0.1,
-            "snap_max": 0.05,
-            "grid_snap": True,
-            "corner_snap": False,
-            "grid_gap_link": True
-        }
-        self.options.update(self.app.options)
+        # since we add a new tool, we update also the initial state of the tool_table through it's dictionary
+        # we add a new entry in the tid2apcode dict
+        self.tid2apcode[len(self.oldapcode_newapcode)] = int(ap_code)
 
-        for option in self.options:
-            if option in self.app.options:
-                self.options[option] = self.app.options[option]
+        self.app.inform.emit('[success] %s: %s' % (_("Added new aperture with code"), str(ap_code)))
 
-        # flag to show if the object was modified
-        self.is_modified = False
-        self.edited_obj_name = ""
-        self.tool_row = 0
+        self.build_ui()
 
-        # Multiprocessing pool
-        self.pool = self.app.pool
+        self.last_aperture_selected = ap_code
 
-        # Multiprocessing results
-        self.results = []
+        # make a quick sort through the tid2apcode dict so we find which row to select
+        row_to_be_selected = None
+        for key in sorted(self.tid2apcode):
+            if self.tid2apcode[key] == int(ap_code):
+                row_to_be_selected = int(key) - 1
+                break
+        self.ui.apertures_table.selectRow(row_to_be_selected)
 
-        # A QTimer
-        self.plot_thread = None
+    def on_aperture_delete(self, ap_code=None):
+        """
+        Called for aperture deletion.
 
-        # a QThread for the edit process
-        self.thread = QtCore.QThread()
+        :param ap_code:     An Aperture code; String
+        :return:
+        """
+        self.is_modified = True
 
-        # def entry2option(option, entry):
-        #     self.options[option] = float(entry.text())
+        try:
+            if ap_code:
+                try:
+                    deleted_apcode_list = [dd for dd in ap_code]
+                except TypeError:
+                    deleted_apcode_list = [ap_code]
+            else:
+                # deleted_tool_dia = float(self.ui.apertures_table.item(self.ui.apertures_table.currentRow(), 1).text())
+                if len(self.ui.apertures_table.selectionModel().selectedRows()) == 0:
+                    self.app.inform.emit('[WARNING_NOTCL] %s' % _(" Select an aperture in Aperture Table"))
+                    return
 
-        self.transform_tool = TransformEditorTool(self.app, self)
+                deleted_apcode_list = []
+                for index in self.ui.apertures_table.selectionModel().selectedRows():
+                    row = index.row()
+                    deleted_apcode_list.append(self.ui.apertures_table.item(row, 1).text())
+        except Exception as exc:
+            self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Select an aperture in Aperture Table -->", str(exc))))
+            return
 
-        # #############################################################################################################
-        # ######################### Gerber Editor Signals #############################################################
-        # #############################################################################################################
+        if deleted_apcode_list:
+            for deleted_aperture in deleted_apcode_list:
+                # delete the storage used for that tool
+                self.storage_dict.pop(deleted_aperture, None)
 
-        # connect the toolbar signals
-        self.connect_grb_toolbar_signals()
+                for deleted_tool in list(self.tid2apcode.keys()):
+                    if self.tid2apcode[deleted_tool] == deleted_aperture:
+                        # delete the tool
+                        self.tid2apcode.pop(deleted_tool, None)
 
-        self.buffer_button.clicked.connect(self.on_buffer)
-        self.scale_button.clicked.connect(self.on_scale)
+                self.oldapcode_newapcode.pop(deleted_aperture, None)
+                self.app.inform.emit('[success] %s: %s' % (_("Deleted aperture with code"), str(deleted_aperture)))
 
-        self.app.ui.aperture_delete_btn.triggered.connect(self.on_delete_btn)
-        self.name_entry.returnPressed.connect(self.on_name_activate)
+        self.plot_all()
+        self.build_ui()
 
-        self.aptype_cb.currentIndexChanged[str].connect(self.on_aptype_changed)
+        # if last aperture selected was in the apertures deleted than make sure to select a
+        # 'new' last aperture selected because there are tools who depend on it.
+        # if there is no aperture left, then add a default one :)
+        if self.last_aperture_selected in deleted_apcode_list:
+            if self.ui.apertures_table.rowCount() == 0:
+                self.on_aperture_add('10')
+                self.last_aperture_selected = '10'
+            else:
+                self.last_aperture_selected = self.ui.apertures_table.item(0, 1).text()
 
-        self.addaperture_btn.clicked.connect(self.on_aperture_add)
-        self.apsize_entry.returnPressed.connect(self.on_aperture_add)
-        self.apdim_entry.returnPressed.connect(self.on_aperture_add)
+    def on_tool_edit(self):
+        if self.ui.apertures_table.currentItem() is None:
+            return
 
-        self.delaperture_btn.clicked.connect(self.on_aperture_delete)
-        self.apertures_table.cellPressed.connect(self.on_row_selected)
+        # if connected, disconnect the signal from the slot on item_changed as it creates issues
+        self.ui.apertures_table.itemChanged.disconnect()
+        # self.ui.apertures_table.cellPressed.disconnect()
 
-        self.app.ui.grb_add_pad_menuitem.triggered.connect(self.on_pad_add)
-        self.app.ui.grb_add_pad_array_menuitem.triggered.connect(self.on_pad_add_array)
+        self.is_modified = True
+        val_edited = None
 
-        self.app.ui.grb_add_track_menuitem.triggered.connect(self.on_track_add)
-        self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add)
+        row_of_item_changed = self.ui.apertures_table.currentRow()
+        col_of_item_changed = self.ui.apertures_table.currentColumn()
 
-        self.app.ui.grb_convert_poly_menuitem.triggered.connect(self.on_poligonize)
-        self.app.ui.grb_add_semidisc_menuitem.triggered.connect(self.on_add_semidisc)
-        self.app.ui.grb_add_disc_menuitem.triggered.connect(self.on_disc_add)
-        self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer)
-        self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale)
-        self.app.ui.grb_add_eraser_menuitem.triggered.connect(self.on_eraser)
-        self.app.ui.grb_add_markarea_menuitem.triggered.connect(self.on_markarea)
+        # rows start with 0, tools start with 1 so we adjust the value by 1
+        key_in_tid2apcode = row_of_item_changed + 1
+        ap_code_old = str(self.tid2apcode[key_in_tid2apcode])
 
-        self.app.ui.grb_transform_menuitem.triggered.connect(self.transform_tool.run)
+        ap_code_new = self.ui.apertures_table.item(row_of_item_changed, 1).text()
 
-        self.app.ui.grb_copy_menuitem.triggered.connect(self.on_copy_button)
-        self.app.ui.grb_delete_menuitem.triggered.connect(self.on_delete_btn)
+        if col_of_item_changed == 1:
+            # we edited the Aperture Code column (int)
+            try:
+                val_edited = int(self.ui.apertures_table.currentItem().text())
+            except ValueError as e:
+                log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
+                # self.ui.apertures_table.setCurrentItem(None)
+                # we reactivate the signals after the after the tool editing
+                self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
+                return
+        elif col_of_item_changed == 3:
+            # we edited the Size column (float)
+            try:
+                val_edited = float(self.ui.apertures_table.currentItem().text())
+            except ValueError as e:
+                log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
+                # self.ui.apertures_table.setCurrentItem(None)
+                # we reactivate the signals after the after the tool editing
+                self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
+                return
+        elif col_of_item_changed == 4:
+            # we edit the Dimensions column (tuple)
+            try:
+                val_edited = [
+                    float(x.strip()) for x in self.ui.apertures_table.currentItem().text().split(",") if x != ''
+                ]
+            except ValueError as e:
+                log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
+                # we reactivate the signals after the after the tool editing
+                self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
+                return
 
-        self.app.ui.grb_move_menuitem.triggered.connect(self.on_move_button)
+            if len(val_edited) != 2:
+                self.app.inform.emit("[WARNING_NOTCL] %s" % _("Dimensions need two float values separated by comma."))
+                old_dims_txt = '%s, %s' % (str(self.storage_dict[ap_code_new]['width']),
+                                           str(self.storage_dict[ap_code_new]['height']))
 
-        self.array_type_combo.currentIndexChanged.connect(self.on_array_type_combo)
-        self.pad_axis_radio.activated_custom.connect(self.on_linear_angle_radio)
+                self.ui.apertures_table.currentItem().setText(old_dims_txt)
+                # we reactivate the signals after the after the tool editing
+                self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
+                return
+            else:
+                self.app.inform.emit("[success] %s" % _("Dimensions edited."))
 
-        self.mp_finished.connect(self.on_multiprocessing_finished)
+        # In case we edited the Aperture Code therefore the val_edited holds a new Aperture Code
+        # TODO Edit of the Aperture Code is not active yet
+        if col_of_item_changed == 1:
+            # aperture code is not used so we create a new Aperture with the desired Aperture Code
+            if val_edited not in self.oldapcode_newapcode.values():
+                # update the dict that holds as keys old Aperture Codes and as values the new Aperture Codes
+                self.oldapcode_newapcode[ap_code_old] = val_edited
+                # update the dict that holds tool_no as key and tool_dia as value
+                self.tid2apcode[key_in_tid2apcode] = val_edited
 
-        self.conversion_factor = 1
+                old_aperture_val = self.storage_dict.pop(ap_code_old)
+                self.storage_dict[val_edited] = old_aperture_val
 
-        self.apertures_row = 0
+            else:
+                # aperture code is already in use so we move the pads from the prior tool to the new tool
+                # but only if they are of the same type
 
-        self.complete = True
+                if self.storage_dict[ap_code_old]['type'] == self.storage_dict[ap_code_new]['type']:
+                    # TODO I have to work here; if type == 'R' or 'O' have t otake care of all attributes ...
+                    factor = val_edited / float(ap_code_old)
+                    geometry = []
+                    for geo_el in self.storage_dict[ap_code_old]:
+                        geometric_data = geo_el.geo
+                        new_geo_el = {}
+                        if 'solid' in geometric_data:
+                            new_geo_el['solid'] = deepcopy(affinity.scale(geometric_data['solid'],
+                                                                          xfact=factor, yfact=factor))
+                        if 'follow' in geometric_data:
+                            new_geo_el['follow'] = deepcopy(affinity.scale(geometric_data['follow'],
+                                                                           xfact=factor, yfact=factor))
+                        if 'clear' in geometric_data:
+                            new_geo_el['clear'] = deepcopy(affinity.scale(geometric_data['clear'],
+                                                                          xfact=factor, yfact=factor))
+                        geometry.append(new_geo_el)
 
-        self.set_ui()
-        log.debug("Initialization of the Gerber Editor is finished ...")
+                    self.add_gerber_shape(geometry, self.storage_dict[val_edited])
 
-    def make_callback(self, the_tool):
-        def f():
-            self.on_tool_select(the_tool)
+                    self.on_aperture_delete(apcode=ap_code_old)
 
-        return f
+        # In case we edited the Size of the Aperture therefore the val_edited holds the new Aperture Size
+        # It will happen only for the Aperture Type == 'C' - I make sure of that in the self.build_ui()
+        elif col_of_item_changed == 3:
+            old_size = float(self.storage_dict[ap_code_old]['size'])
+            new_size = float(val_edited)
+            adjust_size = (new_size - old_size) / 2
+            geometry = []
+            for geo_el in self.storage_dict[ap_code_old]['geometry']:
+                g_data = geo_el.geo
+                new_geo_el = {}
+                if 'solid' in g_data:
+                    if 'follow' in g_data:
+                        if isinstance(g_data['follow'], Point):
+                            new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size))
+                        else:
+                            new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size, join_style=2))
+                if 'follow' in g_data:
+                    new_geo_el['follow'] = deepcopy(g_data['follow'])
+                if 'clear' in g_data:
+                    new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(adjust_size, join_style=2))
+                geometry.append(DrawToolShape(new_geo_el))
 
-    def connect_grb_toolbar_signals(self):
-        self.tools_gerber.update({
-            "select": {"button": self.app.ui.grb_select_btn,            "constructor": FCApertureSelect},
-            "pad": {"button": self.app.ui.grb_add_pad_btn,              "constructor": FCPad},
-            "array": {"button": self.app.ui.add_pad_ar_btn,             "constructor": FCPadArray},
-            "track": {"button": self.app.ui.grb_add_track_btn,          "constructor": FCTrack},
-            "region": {"button": self.app.ui.grb_add_region_btn,        "constructor": FCRegion},
-            "poligonize": {"button": self.app.ui.grb_convert_poly_btn,  "constructor": FCPoligonize},
-            "semidisc": {"button": self.app.ui.grb_add_semidisc_btn,    "constructor": FCSemiDisc},
-            "disc": {"button": self.app.ui.grb_add_disc_btn,            "constructor": FCDisc},
-            "buffer": {"button": self.app.ui.aperture_buffer_btn,       "constructor": FCBuffer},
-            "scale": {"button": self.app.ui.aperture_scale_btn,         "constructor": FCScale},
-            "markarea": {"button": self.app.ui.aperture_markarea_btn,   "constructor": FCMarkArea},
-            "eraser": {"button": self.app.ui.aperture_eraser_btn,       "constructor": FCEraser},
-            "copy": {"button": self.app.ui.aperture_copy_btn,           "constructor": FCApertureCopy},
-            "transform": {"button": self.app.ui.grb_transform_btn,      "constructor": FCTransform},
-            "move": {"button": self.app.ui.aperture_move_btn,           "constructor": FCApertureMove},
-        })
+            self.storage_dict[ap_code_old]['geometry'].clear()
+            self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
+            # self.storage_dict[ap_code_old]['geometry'] = geometry
 
-        for tool in self.tools_gerber:
-            self.tools_gerber[tool]["button"].triggered.connect(self.make_callback(tool))  # Events
-            self.tools_gerber[tool]["button"].setCheckable(True)
+        # In case we edited the Dims of the Aperture therefore the val_edited holds a list with the dimensions
+        # in the format [width, height]
+        # It will happen only for the Aperture Type in ['R', 'O'] - I make sure of that in the self.build_ui()
+        # and below
+        elif col_of_item_changed == 4:
+            if str(self.storage_dict[ap_code_old]['type']) == 'R' or str(self.storage_dict[ap_code_old]['type']) == 'O':
+                # use the biggest from them
+                buff_val_lines = max(val_edited)
+                new_width = val_edited[0]
+                new_height = val_edited[1]
 
-    def pool_recreated(self, pool):
-        self.shapes.pool = pool
-        self.tool_shape.pool = pool
-        self.pool = pool
+                geometry = []
+                for geo_el in self.storage_dict[ap_code_old]['geometry']:
+                    g_data = geo_el.geo
+                    new_geo_el = {}
+                    if 'solid' in g_data:
+                        if 'follow' in g_data:
+                            if isinstance(g_data['follow'], Point):
+                                x = g_data['follow'].x
+                                y = g_data['follow'].y
+                                minx = x - (new_width / 2)
+                                miny = y - (new_height / 2)
+                                maxx = x + (new_width / 2)
+                                maxy = y + (new_height / 2)
+                                geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
+                                new_geo_el['solid'] = deepcopy(geo)
+                            else:
+                                new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(buff_val_lines))
+                    if 'follow' in g_data:
+                        new_geo_el['follow'] = deepcopy(g_data['follow'])
+                    if 'clear' in g_data:
+                        if 'follow' in g_data:
+                            if isinstance(g_data['follow'], Point):
+                                x = g_data['follow'].x
+                                y = g_data['follow'].y
+                                minx = x - (new_width / 2)
+                                miny = y - (new_height / 2)
+                                maxx = x + (new_width / 2)
+                                maxy = y + (new_height / 2)
+                                geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
+                                new_geo_el['clear'] = deepcopy(geo)
+                            else:
+                                new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(buff_val_lines, join_style=2))
+                    geometry.append(DrawToolShape(new_geo_el))
 
-    def set_ui(self):
-        # updated units
-        self.units = self.app.defaults['units'].upper()
-        self.decimals = self.app.decimals
+                self.storage_dict[ap_code_old]['geometry'].clear()
+                self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
 
-        self.oldapcode_newapcode.clear()
-        self.tid2apcode.clear()
+        self.plot_all()
 
-        # update the oldapcode_newapcode dict to make sure we have an updated state of the tool_table
-        for key in self.storage_dict:
-            self.oldapcode_newapcode[key] = key
+        # we reactivate the signals after the after the tool editing
+        self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
+        # self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
 
-        sort_temp = []
-        for aperture in self.oldapcode_newapcode:
-            sort_temp.append(int(aperture))
-        self.sorted_apcode = sorted(sort_temp)
+    def on_name_activate(self):
+        self.edited_obj_name = self.ui.name_entry.get_value()
 
-        # populate self.intial_table_rows dict with the tool number as keys and aperture codes as values
-        for i in range(len(self.sorted_apcode)):
-            tt_aperture = self.sorted_apcode[i]
-            self.tid2apcode[i + 1] = tt_aperture
+    def on_aptype_changed(self, current_text):
+        # 'O' is letter O not zero.
+        if current_text == 'R' or current_text == 'O':
+            self.ui.apdim_lbl.show()
+            self.ui.apdim_entry.show()
+            self.ui.apsize_entry.setDisabled(True)
+        else:
+            self.ui.apdim_lbl.hide()
+            self.ui.apdim_entry.hide()
+            self.ui.apsize_entry.setDisabled(False)
 
-        # Init appGUI
+    def activate_grb_editor(self):
+        # adjust the status of the menu entries related to the editor
+        self.app.ui.menueditedit.setDisabled(True)
+        self.app.ui.menueditok.setDisabled(False)
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(False)
+        self.app.ui.popmenu_save.setVisible(True)
 
-        self.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
-        self.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
-        self.ma_upper_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_high"])
-        self.ma_lower_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_low"])
+        self.connect_canvas_event_handlers()
 
-        self.apsize_entry.set_value(self.app.defaults["gerber_editor_newsize"])
-        self.aptype_cb.set_value(self.app.defaults["gerber_editor_newtype"])
-        self.apdim_entry.set_value(self.app.defaults["gerber_editor_newdim"])
+        # init working objects
+        self.storage_dict = {}
+        self.current_storage = []
+        self.sorted_apcode = []
+        self.new_apertures = {}
+        self.new_aperture_macros = {}
+        self.grb_plot_promises = []
+        self.oldapcode_newapcode = {}
+        self.tid2apcode = {}
 
-        self.pad_array_size_entry.set_value(int(self.app.defaults["gerber_editor_array_size"]))
-        # linear array
-        self.pad_axis_radio.set_value(self.app.defaults["gerber_editor_lin_axis"])
-        self.pad_pitch_entry.set_value(float(self.app.defaults["gerber_editor_lin_pitch"]))
-        self.linear_angle_spinner.set_value(self.app.defaults["gerber_editor_lin_angle"])
-        # circular array
-        self.pad_direction_radio.set_value(self.app.defaults["gerber_editor_circ_dir"])
-        self.pad_angle_entry.set_value(float(self.app.defaults["gerber_editor_circ_angle"]))
+        self.shapes.enabled = True
+        self.tool_shape.enabled = True
 
-    def build_ui(self, first_run=None):
+        self.app.ui.corner_snap_btn.setVisible(True)
+        self.app.ui.snap_magnet.setVisible(True)
 
-        try:
-            # if connected, disconnect the signal from the slot on item_changed as it creates issues
-            self.apertures_table.itemChanged.disconnect()
-        except (TypeError, AttributeError):
-            pass
+        self.app.ui.grb_editor_menu.setDisabled(False)
+        self.app.ui.grb_editor_menu.menuAction().setVisible(True)
 
-        try:
-            self.apertures_table.cellPressed.disconnect()
-        except (TypeError, AttributeError):
-            pass
+        self.app.ui.update_obj_btn.setEnabled(True)
+        self.app.ui.grb_editor_cmenu.setEnabled(True)
 
-        # updated units
-        self.units = self.app.defaults['units'].upper()
+        self.app.ui.grb_edit_toolbar.setDisabled(False)
+        self.app.ui.grb_edit_toolbar.setVisible(True)
+        # self.app.ui.grid_toolbar.setDisabled(False)
 
-        # make a new name for the new Excellon object (the one with edited content)
-        self.edited_obj_name = self.gerber_obj.options['name']
-        self.name_entry.set_value(self.edited_obj_name)
+        # start with GRID toolbar activated
+        if self.app.ui.grid_snap_btn.isChecked() is False:
+            self.app.ui.grid_snap_btn.trigger()
 
-        self.apertures_row = 0
-        # aper_no = self.apertures_row + 1
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(False)
+        self.app.ui.popmenu_save.setVisible(True)
 
-        sort = []
-        for k, v in list(self.storage_dict.items()):
-            sort.append(int(k))
+        self.app.ui.popmenu_disable.setVisible(False)
+        self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
+        self.app.ui.popmenu_properties.setVisible(False)
+        self.app.ui.grb_editor_cmenu.menuAction().setVisible(True)
 
-        sorted_apertures = sorted(sort)
+    def deactivate_grb_editor(self):
+        try:
+            QtGui.QGuiApplication.restoreOverrideCursor()
+        except Exception as e:
+            log.debug("AppGerberEditor.deactivate_grb_editor() --> %s" % str(e))
 
-        # sort = []
-        # for k, v in list(self.gerber_obj.aperture_macros.items()):
-        #     sort.append(k)
-        # sorted_macros = sorted(sort)
+        self.clear()
 
-        # n = len(sorted_apertures) + len(sorted_macros)
-        n = len(sorted_apertures)
-        self.apertures_table.setRowCount(n)
+        # adjust the status of the menu entries related to the editor
+        self.app.ui.menueditedit.setDisabled(False)
+        self.app.ui.menueditok.setDisabled(True)
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(True)
+        self.app.ui.popmenu_save.setVisible(False)
 
-        for ap_code in sorted_apertures:
-            ap_code = str(ap_code)
+        self.disconnect_canvas_event_handlers()
+        self.app.ui.grb_edit_toolbar.setDisabled(True)
 
-            ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
-            ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
-            self.apertures_table.setItem(self.apertures_row, 0, ap_code_item)  # Tool name/id
+        self.app.ui.corner_snap_btn.setVisible(False)
+        self.app.ui.snap_magnet.setVisible(False)
 
-            ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
-            ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+        # set the Editor Toolbar visibility to what was before entering in the Editor
+        self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
+            else self.app.ui.grb_edit_toolbar.setVisible(True)
 
-            ap_type_item = QtWidgets.QTableWidgetItem(str(self.storage_dict[ap_code]['type']))
-            ap_type_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+        # Disable visuals
+        self.shapes.enabled = False
+        self.tool_shape.enabled = False
+        # self.app.app_cursor.enabled = False
 
-            if str(self.storage_dict[ap_code]['type']) == 'R' or str(self.storage_dict[ap_code]['type']) == 'O':
-                ap_dim_item = QtWidgets.QTableWidgetItem(
-                    '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['width'],
-                                    self.decimals, self.storage_dict[ap_code]['height']
-                                    )
-                )
-                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
-            elif str(self.storage_dict[ap_code]['type']) == 'P':
-                ap_dim_item = QtWidgets.QTableWidgetItem(
-                    '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['diam'],
-                                    self.decimals, self.storage_dict[ap_code]['nVertices'])
-                )
-                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
-            else:
-                ap_dim_item = QtWidgets.QTableWidgetItem('')
-                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+        self.app.ui.grb_editor_menu.setDisabled(True)
+        self.app.ui.grb_editor_menu.menuAction().setVisible(False)
 
-            try:
-                if self.storage_dict[ap_code]['size'] is not None:
-                    ap_size_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals,
-                                                                        float(self.storage_dict[ap_code]['size'])))
-                else:
-                    ap_size_item = QtWidgets.QTableWidgetItem('')
-            except KeyError:
-                ap_size_item = QtWidgets.QTableWidgetItem('')
+        self.app.ui.update_obj_btn.setEnabled(False)
 
-            if str(self.storage_dict[ap_code]['type']) == 'C':
-                ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
-            else:
-                ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(True)
+        self.app.ui.popmenu_save.setVisible(False)
 
-            self.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
-            self.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
-            self.apertures_table.setItem(self.apertures_row, 3, ap_size_item)  # Aperture Size
-            self.apertures_table.setItem(self.apertures_row, 4, ap_dim_item)  # Aperture Dimensions
+        self.app.ui.popmenu_disable.setVisible(True)
+        self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
+        self.app.ui.popmenu_properties.setVisible(True)
+        self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
+        self.app.ui.e_editor_cmenu.menuAction().setVisible(False)
+        self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
 
-            self.apertures_row += 1
-            if first_run is True:
-                # set now the last aperture selected
-                self.last_aperture_selected = ap_code
+        # Show original geometry
+        if self.gerber_obj:
+            self.gerber_obj.visible = True
 
-        # for ap_code in sorted_macros:
-        #     ap_code = str(ap_code)
-        #
-        #     ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
-        #     ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
-        #     self.apertures_table.setItem(self.apertures_row, 0, ap_code_item)  # Tool name/id
-        #
-        #     ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
-        #
-        #     ap_type_item = QtWidgets.QTableWidgetItem('AM')
-        #     ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
-        #
-        #     self.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
-        #     self.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
-        #
-        #     self.apertures_row += 1
-        #     if first_run is True:
-        #         # set now the last aperture selected
-        #         self.last_aperture_selected = ap_code
+    def connect_canvas_event_handlers(self):
+        # Canvas events
 
-        self.apertures_table.selectColumn(0)
-        self.apertures_table.resizeColumnsToContents()
-        self.apertures_table.resizeRowsToContents()
+        # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
+        # but those from AppGeoEditor
 
-        vertical_header = self.apertures_table.verticalHeader()
-        # vertical_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
-        vertical_header.hide()
-        self.apertures_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        # first connect to new, then disconnect the old handlers
+        # don't ask why but if there is nothing connected I've seen issues
+        self.mp = self.canvas.graph_event_connect('mouse_press', self.on_canvas_click)
+        self.mm = self.canvas.graph_event_connect('mouse_move', self.on_canvas_move)
+        self.mr = self.canvas.graph_event_connect('mouse_release', self.on_grb_click_release)
 
-        horizontal_header = self.apertures_table.horizontalHeader()
-        horizontal_header.setMinimumSectionSize(10)
-        horizontal_header.setDefaultSectionSize(70)
-        horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
-        horizontal_header.resizeSection(0, 27)
-        horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
-        horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
-        horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
-        horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch)
+        if self.app.is_legacy is False:
+            self.canvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.canvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            self.canvas.graph_event_disconnect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
+        else:
+            self.canvas.graph_event_disconnect(self.app.mp)
+            self.canvas.graph_event_disconnect(self.app.mm)
+            self.canvas.graph_event_disconnect(self.app.mr)
+            self.canvas.graph_event_disconnect(self.app.mdc)
 
-        self.apertures_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.apertures_table.setSortingEnabled(False)
-        self.apertures_table.setMinimumHeight(self.apertures_table.getHeight())
-        self.apertures_table.setMaximumHeight(self.apertures_table.getHeight())
+        self.app.collection.view.clicked.disconnect()
 
-        # make sure no rows are selected so the user have to click the correct row, meaning selecting the correct tool
-        self.apertures_table.clearSelection()
+        self.app.ui.popmenu_copy.triggered.disconnect()
+        self.app.ui.popmenu_delete.triggered.disconnect()
+        self.app.ui.popmenu_move.triggered.disconnect()
 
-        # Remove anything else in the GUI Properties Tab
-        self.app.ui.properties_scroll_area.takeWidget()
-        # Put ourselves in the GUI Properties Tab
-        self.app.ui.properties_scroll_area.setWidget(self.grb_edit_widget)
-        # Switch notebook to Properties page
-        self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
+        self.app.ui.popmenu_copy.triggered.connect(self.on_copy_button)
+        self.app.ui.popmenu_delete.triggered.connect(self.on_delete_btn)
+        self.app.ui.popmenu_move.triggered.connect(self.on_move_button)
 
-        # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
-        self.apertures_table.itemChanged.connect(self.on_tool_edit)
-        self.apertures_table.cellPressed.connect(self.on_row_selected)
+        # Gerber Editor
+        self.app.ui.grb_draw_pad.triggered.connect(self.on_pad_add)
+        self.app.ui.grb_draw_pad_array.triggered.connect(self.on_pad_add_array)
+        self.app.ui.grb_draw_track.triggered.connect(self.on_track_add)
+        self.app.ui.grb_draw_region.triggered.connect(self.on_region_add)
 
-        # for convenience set the next aperture code in the apcode field
-        try:
-            self.apcode_entry.set_value(max(self.tid2apcode.values()) + 1)
-        except ValueError:
-            # this means that the edited object has no apertures so we start with 10 (Gerber specifications)
-            self.apcode_entry.set_value(self.app.defaults["gerber_editor_newcode"])
+        self.app.ui.grb_draw_poligonize.triggered.connect(self.on_poligonize)
+        self.app.ui.grb_draw_semidisc.triggered.connect(self.on_add_semidisc)
+        self.app.ui.grb_draw_disc.triggered.connect(self.on_disc_add)
+        self.app.ui.grb_draw_buffer.triggered.connect(lambda: self.select_tool("buffer"))
+        self.app.ui.grb_draw_scale.triggered.connect(lambda: self.select_tool("scale"))
+        self.app.ui.grb_draw_markarea.triggered.connect(lambda: self.select_tool("markarea"))
+        self.app.ui.grb_draw_eraser.triggered.connect(self.on_eraser)
+        self.app.ui.grb_draw_transformations.triggered.connect(self.on_transform)
 
-    def on_aperture_add(self, apcode=None):
-        self.is_modified = True
-        if apcode:
-            ap_code = apcode
+    def disconnect_canvas_event_handlers(self):
+
+        # we restore the key and mouse control to FlatCAMApp method
+        # first connect to new, then disconnect the old handlers
+        # don't ask why but if there is nothing connected I've seen issues
+        self.app.mp = self.canvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.mm = self.canvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.mr = self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.mdc = self.canvas.graph_event_connect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
+        self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
+
+        if self.app.is_legacy is False:
+            self.canvas.graph_event_disconnect('mouse_press', self.on_canvas_click)
+            self.canvas.graph_event_disconnect('mouse_move', self.on_canvas_move)
+            self.canvas.graph_event_disconnect('mouse_release', self.on_grb_click_release)
         else:
-            try:
-                ap_code = str(self.apcode_entry.get_value())
-            except ValueError:
-                self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                     _("Aperture code value is missing or wrong format. Add it and retry."))
-                return
-            if ap_code == '':
-                self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                     _("Aperture code value is missing or wrong format. Add it and retry."))
-                return
+            self.canvas.graph_event_disconnect(self.mp)
+            self.canvas.graph_event_disconnect(self.mm)
+            self.canvas.graph_event_disconnect(self.mr)
 
-        if ap_code == '0':
-            if ap_code not in self.tid2apcode:
-                self.storage_dict[ap_code] = {}
-                self.storage_dict[ap_code]['type'] = 'REG'
-                size_val = 0
-                self.apsize_entry.set_value(size_val)
-                self.storage_dict[ap_code]['size'] = size_val
+        try:
+            self.app.ui.popmenu_copy.triggered.disconnect(self.on_copy_button)
+        except (TypeError, AttributeError):
+            pass
 
-                self.storage_dict[ap_code]['geometry'] = []
+        try:
+            self.app.ui.popmenu_delete.triggered.disconnect(self.on_delete_btn)
+        except (TypeError, AttributeError):
+            pass
 
-                # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and
-                # gets updated on values each time a aperture code is edited or added
-                self.oldapcode_newapcode[ap_code] = ap_code
-        else:
-            if ap_code not in self.oldapcode_newapcode:
-                self.storage_dict[ap_code] = {}
+        try:
+            self.app.ui.popmenu_move.triggered.disconnect(self.on_move_button)
+        except (TypeError, AttributeError):
+            pass
 
-                type_val = self.aptype_cb.currentText()
-                self.storage_dict[ap_code]['type'] = type_val
+        self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_command)
+        self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
+        self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
 
-                if type_val == 'R' or type_val == 'O':
-                    try:
-                        dims = self.apdim_entry.get_value()
-                        self.storage_dict[ap_code]['width'] = dims[0]
-                        self.storage_dict[ap_code]['height'] = dims[1]
+        # Gerber Editor
 
-                        size_val = np.sqrt((dims[0] ** 2) + (dims[1] ** 2))
-                        self.apsize_entry.set_value(size_val)
+        try:
+            self.app.ui.grb_draw_pad.triggered.disconnect(self.on_pad_add)
+        except (TypeError, AttributeError):
+            pass
 
-                    except Exception as e:
-                        log.error("AppGerberEditor.on_aperture_add() --> the R or O aperture dims has to be in a "
-                                  "tuple format (x,y)\nError: %s" % str(e))
-                        self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                             _("Aperture dimensions value is missing or wrong format. "
-                                               "Add it in format (width, height) and retry."))
-                        return
-                else:
-                    try:
-                        size_val = float(self.apsize_entry.get_value())
-                    except ValueError:
-                        # try to convert comma to decimal point. if it's still not working error message and return
-                        try:
-                            size_val = float(self.apsize_entry.get_value().replace(',', '.'))
-                            self.apsize_entry.set_value(size_val)
-                        except ValueError:
-                            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                                 _("Aperture size value is missing or wrong format. Add it and retry."))
-                            return
-                self.storage_dict[ap_code]['size'] = size_val
+        try:
+            self.app.ui.grb_draw_pad_array.triggered.disconnect(self.on_pad_add_array)
+        except (TypeError, AttributeError):
+            pass
 
-                self.storage_dict[ap_code]['geometry'] = []
+        try:
+            self.app.ui.grb_draw_track.triggered.disconnect(self.on_track_add)
+        except (TypeError, AttributeError):
+            pass
 
-                # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and gets updated on
-                # values  each time a aperture code is edited or added
-                self.oldapcode_newapcode[ap_code] = ap_code
-            else:
-                self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                     _("Aperture already in the aperture table."))
-                return
+        try:
+            self.app.ui.grb_draw_region.triggered.disconnect(self.on_region_add)
+        except (TypeError, AttributeError):
+            pass
 
-        # since we add a new tool, we update also the initial state of the tool_table through it's dictionary
-        # we add a new entry in the tid2apcode dict
-        self.tid2apcode[len(self.oldapcode_newapcode)] = int(ap_code)
+        try:
+            self.app.ui.grb_draw_poligonize.triggered.disconnect(self.on_poligonize)
+        except (TypeError, AttributeError):
+            pass
+        try:
+            self.app.ui.grb_draw_semidisc.triggered.diconnect(self.on_add_semidisc)
+        except (TypeError, AttributeError):
+            pass
+        try:
+            self.app.ui.grb_draw_disc.triggered.disconnect(self.on_disc_add)
+        except (TypeError, AttributeError):
+            pass
+        try:
+            self.app.ui.grb_draw_buffer.triggered.disconnect()
+        except (TypeError, AttributeError):
+            pass
+        try:
+            self.app.ui.grb_draw_scale.triggered.disconnect()
+        except (TypeError, AttributeError):
+            pass
+        try:
+            self.app.ui.grb_draw_markarea.triggered.disconnect()
+        except (TypeError, AttributeError):
+            pass
+        try:
+            self.app.ui.grb_draw_eraser.triggered.disconnect(self.on_eraser)
+        except (TypeError, AttributeError):
+            pass
+        try:
+            self.app.ui.grb_draw_transformations.triggered.disconnect(self.on_transform)
+        except (TypeError, AttributeError):
+            pass
 
-        self.app.inform.emit('[success] %s: %s' % (_("Added new aperture with code"), str(ap_code)))
+        try:
+            self.app.jump_signal.disconnect()
+        except (TypeError, AttributeError):
+            pass
 
-        self.build_ui()
+    def clear(self):
+        self.thread.quit()
 
-        self.last_aperture_selected = ap_code
+        self.active_tool = None
+        self.selected = []
+        self.storage_dict.clear()
+        self.results.clear()
 
-        # make a quick sort through the tid2apcode dict so we find which row to select
-        row_to_be_selected = None
-        for key in sorted(self.tid2apcode):
-            if self.tid2apcode[key] == int(ap_code):
-                row_to_be_selected = int(key) - 1
-                break
-        self.apertures_table.selectRow(row_to_be_selected)
+        self.shapes.clear(update=True)
+        self.tool_shape.clear(update=True)
+        self.ma_annotation.clear(update=True)
 
-    def on_aperture_delete(self, ap_code=None):
+    def edit_fcgerber(self, orig_grb_obj):
         """
-        Called for aperture deletion.
+        Imports the geometry found in self.apertures from the given FlatCAM Gerber object
+        into the editor.
 
-        :param ap_code:     An Aperture code; String
-        :return:
+        :param orig_grb_obj: ExcellonObject
+        :return: None
         """
-        self.is_modified = True
-
-        try:
-            if ap_code:
-                try:
-                    deleted_apcode_list = [dd for dd in ap_code]
-                except TypeError:
-                    deleted_apcode_list = [ap_code]
-            else:
-                # deleted_tool_dia = float(self.apertures_table.item(self.apertures_table.currentRow(), 1).text())
-                if len(self.apertures_table.selectionModel().selectedRows()) == 0:
-                    self.app.inform.emit('[WARNING_NOTCL] %s' % _(" Select an aperture in Aperture Table"))
-                    return
 
-                deleted_apcode_list = []
-                for index in self.apertures_table.selectionModel().selectedRows():
-                    row = index.row()
-                    deleted_apcode_list.append(self.apertures_table.item(row, 1).text())
-        except Exception as exc:
-            self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Select an aperture in Aperture Table -->", str(exc))))
-            return
+        self.deactivate_grb_editor()
+        self.activate_grb_editor()
 
-        if deleted_apcode_list:
-            for deleted_aperture in deleted_apcode_list:
-                # delete the storage used for that tool
-                self.storage_dict.pop(deleted_aperture, None)
+        # reset the tool table
+        self.ui.apertures_table.clear()
 
-                for deleted_tool in list(self.tid2apcode.keys()):
-                    if self.tid2apcode[deleted_tool] == deleted_aperture:
-                        # delete the tool
-                        self.tid2apcode.pop(deleted_tool, None)
+        self.ui.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
+        self.last_aperture_selected = None
 
-                self.oldapcode_newapcode.pop(deleted_aperture, None)
-                self.app.inform.emit('[success] %s: %s' % (_("Deleted aperture with code"), str(deleted_aperture)))
+        # create a reference to the source object
+        self.gerber_obj = orig_grb_obj
+        self.gerber_obj_options = orig_grb_obj.options
 
-        self.plot_all()
-        self.build_ui()
+        file_units = self.gerber_obj.units if self.gerber_obj.units else 'IN'
+        app_units = self.app.defaults['units']
+        # self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
 
-        # if last aperture selected was in the apertures deleted than make sure to select a
-        # 'new' last aperture selected because there are tools who depend on it.
-        # if there is no aperture left, then add a default one :)
-        if self.last_aperture_selected in deleted_apcode_list:
-            if self.apertures_table.rowCount() == 0:
-                self.on_aperture_add('10')
-                self.last_aperture_selected = '10'
+        if file_units == app_units:
+            self.conversion_factor = 1
+        else:
+            if file_units == 'IN':
+                self.conversion_factor = 25.4
             else:
-                self.last_aperture_selected = self.apertures_table.item(0, 1).text()
+                self.conversion_factor = 0.0393700787401575
 
-    def on_tool_edit(self):
-        if self.apertures_table.currentItem() is None:
-            return
+        # Hide original geometry
+        orig_grb_obj.visible = False
 
-        # if connected, disconnect the signal from the slot on item_changed as it creates issues
-        self.apertures_table.itemChanged.disconnect()
-        # self.apertures_table.cellPressed.disconnect()
+        # Set selection tolerance
+        # DrawToolShape.tolerance = fc_excellon.drawing_tolerance * 10
 
-        self.is_modified = True
-        val_edited = None
-
-        row_of_item_changed = self.apertures_table.currentRow()
-        col_of_item_changed = self.apertures_table.currentColumn()
+        self.select_tool("select")
 
-        # rows start with 0, tools start with 1 so we adjust the value by 1
-        key_in_tid2apcode = row_of_item_changed + 1
-        ap_code_old = str(self.tid2apcode[key_in_tid2apcode])
+        try:
+            # we activate this after the initial build as we don't need to see the tool been populated
+            self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
+        except Exception as e:
+            log.debug("AppGerberEditor.edit_fcgerber() --> %s" % str(e))
 
-        ap_code_new = self.apertures_table.item(row_of_item_changed, 1).text()
+        # apply the conversion factor on the obj.apertures
+        conv_apertures = deepcopy(self.gerber_obj.apertures)
+        for apcode in self.gerber_obj.apertures:
+            for key in self.gerber_obj.apertures[apcode]:
+                if key == 'width':
+                    conv_apertures[apcode]['width'] = self.gerber_obj.apertures[apcode]['width'] * \
+                                                      self.conversion_factor
+                elif key == 'height':
+                    conv_apertures[apcode]['height'] = self.gerber_obj.apertures[apcode]['height'] * \
+                                                       self.conversion_factor
+                elif key == 'diam':
+                    conv_apertures[apcode]['diam'] = self.gerber_obj.apertures[apcode]['diam'] * self.conversion_factor
+                elif key == 'size':
+                    conv_apertures[apcode]['size'] = self.gerber_obj.apertures[apcode]['size'] * self.conversion_factor
+                else:
+                    conv_apertures[apcode][key] = self.gerber_obj.apertures[apcode][key]
 
-        if col_of_item_changed == 1:
-            # we edited the Aperture Code column (int)
-            try:
-                val_edited = int(self.apertures_table.currentItem().text())
-            except ValueError as e:
-                log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
-                # self.apertures_table.setCurrentItem(None)
-                # we reactivate the signals after the after the tool editing
-                self.apertures_table.itemChanged.connect(self.on_tool_edit)
-                return
-        elif col_of_item_changed == 3:
-            # we edited the Size column (float)
-            try:
-                val_edited = float(self.apertures_table.currentItem().text())
-            except ValueError as e:
-                log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
-                # self.apertures_table.setCurrentItem(None)
-                # we reactivate the signals after the after the tool editing
-                self.apertures_table.itemChanged.connect(self.on_tool_edit)
-                return
-        elif col_of_item_changed == 4:
-            # we edit the Dimensions column (tuple)
-            try:
-                val_edited = [
-                    float(x.strip()) for x in self.apertures_table.currentItem().text().split(",") if x != ''
-                ]
-            except ValueError as e:
-                log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
-                # we reactivate the signals after the after the tool editing
-                self.apertures_table.itemChanged.connect(self.on_tool_edit)
-                return
+        self.gerber_obj.apertures = conv_apertures
+        self.gerber_obj.units = app_units
 
-            if len(val_edited) != 2:
-                self.app.inform.emit("[WARNING_NOTCL] %s" % _("Dimensions need two float values separated by comma."))
-                old_dims_txt = '%s, %s' % (str(self.storage_dict[ap_code_new]['width']),
-                                           str(self.storage_dict[ap_code_new]['height']))
+        # # and then add it to the storage elements (each storage elements is a member of a list
+        # def job_thread(aperture_id):
+        #     with self.app.proc_container.new('%s: %s ...' %
+        #                                      (_("Adding geometry for aperture"),  str(aperture_id))):
+        #         storage_elem = []
+        #         self.storage_dict[aperture_id] = {}
+        #
+        #         # add the Gerber geometry to editor storage
+        #         for k, v in self.gerber_obj.apertures[aperture_id].items():
+        #             try:
+        #                 if k == 'geometry':
+        #                     for geo_el in v:
+        #                         if geo_el:
+        #                             self.add_gerber_shape(DrawToolShape(geo_el), storage_elem)
+        #                     self.storage_dict[aperture_id][k] = storage_elem
+        #                 else:
+        #                     self.storage_dict[aperture_id][k] = self.gerber_obj.apertures[aperture_id][k]
+        #             except Exception as e:
+        #                 log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
+        #
+        #         # Check promises and clear if exists
+        #         while True:
+        #             try:
+        #                 self.grb_plot_promises.remove(aperture_id)
+        #                 time.sleep(0.5)
+        #             except ValueError:
+        #                 break
+        #
+        # # we create a job work each aperture, job that work in a threaded way to store the geometry in local storage
+        # # as DrawToolShapes
+        # for ap_code in self.gerber_obj.apertures:
+        #     self.grb_plot_promises.append(ap_code)
+        #     self.app.worker_task.emit({'fcn': job_thread, 'params': [ap_code]})
+        #
+        # self.set_ui()
+        #
+        # # do the delayed plot only if there is something to plot (the gerber is not empty)
+        # try:
+        #     if bool(self.gerber_obj.apertures):
+        #         self.start_delayed_plot(check_period=1000)
+        #     else:
+        #         raise AttributeError
+        # except AttributeError:
+        #     # now that we have data (empty data actually), create the GUI interface and add it to the Tool Tab
+        #     self.build_ui(first_run=True)
+        #     # and add the first aperture to have something to play with
+        #     self.on_aperture_add('10')
 
-                self.apertures_table.currentItem().setText(old_dims_txt)
-                # we reactivate the signals after the after the tool editing
-                self.apertures_table.itemChanged.connect(self.on_tool_edit)
-                return
-            else:
-                self.app.inform.emit("[success] %s" % _("Dimensions edited."))
+        # self.app.worker_task.emit({'fcn': worker_job, 'params': [self]})
 
-        # In case we edited the Aperture Code therefore the val_edited holds a new Aperture Code
-        # TODO Edit of the Aperture Code is not active yet
-        if col_of_item_changed == 1:
-            # aperture code is not used so we create a new Aperture with the desired Aperture Code
-            if val_edited not in self.oldapcode_newapcode.values():
-                # update the dict that holds as keys old Aperture Codes and as values the new Aperture Codes
-                self.oldapcode_newapcode[ap_code_old] = val_edited
-                # update the dict that holds tool_no as key and tool_dia as value
-                self.tid2apcode[key_in_tid2apcode] = val_edited
+        class Execute_Edit(QtCore.QObject):
 
-                old_aperture_val = self.storage_dict.pop(ap_code_old)
-                self.storage_dict[val_edited] = old_aperture_val
+            start = QtCore.pyqtSignal(str)
 
-            else:
-                # aperture code is already in use so we move the pads from the prior tool to the new tool
-                # but only if they are of the same type
+            def __init__(self, app):
+                super(Execute_Edit, self).__init__()
+                self.app = app
+                self.start.connect(self.run)
 
-                if self.storage_dict[ap_code_old]['type'] == self.storage_dict[ap_code_new]['type']:
-                    # TODO I have to work here; if type == 'R' or 'O' have t otake care of all attributes ...
-                    factor = val_edited / float(ap_code_old)
-                    geometry = []
-                    for geo_el in self.storage_dict[ap_code_old]:
-                        geometric_data = geo_el.geo
-                        new_geo_el = {}
-                        if 'solid' in geometric_data:
-                            new_geo_el['solid'] = deepcopy(affinity.scale(geometric_data['solid'],
-                                                                          xfact=factor, yfact=factor))
-                        if 'follow' in geometric_data:
-                            new_geo_el['follow'] = deepcopy(affinity.scale(geometric_data['follow'],
-                                                                           xfact=factor, yfact=factor))
-                        if 'clear' in geometric_data:
-                            new_geo_el['clear'] = deepcopy(affinity.scale(geometric_data['clear'],
-                                                                          xfact=factor, yfact=factor))
-                        geometry.append(new_geo_el)
+            @staticmethod
+            def worker_job(app_obj):
+                with app_obj.app.proc_container.new('%s ...' % _("Loading")):
+                    # ###############################################################
+                    # APPLY CLEAR_GEOMETRY on the SOLID_GEOMETRY
+                    # ###############################################################
 
-                    self.add_gerber_shape(geometry, self.storage_dict[val_edited])
+                    # list of clear geos that are to be applied to the entire file
+                    global_clear_geo = []
 
-                    self.on_aperture_delete(apcode=ap_code_old)
+                    # create one big geometry made out of all 'negative' (clear) polygons
+                    for aper_id in app_obj.gerber_obj.apertures:
+                        # first check if we have any clear_geometry (LPC) and if yes added it to the global_clear_geo
+                        if 'geometry' in app_obj.gerber_obj.apertures[aper_id]:
+                            for elem in app_obj.gerber_obj.apertures[aper_id]['geometry']:
+                                if 'clear' in elem:
+                                    global_clear_geo.append(elem['clear'])
+                    log.warning("Found %d clear polygons." % len(global_clear_geo))
 
-        # In case we edited the Size of the Aperture therefore the val_edited holds the new Aperture Size
-        # It will happen only for the Aperture Type == 'C' - I make sure of that in the self.build_ui()
-        elif col_of_item_changed == 3:
-            old_size = float(self.storage_dict[ap_code_old]['size'])
-            new_size = float(val_edited)
-            adjust_size = (new_size - old_size) / 2
-            geometry = []
-            for geo_el in self.storage_dict[ap_code_old]['geometry']:
-                g_data = geo_el.geo
-                new_geo_el = {}
-                if 'solid' in g_data:
-                    if 'follow' in g_data:
-                        if isinstance(g_data['follow'], Point):
-                            new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size))
-                        else:
-                            new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size, join_style=2))
-                if 'follow' in g_data:
-                    new_geo_el['follow'] = deepcopy(g_data['follow'])
-                if 'clear' in g_data:
-                    new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(adjust_size, join_style=2))
-                geometry.append(DrawToolShape(new_geo_el))
+                    if global_clear_geo:
+                        global_clear_geo = unary_union(global_clear_geo)
+                        if isinstance(global_clear_geo, Polygon):
+                            global_clear_geo = [global_clear_geo]
 
-            self.storage_dict[ap_code_old]['geometry'].clear()
-            self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
-            # self.storage_dict[ap_code_old]['geometry'] = geometry
+                    # we subtract the big "negative" (clear) geometry from each solid polygon but only the part of
+                    # clear geometry that fits inside the solid. otherwise we may loose the solid
+                    for ap_code in app_obj.gerber_obj.apertures:
+                        temp_solid_geometry = []
+                        if 'geometry' in app_obj.gerber_obj.apertures[ap_code]:
+                            # for elem in self.gerber_obj.apertures[apcode]['geometry']:
+                            #     if 'solid' in elem:
+                            #         solid_geo = elem['solid']
+                            #         for clear_geo in global_clear_geo:
+                            #             # Make sure that the clear_geo is within the solid_geo otherwise we loose
+                            #             # the solid_geometry. We want for clear_geometry just to cut
+                            #             # into solid_geometry not to delete it
+                            #             if clear_geo.within(solid_geo):
+                            #                 solid_geo = solid_geo.difference(clear_geo)
+                            #         try:
+                            #             for poly in solid_geo:
+                            #                 new_elem = {}
+                            #
+                            #                 new_elem['solid'] = poly
+                            #                 if 'clear' in elem:
+                            #                     new_elem['clear'] = poly
+                            #                 if 'follow' in elem:
+                            #                     new_elem['follow'] = poly
+                            #                 temp_elem.append(deepcopy(new_elem))
+                            #         except TypeError:
+                            #             new_elem = {}
+                            #             new_elem['solid'] = solid_geo
+                            #             if 'clear' in elem:
+                            #                 new_elem['clear'] = solid_geo
+                            #             if 'follow' in elem:
+                            #                 new_elem['follow'] = solid_geo
+                            #             temp_elem.append(deepcopy(new_elem))
+                            for elem in app_obj.gerber_obj.apertures[ap_code]['geometry']:
+                                new_elem = {}
+                                if 'solid' in elem:
+                                    solid_geo = elem['solid']
+                                    if not global_clear_geo or global_clear_geo.is_empty:
+                                        pass
+                                    else:
+                                        for clear_geo in global_clear_geo:
+                                            # Make sure that the clear_geo is within the solid_geo otherwise we loose
+                                            # the solid_geometry. We want for clear_geometry just to cut into
+                                            # solid_geometry not to delete it
+                                            if clear_geo.within(solid_geo):
+                                                solid_geo = solid_geo.difference(clear_geo)
 
-        # In case we edited the Dims of the Aperture therefore the val_edited holds a list with the dimensions
-        # in the format [width, height]
-        # It will happen only for the Aperture Type in ['R', 'O'] - I make sure of that in the self.build_ui()
-        # and below
-        elif col_of_item_changed == 4:
-            if str(self.storage_dict[ap_code_old]['type']) == 'R' or str(self.storage_dict[ap_code_old]['type']) == 'O':
-                # use the biggest from them
-                buff_val_lines = max(val_edited)
-                new_width = val_edited[0]
-                new_height = val_edited[1]
+                                    new_elem['solid'] = solid_geo
+                                if 'clear' in elem:
+                                    new_elem['clear'] = elem['clear']
+                                if 'follow' in elem:
+                                    new_elem['follow'] = elem['follow']
+                                temp_solid_geometry.append(deepcopy(new_elem))
 
-                geometry = []
-                for geo_el in self.storage_dict[ap_code_old]['geometry']:
-                    g_data = geo_el.geo
-                    new_geo_el = {}
-                    if 'solid' in g_data:
-                        if 'follow' in g_data:
-                            if isinstance(g_data['follow'], Point):
-                                x = g_data['follow'].x
-                                y = g_data['follow'].y
-                                minx = x - (new_width / 2)
-                                miny = y - (new_height / 2)
-                                maxx = x + (new_width / 2)
-                                maxy = y + (new_height / 2)
-                                geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
-                                new_geo_el['solid'] = deepcopy(geo)
-                            else:
-                                new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(buff_val_lines))
-                    if 'follow' in g_data:
-                        new_geo_el['follow'] = deepcopy(g_data['follow'])
-                    if 'clear' in g_data:
-                        if 'follow' in g_data:
-                            if isinstance(g_data['follow'], Point):
-                                x = g_data['follow'].x
-                                y = g_data['follow'].y
-                                minx = x - (new_width / 2)
-                                miny = y - (new_height / 2)
-                                maxx = x + (new_width / 2)
-                                maxy = y + (new_height / 2)
-                                geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
-                                new_geo_el['clear'] = deepcopy(geo)
-                            else:
-                                new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(buff_val_lines, join_style=2))
-                    geometry.append(DrawToolShape(new_geo_el))
+                            app_obj.gerber_obj.apertures[ap_code]['geometry'] = deepcopy(temp_solid_geometry)
 
-                self.storage_dict[ap_code_old]['geometry'].clear()
-                self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
+                    log.warning("Polygon difference done for %d apertures." % len(app_obj.gerber_obj.apertures))
 
-        self.plot_all()
+                    try:
+                        # Loading the Geometry into Editor Storage
+                        for ap_code, ap_dict in app_obj.gerber_obj.apertures.items():
+                            app_obj.results.append(
+                                app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_code, ap_dict))
+                            )
+                    except Exception as ee:
+                        log.debug(
+                            "AppGerberEditor.edit_fcgerber.worker_job() Adding processes to pool --> %s" % str(ee))
+                        traceback.print_exc()
 
-        # we reactivate the signals after the after the tool editing
-        self.apertures_table.itemChanged.connect(self.on_tool_edit)
-        # self.apertures_table.cellPressed.connect(self.on_row_selected)
+                    output = []
+                    for p in app_obj.results:
+                        output.append(p.get())
 
-    def on_name_activate(self):
-        self.edited_obj_name = self.name_entry.get_value()
+                    for elem in output:
+                        app_obj.storage_dict[elem[0]] = deepcopy(elem[1])
 
-    def on_aptype_changed(self, current_text):
-        # 'O' is letter O not zero.
-        if current_text == 'R' or current_text == 'O':
-            self.apdim_lbl.show()
-            self.apdim_entry.show()
-            self.apsize_entry.setDisabled(True)
-        else:
-            self.apdim_lbl.hide()
-            self.apdim_entry.hide()
-            self.apsize_entry.setDisabled(False)
+                    app_obj.mp_finished.emit(output)
 
-    def activate_grb_editor(self):
-        # adjust the status of the menu entries related to the editor
-        self.app.ui.menueditedit.setDisabled(True)
-        self.app.ui.menueditok.setDisabled(False)
-        # adjust the visibility of some of the canvas context menu
-        self.app.ui.popmenu_edit.setVisible(False)
-        self.app.ui.popmenu_save.setVisible(True)
+            def run(self):
+                self.worker_job(self.app)
 
-        self.connect_canvas_event_handlers()
+        # self.thread.start(QtCore.QThread.NormalPriority)
 
-        # init working objects
-        self.storage_dict = {}
-        self.current_storage = []
-        self.sorted_apcode = []
-        self.new_apertures = {}
-        self.new_aperture_macros = {}
-        self.grb_plot_promises = []
-        self.oldapcode_newapcode = {}
-        self.tid2apcode = {}
+        executable_edit = Execute_Edit(app=self)
+        # executable_edit.moveToThread(self.thread)
+        # executable_edit.start.emit("Started")
 
-        self.shapes.enabled = True
-        self.tool_shape.enabled = True
+        self.app.worker_task.emit({'fcn': executable_edit.run, 'params': []})
 
-        self.app.ui.corner_snap_btn.setVisible(True)
-        self.app.ui.snap_magnet.setVisible(True)
+    @staticmethod
+    def add_apertures(aperture_id, aperture_dict):
+        storage_elem = []
+        storage_dict = {}
 
-        self.app.ui.grb_editor_menu.setDisabled(False)
-        self.app.ui.grb_editor_menu.menuAction().setVisible(True)
+        for k, v in list(aperture_dict.items()):
+            try:
+                if k == 'geometry':
+                    for geo_el in v:
+                        if geo_el:
+                            storage_elem.append(DrawToolShape(geo_el))
+                    storage_dict[k] = storage_elem
+                else:
+                    storage_dict[k] = aperture_dict[k]
+            except Exception as e:
+                log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
 
-        self.app.ui.update_obj_btn.setEnabled(True)
-        self.app.ui.grb_editor_cmenu.setEnabled(True)
+        return [aperture_id, storage_dict]
 
-        self.app.ui.grb_edit_toolbar.setDisabled(False)
-        self.app.ui.grb_edit_toolbar.setVisible(True)
-        # self.app.ui.grid_toolbar.setDisabled(False)
+    def on_multiprocessing_finished(self):
+        self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
+        self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI"))
+        self.set_ui()
+        self.build_ui(first_run=True)
+        self.plot_all()
 
-        # start with GRID toolbar activated
-        if self.app.ui.grid_snap_btn.isChecked() is False:
-            self.app.ui.grid_snap_btn.trigger()
+        # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
+        # - perhaps is a bug in VisPy implementation
+        self.app.app_cursor.enabled = False
+        self.app.app_cursor.enabled = True
+        self.app.inform.emit('[success] %s' % _("Finished loading the Gerber object into the editor."))
 
-        # adjust the visibility of some of the canvas context menu
-        self.app.ui.popmenu_edit.setVisible(False)
-        self.app.ui.popmenu_save.setVisible(True)
+    def update_fcgerber(self):
+        """
+        Create a new Gerber object that contain the edited content of the source Gerber object
 
-        self.app.ui.popmenu_disable.setVisible(False)
-        self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
-        self.app.ui.popmenu_properties.setVisible(False)
-        self.app.ui.grb_editor_cmenu.menuAction().setVisible(True)
+        :return: None
+        """
+        new_grb_name = self.edited_obj_name
 
-    def deactivate_grb_editor(self):
+        # if the 'delayed plot' malfunctioned stop the QTimer
         try:
-            QtGui.QGuiApplication.restoreOverrideCursor()
+            self.plot_thread.stop()
         except Exception as e:
-            log.debug("AppGerberEditor.deactivate_grb_editor() --> %s" % str(e))
+            log.debug("AppGerberEditor.update_fcgerber() --> %s" % str(e))
 
-        self.clear()
+        if "_edit" in self.edited_obj_name:
+            try:
+                _id = int(self.edited_obj_name[-1]) + 1
+                new_grb_name = self.edited_obj_name[:-1] + str(_id)
+            except ValueError:
+                new_grb_name += "_1"
+        else:
+            new_grb_name = self.edited_obj_name + "_edit"
 
-        # adjust the status of the menu entries related to the editor
-        self.app.ui.menueditedit.setDisabled(False)
-        self.app.ui.menueditok.setDisabled(True)
-        # adjust the visibility of some of the canvas context menu
-        self.app.ui.popmenu_edit.setVisible(True)
-        self.app.ui.popmenu_save.setVisible(False)
+        self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
+        # self.new_edited_gerber(new_grb_name, self.storage_dict)
 
-        self.disconnect_canvas_event_handlers()
-        self.app.ui.grb_edit_toolbar.setDisabled(True)
+    @staticmethod
+    def update_options(obj):
+        try:
+            if not obj.options:
+                obj.options = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0}
+                return True
+            else:
+                return False
+        except AttributeError:
+            obj.options = {}
+            return True
 
-        self.app.ui.corner_snap_btn.setVisible(False)
-        self.app.ui.snap_magnet.setVisible(False)
+    def new_edited_gerber(self, outname, aperture_storage):
+        """
+        Creates a new Gerber object for the edited Gerber. Thread-safe.
 
-        # set the Editor Toolbar visibility to what was before entering in the Editor
-        self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
-            else self.app.ui.grb_edit_toolbar.setVisible(True)
+        :param outname:             Name of the resulting object. None causes the name to be that of the file.
+        :type outname:              str
+        :param aperture_storage:    a dictionary that holds all the objects geometry
+        :type aperture_storage:     dict
+        :return: None
+        """
 
-        # Disable visuals
-        self.shapes.enabled = False
-        self.tool_shape.enabled = False
-        # self.app.app_cursor.enabled = False
+        self.app.log.debug("Update the Gerber object with edited content. Source is: %s" %
+                           self.gerber_obj.options['name'].upper())
 
-        self.app.ui.grb_editor_menu.setDisabled(True)
-        self.app.ui.grb_editor_menu.menuAction().setVisible(False)
+        out_name = outname
+        storage_dict = aperture_storage
 
-        self.app.ui.update_obj_btn.setEnabled(False)
+        local_storage_dict = {}
+        for aperture in storage_dict:
+            if 'geometry' in storage_dict[aperture]:
+                # add aperture only if it has geometry
+                if len(storage_dict[aperture]['geometry']) > 0:
+                    local_storage_dict[aperture] = deepcopy(storage_dict[aperture])
 
-        # adjust the visibility of some of the canvas context menu
-        self.app.ui.popmenu_edit.setVisible(True)
-        self.app.ui.popmenu_save.setVisible(False)
+        # How the object should be initialized
+        def obj_init(grb_obj, app_obj):
 
-        self.app.ui.popmenu_disable.setVisible(True)
-        self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
-        self.app.ui.popmenu_properties.setVisible(True)
-        self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
-        self.app.ui.e_editor_cmenu.menuAction().setVisible(False)
-        self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
+            poly_buffer = []
+            follow_buffer = []
 
-        # Show original geometry
-        if self.gerber_obj:
-            self.gerber_obj.visible = True
+            for storage_apcode, storage_val in local_storage_dict.items():
+                grb_obj.apertures[storage_apcode] = {}
 
-    def connect_canvas_event_handlers(self):
-        # Canvas events
+                for k, val in storage_val.items():
+                    if k == 'geometry':
+                        grb_obj.apertures[storage_apcode][k] = []
+                        for geo_el in val:
+                            geometric_data = geo_el.geo
+                            new_geo_el = {}
+                            if 'solid' in geometric_data:
+                                new_geo_el['solid'] = geometric_data['solid']
+                                poly_buffer.append(deepcopy(new_geo_el['solid']))
 
-        # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
-        # but those from AppGeoEditor
+                            if 'follow' in geometric_data:
+                                # if isinstance(geometric_data['follow'], Polygon):
+                                #     buff_val = -(int(storage_val['size']) / 2)
+                                #     geo_f = (geometric_data['follow'].buffer(buff_val)).exterior
+                                #     new_geo_el['follow'] = geo_f
+                                # else:
+                                #     new_geo_el['follow'] = geometric_data['follow']
+                                new_geo_el['follow'] = geometric_data['follow']
+                                follow_buffer.append(deepcopy(new_geo_el['follow']))
+                            else:
+                                if 'solid' in geometric_data:
+                                    geo_f = geometric_data['solid'].exterior
+                                    new_geo_el['follow'] = geo_f
+                                    follow_buffer.append(deepcopy(new_geo_el['follow']))
 
-        # first connect to new, then disconnect the old handlers
-        # don't ask why but if there is nothing connected I've seen issues
-        self.mp = self.canvas.graph_event_connect('mouse_press', self.on_canvas_click)
-        self.mm = self.canvas.graph_event_connect('mouse_move', self.on_canvas_move)
-        self.mr = self.canvas.graph_event_connect('mouse_release', self.on_grb_click_release)
+                            if 'clear' in geometric_data:
+                                new_geo_el['clear'] = geometric_data['clear']
 
-        if self.app.is_legacy is False:
-            self.canvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-            self.canvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-            self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-            self.canvas.graph_event_disconnect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
-        else:
-            self.canvas.graph_event_disconnect(self.app.mp)
-            self.canvas.graph_event_disconnect(self.app.mm)
-            self.canvas.graph_event_disconnect(self.app.mr)
-            self.canvas.graph_event_disconnect(self.app.mdc)
+                            if new_geo_el:
+                                grb_obj.apertures[storage_apcode][k].append(deepcopy(new_geo_el))
+                    else:
+                        grb_obj.apertures[storage_apcode][k] = val
 
-        self.app.collection.view.clicked.disconnect()
+            grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
 
-        self.app.ui.popmenu_copy.triggered.disconnect()
-        self.app.ui.popmenu_delete.triggered.disconnect()
-        self.app.ui.popmenu_move.triggered.disconnect()
+            new_poly = MultiPolygon(poly_buffer)
+            new_poly = new_poly.buffer(0.00000001)
+            new_poly = new_poly.buffer(-0.00000001)
 
-        self.app.ui.popmenu_copy.triggered.connect(self.on_copy_button)
-        self.app.ui.popmenu_delete.triggered.connect(self.on_delete_btn)
-        self.app.ui.popmenu_move.triggered.connect(self.on_move_button)
+            # for ad in grb_obj.apertures:
+            #     print(ad, grb_obj.apertures[ad])
 
-        # Gerber Editor
-        self.app.ui.grb_draw_pad.triggered.connect(self.on_pad_add)
-        self.app.ui.grb_draw_pad_array.triggered.connect(self.on_pad_add_array)
-        self.app.ui.grb_draw_track.triggered.connect(self.on_track_add)
-        self.app.ui.grb_draw_region.triggered.connect(self.on_region_add)
+            try:
+                __ = iter(new_poly)
+            except TypeError:
+                new_poly = [new_poly]
+
+            grb_obj.solid_geometry = deepcopy(new_poly)
+            grb_obj.follow_geometry = deepcopy(follow_buffer)
 
-        self.app.ui.grb_draw_poligonize.triggered.connect(self.on_poligonize)
-        self.app.ui.grb_draw_semidisc.triggered.connect(self.on_add_semidisc)
-        self.app.ui.grb_draw_disc.triggered.connect(self.on_disc_add)
-        self.app.ui.grb_draw_buffer.triggered.connect(lambda: self.select_tool("buffer"))
-        self.app.ui.grb_draw_scale.triggered.connect(lambda: self.select_tool("scale"))
-        self.app.ui.grb_draw_markarea.triggered.connect(lambda: self.select_tool("markarea"))
-        self.app.ui.grb_draw_eraser.triggered.connect(self.on_eraser)
-        self.app.ui.grb_draw_transformations.triggered.connect(self.on_transform)
+            for k, v in self.gerber_obj_options.items():
+                if k == 'name':
+                    grb_obj.options[k] = out_name
+                else:
+                    grb_obj.options[k] = deepcopy(v)
 
-    def disconnect_canvas_event_handlers(self):
+            grb_obj.multigeo = False
+            grb_obj.follow = False
+            grb_obj.units = app_obj.defaults['units']
 
-        # we restore the key and mouse control to FlatCAMApp method
-        # first connect to new, then disconnect the old handlers
-        # don't ask why but if there is nothing connected I've seen issues
-        self.app.mp = self.canvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
-        self.app.mm = self.canvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
-        self.app.mr = self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        self.app.mdc = self.canvas.graph_event_connect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
-        self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
+            try:
+                grb_obj.create_geometry()
+            except KeyError:
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("There are no Aperture definitions in the file. Aborting Gerber creation."))
+            except Exception:
+                msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
+                msg += traceback.format_exc()
+                app_obj.inform.emit(msg)
+                raise
 
-        if self.app.is_legacy is False:
-            self.canvas.graph_event_disconnect('mouse_press', self.on_canvas_click)
-            self.canvas.graph_event_disconnect('mouse_move', self.on_canvas_move)
-            self.canvas.graph_event_disconnect('mouse_release', self.on_grb_click_release)
-        else:
-            self.canvas.graph_event_disconnect(self.mp)
-            self.canvas.graph_event_disconnect(self.mm)
-            self.canvas.graph_event_disconnect(self.mr)
+            grb_obj.source_file = self.app.f_handlers.export_gerber(obj_name=out_name, filename=None,
+                                                                    local_use=grb_obj, use_thread=False)
 
-        try:
-            self.app.ui.popmenu_copy.triggered.disconnect(self.on_copy_button)
-        except (TypeError, AttributeError):
-            pass
+        with self.app.proc_container.new(_("Working ...")):
+            try:
+                self.app.app_obj.new_object("gerber", outname, obj_init)
+            except Exception as e:
+                log.error("Error on Edited object creation: %s" % str(e))
+                # make sure to clean the previous results
+                self.results = []
+                return
 
-        try:
-            self.app.ui.popmenu_delete.triggered.disconnect(self.on_delete_btn)
-        except (TypeError, AttributeError):
-            pass
+            # make sure to clean the previous results
+            self.results = []
+            self.deactivate_grb_editor()
+            self.app.inform.emit('[success] %s' % _("Done."))
 
-        try:
-            self.app.ui.popmenu_move.triggered.disconnect(self.on_move_button)
-        except (TypeError, AttributeError):
-            pass
+    def on_tool_select(self, tool):
+        """
+        Behavior of the toolbar. Tool initialization.
 
-        self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_command)
-        self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
-        self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
+        :rtype : None
+        """
+        current_tool = tool
 
-        # Gerber Editor
+        self.app.log.debug("on_tool_select('%s')" % tool)
 
-        try:
-            self.app.ui.grb_draw_pad.triggered.disconnect(self.on_pad_add)
-        except (TypeError, AttributeError):
-            pass
+        if self.last_aperture_selected is None and current_tool != 'select':
+            # self.draw_app.select_tool('select')
+            self.complete = True
+            current_tool = 'select'
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No aperture is selected"))
 
-        try:
-            self.app.ui.grb_draw_pad_array.triggered.disconnect(self.on_pad_add_array)
-        except (TypeError, AttributeError):
-            pass
+        # This is to make the group behave as radio group
+        if current_tool in self.tools_gerber:
+            if self.tools_gerber[current_tool]["button"].isChecked():
+                self.app.log.debug("%s is checked." % current_tool)
+                for t in self.tools_gerber:
+                    if t != current_tool:
+                        self.tools_gerber[t]["button"].setChecked(False)
 
-        try:
-            self.app.ui.grb_draw_track.triggered.disconnect(self.on_track_add)
-        except (TypeError, AttributeError):
-            pass
+                # this is where the Editor toolbar classes (button's) are instantiated
+                self.active_tool = self.tools_gerber[current_tool]["constructor"](self)
+                # self.app.inform.emit(self.active_tool.start_msg)
+            else:
+                self.app.log.debug("%s is NOT checked." % current_tool)
+                for t in self.tools_gerber:
+                    self.tools_gerber[t]["button"].setChecked(False)
 
-        try:
-            self.app.ui.grb_draw_region.triggered.disconnect(self.on_region_add)
-        except (TypeError, AttributeError):
-            pass
+                self.select_tool('select')
+                self.active_tool = FCApertureSelect(self)
 
-        try:
-            self.app.ui.grb_draw_poligonize.triggered.disconnect(self.on_poligonize)
-        except (TypeError, AttributeError):
-            pass
-        try:
-            self.app.ui.grb_draw_semidisc.triggered.diconnect(self.on_add_semidisc)
-        except (TypeError, AttributeError):
-            pass
-        try:
-            self.app.ui.grb_draw_disc.triggered.disconnect(self.on_disc_add)
-        except (TypeError, AttributeError):
-            pass
-        try:
-            self.app.ui.grb_draw_buffer.triggered.disconnect()
-        except (TypeError, AttributeError):
-            pass
-        try:
-            self.app.ui.grb_draw_scale.triggered.disconnect()
-        except (TypeError, AttributeError):
-            pass
-        try:
-            self.app.ui.grb_draw_markarea.triggered.disconnect()
-        except (TypeError, AttributeError):
-            pass
-        try:
-            self.app.ui.grb_draw_eraser.triggered.disconnect(self.on_eraser)
-        except (TypeError, AttributeError):
-            pass
-        try:
-            self.app.ui.grb_draw_transformations.triggered.disconnect(self.on_transform)
-        except (TypeError, AttributeError):
+    def on_row_selected(self, row, col):
+        # if col == 0:
+        key_modifier = QtWidgets.QApplication.keyboardModifiers()
+        if self.app.defaults["global_mselect_key"] == 'Control':
+            modifier_to_use = Qt.ControlModifier
+        else:
+            modifier_to_use = Qt.ShiftModifier
+
+        if key_modifier == modifier_to_use:
             pass
+        else:
+            self.selected = []
 
         try:
-            self.app.jump_signal.disconnect()
-        except (TypeError, AttributeError):
-            pass
+            selected_ap_code = self.ui.apertures_table.item(row, 1).text()
+            self.last_aperture_selected = copy(selected_ap_code)
 
-    def clear(self):
-        self.thread.quit()
+            for obj in self.storage_dict[selected_ap_code]['geometry']:
+                self.selected.append(obj)
+        except Exception as e:
+            self.app.log.debug(str(e))
 
-        self.active_tool = None
-        self.selected = []
-        self.storage_dict.clear()
-        self.results.clear()
+        self.plot_all()
 
-        self.shapes.clear(update=True)
-        self.tool_shape.clear(update=True)
-        self.ma_annotation.clear(update=True)
+    # def toolbar_tool_toggle(self, key):
+    #     """
+    #
+    #     :param key: key to update in self.options dictionary
+    #     :return:
+    #     """
+    #     self.options[key] = self.sender().isChecked()
+    #     return self.options[key]
 
-    def edit_fcgerber(self, orig_grb_obj):
+    def on_grb_shape_complete(self, storage=None, specific_shape=None, no_plot=False):
         """
-        Imports the geometry found in self.apertures from the given FlatCAM Gerber object
-        into the editor.
 
-        :param orig_grb_obj: ExcellonObject
-        :return: None
+        :param storage: where to store the shape
+        :param specific_shape: optional, the shape to be stored
+        :param no_plot: use this if you want the added shape not plotted
+        :return:
         """
+        self.app.log.debug("on_grb_shape_complete()")
 
-        self.deactivate_grb_editor()
-        self.activate_grb_editor()
+        if specific_shape:
+            geo = specific_shape
+        else:
+            geo = deepcopy(self.active_tool.geometry)
+            if geo is None:
+                return
 
-        # reset the tool table
-        self.apertures_table.clear()
+        if storage is not None:
+            # Add shape
+            self.add_gerber_shape(geo, storage)
+        else:
+            stora = self.storage_dict[self.last_aperture_selected]['geometry']
+            self.add_gerber_shape(geo, storage=stora)
 
-        self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
-        self.last_aperture_selected = None
+        # Remove any utility shapes
+        self.delete_utility_geometry()
+        self.tool_shape.clear(update=True)
 
-        # create a reference to the source object
-        self.gerber_obj = orig_grb_obj
-        self.gerber_obj_options = orig_grb_obj.options
+        if no_plot is False:
+            # Re-plot and reset tool.
+            self.plot_all()
 
-        file_units = self.gerber_obj.units if self.gerber_obj.units else 'IN'
-        app_units = self.app.defaults['units']
-        # self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
+    def add_gerber_shape(self, shape_element, storage):
+        """
+        Adds a shape to the shape storage.
 
-        if file_units == app_units:
-            self.conversion_factor = 1
-        else:
-            if file_units == 'IN':
-                self.conversion_factor = 25.4
-            else:
-                self.conversion_factor = 0.0393700787401575
+        :param shape_element: Shape to be added.
+        :type shape_element: DrawToolShape or DrawToolUtilityShape Geometry is stored as a dict with keys: solid,
+        follow, clear, each value being a list of Shapely objects. The dict can have at least one of the mentioned keys
+        :param storage: Where to store the shape
+        :return: None
+        """
+        # List of DrawToolShape?
 
-        # Hide original geometry
-        orig_grb_obj.visible = False
+        if isinstance(shape_element, list):
+            for subshape in shape_element:
+                self.add_gerber_shape(subshape, storage)
+            return
 
-        # Set selection tolerance
-        # DrawToolShape.tolerance = fc_excellon.drawing_tolerance * 10
+        assert isinstance(shape_element, DrawToolShape), \
+            "Expected a DrawToolShape, got %s" % str(type(shape_element))
+
+        assert shape_element.geo is not None, \
+            "Shape object has empty geometry (None)"
 
-        self.select_tool("select")
+        assert(isinstance(shape_element.geo, list) and len(shape_element.geo) > 0) or not \
+            isinstance(shape_element.geo, list), "Shape objects has empty geometry ([])"
 
-        try:
-            # we activate this after the initial build as we don't need to see the tool been populated
-            self.apertures_table.itemChanged.connect(self.on_tool_edit)
-        except Exception as e:
-            log.debug("AppGerberEditor.edit_fcgerber() --> %s" % str(e))
+        if isinstance(shape_element, DrawToolUtilityShape):
+            self.utility.append(shape_element)
+        else:
+            storage.append(shape_element)
 
-        # apply the conversion factor on the obj.apertures
-        conv_apertures = deepcopy(self.gerber_obj.apertures)
-        for apcode in self.gerber_obj.apertures:
-            for key in self.gerber_obj.apertures[apcode]:
-                if key == 'width':
-                    conv_apertures[apcode]['width'] = self.gerber_obj.apertures[apcode]['width'] * \
-                                                      self.conversion_factor
-                elif key == 'height':
-                    conv_apertures[apcode]['height'] = self.gerber_obj.apertures[apcode]['height'] * \
-                                                       self.conversion_factor
-                elif key == 'diam':
-                    conv_apertures[apcode]['diam'] = self.gerber_obj.apertures[apcode]['diam'] * self.conversion_factor
-                elif key == 'size':
-                    conv_apertures[apcode]['size'] = self.gerber_obj.apertures[apcode]['size'] * self.conversion_factor
-                else:
-                    conv_apertures[apcode][key] = self.gerber_obj.apertures[apcode][key]
+    def on_canvas_click(self, event):
+        """
+        event.x and .y have canvas coordinates
+        event.xdata and .ydata have plot coordinates
 
-        self.gerber_obj.apertures = conv_apertures
-        self.gerber_obj.units = app_units
+        :param event: Event object dispatched by VisPy
+        :return: None
+        """
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            # event_is_dragging = event.is_dragging
+            # right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            # event_is_dragging = self.app.plotcanvas.is_dragging
+            # right_button = 3
 
-        # # and then add it to the storage elements (each storage elements is a member of a list
-        # def job_thread(aperture_id):
-        #     with self.app.proc_container.new('%s: %s ...' %
-        #                                      (_("Adding geometry for aperture"),  str(aperture_id))):
-        #         storage_elem = []
-        #         self.storage_dict[aperture_id] = {}
-        #
-        #         # add the Gerber geometry to editor storage
-        #         for k, v in self.gerber_obj.apertures[aperture_id].items():
-        #             try:
-        #                 if k == 'geometry':
-        #                     for geo_el in v:
-        #                         if geo_el:
-        #                             self.add_gerber_shape(DrawToolShape(geo_el), storage_elem)
-        #                     self.storage_dict[aperture_id][k] = storage_elem
-        #                 else:
-        #                     self.storage_dict[aperture_id][k] = self.gerber_obj.apertures[aperture_id][k]
-        #             except Exception as e:
-        #                 log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
-        #
-        #         # Check promises and clear if exists
-        #         while True:
-        #             try:
-        #                 self.grb_plot_promises.remove(aperture_id)
-        #                 time.sleep(0.5)
-        #             except ValueError:
-        #                 break
-        #
-        # # we create a job work each aperture, job that work in a threaded way to store the geometry in local storage
-        # # as DrawToolShapes
-        # for ap_code in self.gerber_obj.apertures:
-        #     self.grb_plot_promises.append(ap_code)
-        #     self.app.worker_task.emit({'fcn': job_thread, 'params': [ap_code]})
-        #
-        # self.set_ui()
-        #
-        # # do the delayed plot only if there is something to plot (the gerber is not empty)
-        # try:
-        #     if bool(self.gerber_obj.apertures):
-        #         self.start_delayed_plot(check_period=1000)
-        #     else:
-        #         raise AttributeError
-        # except AttributeError:
-        #     # now that we have data (empty data actually), create the GUI interface and add it to the Tool Tab
-        #     self.build_ui(first_run=True)
-        #     # and add the first aperture to have something to play with
-        #     self.on_aperture_add('10')
+        self.pos = self.canvas.translate_coords(event_pos)
 
-        # self.app.worker_task.emit({'fcn': worker_job, 'params': [self]})
+        if self.app.grid_status():
+            self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
+        else:
+            self.pos = (self.pos[0], self.pos[1])
 
-        class Execute_Edit(QtCore.QObject):
+        if event.button == 1:
+            self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
 
-            start = QtCore.pyqtSignal(str)
+            # Selection with left mouse button
+            if self.active_tool is not None:
+                modifiers = QtWidgets.QApplication.keyboardModifiers()
 
-            def __init__(self, app):
-                super(Execute_Edit, self).__init__()
-                self.app = app
-                self.start.connect(self.run)
+                # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
+                if modifiers == QtCore.Qt.ShiftModifier:
+                    self.app.clipboard.setText(
+                        self.app.defaults["global_point_clipboard_format"] %
+                        (self.decimals, self.pos[0], self.decimals, self.pos[1])
+                    )
+                    self.app.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
+                    return
 
-            @staticmethod
-            def worker_job(app_obj):
-                with app_obj.app.proc_container.new('%s ...' % _("Loading")):
-                    # ###############################################################
-                    # APPLY CLEAR_GEOMETRY on the SOLID_GEOMETRY
-                    # ###############################################################
+                # Dispatch event to active_tool
+                self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
 
-                    # list of clear geos that are to be applied to the entire file
-                    global_clear_geo = []
+                # If it is a shape generating tool
+                if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
+                    if self.current_storage is not None:
+                        self.on_grb_shape_complete(self.current_storage)
+                        self.build_ui()
 
-                    # create one big geometry made out of all 'negative' (clear) polygons
-                    for aper_id in app_obj.gerber_obj.apertures:
-                        # first check if we have any clear_geometry (LPC) and if yes added it to the global_clear_geo
-                        if 'geometry' in app_obj.gerber_obj.apertures[aper_id]:
-                            for elem in app_obj.gerber_obj.apertures[aper_id]['geometry']:
-                                if 'clear' in elem:
-                                    global_clear_geo.append(elem['clear'])
-                    log.warning("Found %d clear polygons." % len(global_clear_geo))
+                    # MS: always return to the Select Tool if modifier key is not pressed
+                    # else return to the current tool
+                    key_modifier = QtWidgets.QApplication.keyboardModifiers()
+                    if self.app.defaults["global_mselect_key"] == 'Control':
+                        modifier_to_use = Qt.ControlModifier
+                    else:
+                        modifier_to_use = Qt.ShiftModifier
 
-                    if global_clear_geo:
-                        global_clear_geo = unary_union(global_clear_geo)
-                        if isinstance(global_clear_geo, Polygon):
-                            global_clear_geo = [global_clear_geo]
+                    # if modifier key is pressed then we add to the selected list the current shape but if it's already
+                    # in the selected list, we removed it. Therefore first click selects, second deselects.
+                    if key_modifier == modifier_to_use:
+                        self.select_tool(self.active_tool.name)
+                    else:
+                        # return to Select tool but not for FCPad
+                        if isinstance(self.active_tool, FCPad):
+                            self.select_tool(self.active_tool.name)
+                        else:
+                            self.select_tool("select")
+                        return
 
-                    # we subtract the big "negative" (clear) geometry from each solid polygon but only the part of
-                    # clear geometry that fits inside the solid. otherwise we may loose the solid
-                    for ap_code in app_obj.gerber_obj.apertures:
-                        temp_solid_geometry = []
-                        if 'geometry' in app_obj.gerber_obj.apertures[ap_code]:
-                            # for elem in self.gerber_obj.apertures[apcode]['geometry']:
-                            #     if 'solid' in elem:
-                            #         solid_geo = elem['solid']
-                            #         for clear_geo in global_clear_geo:
-                            #             # Make sure that the clear_geo is within the solid_geo otherwise we loose
-                            #             # the solid_geometry. We want for clear_geometry just to cut
-                            #             # into solid_geometry not to delete it
-                            #             if clear_geo.within(solid_geo):
-                            #                 solid_geo = solid_geo.difference(clear_geo)
-                            #         try:
-                            #             for poly in solid_geo:
-                            #                 new_elem = {}
-                            #
-                            #                 new_elem['solid'] = poly
-                            #                 if 'clear' in elem:
-                            #                     new_elem['clear'] = poly
-                            #                 if 'follow' in elem:
-                            #                     new_elem['follow'] = poly
-                            #                 temp_elem.append(deepcopy(new_elem))
-                            #         except TypeError:
-                            #             new_elem = {}
-                            #             new_elem['solid'] = solid_geo
-                            #             if 'clear' in elem:
-                            #                 new_elem['clear'] = solid_geo
-                            #             if 'follow' in elem:
-                            #                 new_elem['follow'] = solid_geo
-                            #             temp_elem.append(deepcopy(new_elem))
-                            for elem in app_obj.gerber_obj.apertures[ap_code]['geometry']:
-                                new_elem = {}
-                                if 'solid' in elem:
-                                    solid_geo = elem['solid']
-                                    if not global_clear_geo or global_clear_geo.is_empty:
-                                        pass
-                                    else:
-                                        for clear_geo in global_clear_geo:
-                                            # Make sure that the clear_geo is within the solid_geo otherwise we loose
-                                            # the solid_geometry. We want for clear_geometry just to cut into
-                                            # solid_geometry not to delete it
-                                            if clear_geo.within(solid_geo):
-                                                solid_geo = solid_geo.difference(clear_geo)
+                # if isinstance(self.active_tool, FCApertureSelect):
+                #     self.plot_all()
+            else:
+                self.app.log.debug("No active tool to respond to click!")
 
-                                    new_elem['solid'] = solid_geo
-                                if 'clear' in elem:
-                                    new_elem['clear'] = elem['clear']
-                                if 'follow' in elem:
-                                    new_elem['follow'] = elem['follow']
-                                temp_solid_geometry.append(deepcopy(new_elem))
+    def on_grb_click_release(self, event):
+        self.modifiers = QtWidgets.QApplication.keyboardModifiers()
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            # event_is_dragging = event.is_dragging
+            right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            # event_is_dragging = self.app.plotcanvas.is_dragging
+            right_button = 3
 
-                            app_obj.gerber_obj.apertures[ap_code]['geometry'] = deepcopy(temp_solid_geometry)
+        pos_canvas = self.canvas.translate_coords(event_pos)
+        if self.app.grid_status():
+            pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
+        else:
+            pos = (pos_canvas[0], pos_canvas[1])
 
-                    log.warning("Polygon difference done for %d apertures." % len(app_obj.gerber_obj.apertures))
+        # if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context
+        # canvas menu
+        try:
+            if event.button == right_button:  # right click
+                if self.app.ui.popMenu.mouse_is_panning is False:
+                    if self.in_action is False:
+                        try:
+                            QtGui.QGuiApplication.restoreOverrideCursor()
+                        except Exception as e:
+                            log.debug("AppGerberEditor.on_grb_click_release() --> %s" % str(e))
 
-                    try:
-                        # Loading the Geometry into Editor Storage
-                        for ap_code, ap_dict in app_obj.gerber_obj.apertures.items():
-                            app_obj.results.append(
-                                app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_code, ap_dict))
-                            )
-                    except Exception as ee:
-                        log.debug(
-                            "AppGerberEditor.edit_fcgerber.worker_job() Adding processes to pool --> %s" % str(ee))
-                        traceback.print_exc()
+                        if self.active_tool.complete is False and not isinstance(self.active_tool, FCApertureSelect):
+                            self.active_tool.complete = True
+                            self.in_action = False
+                            self.delete_utility_geometry()
+                            self.app.inform.emit('[success] %s' %
+                                                 _("Done."))
+                            self.select_tool('select')
+                        else:
+                            self.app.cursor = QtGui.QCursor()
+                            self.app.populate_cmenu_grids()
+                            self.app.ui.popMenu.popup(self.app.cursor.pos())
+                    else:
+                        # if right click on canvas and the active tool need to be finished (like Path or Polygon)
+                        # right mouse click will finish the action
+                        if isinstance(self.active_tool, FCShapeTool):
+                            if isinstance(self.active_tool, FCTrack):
+                                self.active_tool.make()
+                            else:
+                                self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
+                                self.active_tool.make()
+                            if self.active_tool.complete:
+                                self.on_grb_shape_complete()
+                                self.app.inform.emit('[success] %s' % _("Done."))
 
-                    output = []
-                    for p in app_obj.results:
-                        output.append(p.get())
+                                # MS: always return to the Select Tool if modifier key is not pressed
+                                # else return to the current tool but not for FCTrack
 
-                    for elem in output:
-                        app_obj.storage_dict[elem[0]] = deepcopy(elem[1])
+                                if isinstance(self.active_tool, FCTrack):
+                                    self.select_tool(self.active_tool.name)
+                                else:
+                                    key_modifier = QtWidgets.QApplication.keyboardModifiers()
+                                    if (self.app.defaults["global_mselect_key"] == 'Control' and
+                                        key_modifier == Qt.ControlModifier) or \
+                                            (self.app.defaults["global_mselect_key"] == 'Shift' and
+                                             key_modifier == Qt.ShiftModifier):
 
-                    app_obj.mp_finished.emit(output)
+                                        self.select_tool(self.active_tool.name)
+                                    else:
+                                        self.select_tool("select")
+        except Exception as e:
+            log.warning("AppGerberEditor.on_grb_click_release() RMB click --> Error: %s" % str(e))
+            raise
 
-            def run(self):
-                self.worker_job(self.app)
+        # if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right
+        # selection and then select a type of selection ("enclosing" or "touching")
+        try:
+            if event.button == 1:  # left click
+                if self.app.selection_type is not None:
+                    self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
+                    self.app.selection_type = None
 
-        # self.thread.start(QtCore.QThread.NormalPriority)
+                elif isinstance(self.active_tool, FCApertureSelect):
+                    self.active_tool.click_release((self.pos[0], self.pos[1]))
 
-        executable_edit = Execute_Edit(app=self)
-        # executable_edit.moveToThread(self.thread)
-        # executable_edit.start.emit("Started")
+                    # # if there are selected objects then plot them
+                    # if self.selected:
+                    #     self.plot_all()
+        except Exception as e:
+            log.warning("AppGerberEditor.on_grb_click_release() LMB click --> Error: %s" % str(e))
+            raise
 
-        self.app.worker_task.emit({'fcn': executable_edit.run, 'params': []})
+    def draw_selection_area_handler(self, start_pos, end_pos, sel_type):
+        """
+        :param start_pos: mouse position when the selection LMB click was done
+        :param end_pos: mouse position when the left mouse button is released
+        :param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
+        :return:
+        """
 
-    @staticmethod
-    def add_apertures(aperture_id, aperture_dict):
-        storage_elem = []
-        storage_dict = {}
+        poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
+        sel_aperture = set()
+        self.ui.apertures_table.clearSelection()
 
-        for k, v in list(aperture_dict.items()):
-            try:
-                if k == 'geometry':
-                    for geo_el in v:
-                        if geo_el:
-                            storage_elem.append(DrawToolShape(geo_el))
-                    storage_dict[k] = storage_elem
-                else:
-                    storage_dict[k] = aperture_dict[k]
-            except Exception as e:
-                log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
+        self.app.delete_selection_shape()
+        for storage in self.storage_dict:
+            for obj in self.storage_dict[storage]['geometry']:
+                if 'solid' in obj.geo:
+                    geometric_data = obj.geo['solid']
+                    if (sel_type is True and poly_selection.contains(geometric_data)) or \
+                            (sel_type is False and poly_selection.intersects(geometric_data)):
+                        if self.key == self.app.defaults["global_mselect_key"]:
+                            if obj in self.selected:
+                                self.selected.remove(obj)
+                            else:
+                                # add the object to the selected shapes
+                                self.selected.append(obj)
+                                sel_aperture.add(storage)
+                        else:
+                            self.selected.append(obj)
+                            sel_aperture.add(storage)
 
-        return [aperture_id, storage_dict]
+        try:
+            self.ui.apertures_table.cellPressed.disconnect()
+        except Exception as e:
+            log.debug("AppGerberEditor.draw_selection_Area_handler() --> %s" % str(e))
+        # select the aperture code of the selected geometry, in the tool table
+        self.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+        for aper in sel_aperture:
+            for row_to_sel in range(self.ui.apertures_table.rowCount()):
+                if str(aper) == self.ui.apertures_table.item(row_to_sel, 1).text():
+                    if row_to_sel not in set(index.row() for index in self.ui.apertures_table.selectedIndexes()):
+                        self.ui.apertures_table.selectRow(row_to_sel)
+                    self.last_aperture_selected = aper
+        self.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
 
-    def on_multiprocessing_finished(self):
-        self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
-        self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI"))
-        self.set_ui()
-        self.build_ui(first_run=True)
+        self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
         self.plot_all()
 
-        # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
-        # - perhaps is a bug in VisPy implementation
-        self.app.app_cursor.enabled = False
-        self.app.app_cursor.enabled = True
-        self.app.inform.emit('[success] %s' % _("Finished loading the Gerber object into the editor."))
-
-    def update_fcgerber(self):
+    def on_canvas_move(self, event):
         """
-        Create a new Gerber object that contain the edited content of the source Gerber object
+        Called on 'mouse_move' event
+
+        event.pos have canvas screen coordinates
 
+        :param event: Event object dispatched by VisPy SceneCavas
         :return: None
         """
-        new_grb_name = self.edited_obj_name
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            event_is_dragging = event.is_dragging
+            right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            event_is_dragging = self.app.plotcanvas.is_dragging
+            right_button = 3
 
-        # if the 'delayed plot' malfunctioned stop the QTimer
-        try:
-            self.plot_thread.stop()
-        except Exception as e:
-            log.debug("AppGerberEditor.update_fcgerber() --> %s" % str(e))
+        pos_canvas = self.canvas.translate_coords(event_pos)
+        event.xdata, event.ydata = pos_canvas[0], pos_canvas[1]
 
-        if "_edit" in self.edited_obj_name:
-            try:
-                _id = int(self.edited_obj_name[-1]) + 1
-                new_grb_name = self.edited_obj_name[:-1] + str(_id)
-            except ValueError:
-                new_grb_name += "_1"
-        else:
-            new_grb_name = self.edited_obj_name + "_edit"
+        self.x = event.xdata
+        self.y = event.ydata
 
-        self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
-        # self.new_edited_gerber(new_grb_name, self.storage_dict)
+        self.app.ui.popMenu.mouse_is_panning = False
+
+        # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
+        if event.button == right_button and event_is_dragging == 1:
+            self.app.ui.popMenu.mouse_is_panning = True
+            return
 
-    @staticmethod
-    def update_options(obj):
         try:
-            if not obj.options:
-                obj.options = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0}
-                return True
-            else:
-                return False
-        except AttributeError:
-            obj.options = {}
-            return True
+            x = float(event.xdata)
+            y = float(event.ydata)
+        except TypeError:
+            return
 
-    def new_edited_gerber(self, outname, aperture_storage):
-        """
-        Creates a new Gerber object for the edited Gerber. Thread-safe.
+        if self.active_tool is None:
+            return
 
-        :param outname:             Name of the resulting object. None causes the name to be that of the file.
-        :type outname:              str
-        :param aperture_storage:    a dictionary that holds all the objects geometry
-        :type aperture_storage:     dict
-        :return: None
-        """
+        # # ## Snap coordinates
+        if self.app.grid_status():
+            x, y = self.app.geo_editor.snap(x, y)
 
-        self.app.log.debug("Update the Gerber object with edited content. Source is: %s" %
-                           self.gerber_obj.options['name'].upper())
+            # Update cursor
+            self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color=self.app.cursor_color_3D,
+                                         edge_width=self.app.defaults["global_cursor_width"],
+                                         size=self.app.defaults["global_cursor_size"])
 
-        out_name = outname
-        storage_dict = aperture_storage
+        self.snap_x = x
+        self.snap_y = y
 
-        local_storage_dict = {}
-        for aperture in storage_dict:
-            if 'geometry' in storage_dict[aperture]:
-                # add aperture only if it has geometry
-                if len(storage_dict[aperture]['geometry']) > 0:
-                    local_storage_dict[aperture] = deepcopy(storage_dict[aperture])
+        self.app.mouse = [x, y]
 
-        # How the object should be initialized
-        def obj_init(grb_obj, app_obj):
+        if self.pos is None:
+            self.pos = (0, 0)
+        self.app.dx = x - self.pos[0]
+        self.app.dy = y - self.pos[1]
 
-            poly_buffer = []
-            follow_buffer = []
+        # # update the position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                           "<b>Y</b>: %.4f&nbsp;" % (x, y))
 
-            for storage_apcode, storage_val in local_storage_dict.items():
-                grb_obj.apertures[storage_apcode] = {}
+        # update the reference position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
 
-                for k, val in storage_val.items():
-                    if k == 'geometry':
-                        grb_obj.apertures[storage_apcode][k] = []
-                        for geo_el in val:
-                            geometric_data = geo_el.geo
-                            new_geo_el = {}
-                            if 'solid' in geometric_data:
-                                new_geo_el['solid'] = geometric_data['solid']
-                                poly_buffer.append(deepcopy(new_geo_el['solid']))
+        units = self.app.defaults["units"].lower()
+        self.app.plotcanvas.text_hud.text = \
+            'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX:  \t{:<.4f} [{:s}]\nY:  \t{:<.4f} [{:s}]'.format(
+                self.app.dx, units, self.app.dy, units, x, units, y, units)
+
+        self.update_utility_geometry(data=(x, y))
+
+        # # ## Selection area on canvas section # ##
+        if event_is_dragging == 1 and event.button == 1:
+            # I make an exception for FCRegion and FCTrack because clicking and dragging while making regions can
+            # create strange issues like missing a point in a track/region
+            if isinstance(self.active_tool, FCRegion) or isinstance(self.active_tool, FCTrack):
+                pass
+            else:
+                dx = pos_canvas[0] - self.pos[0]
+                self.app.delete_selection_shape()
+                if dx < 0:
+                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
+                                                         color=self.app.defaults["global_alt_sel_line"],
+                                                         face_color=self.app.defaults['global_alt_sel_fill'])
+                    self.app.selection_type = False
+                else:
+                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y))
+                    self.app.selection_type = True
+        else:
+            self.app.selection_type = None
 
-                            if 'follow' in geometric_data:
-                                # if isinstance(geometric_data['follow'], Polygon):
-                                #     buff_val = -(int(storage_val['size']) / 2)
-                                #     geo_f = (geometric_data['follow'].buffer(buff_val)).exterior
-                                #     new_geo_el['follow'] = geo_f
-                                # else:
-                                #     new_geo_el['follow'] = geometric_data['follow']
-                                new_geo_el['follow'] = geometric_data['follow']
-                                follow_buffer.append(deepcopy(new_geo_el['follow']))
-                            else:
-                                if 'solid' in geometric_data:
-                                    geo_f = geometric_data['solid'].exterior
-                                    new_geo_el['follow'] = geo_f
-                                    follow_buffer.append(deepcopy(new_geo_el['follow']))
+    def update_utility_geometry(self, data):
+        # # ## Utility geometry (animated)
+        geo = self.active_tool.utility_geometry(data=data)
 
-                            if 'clear' in geometric_data:
-                                new_geo_el['clear'] = geometric_data['clear']
+        if isinstance(geo, DrawToolShape) and geo.geo is not None:
+            # Remove any previous utility shape
+            self.tool_shape.clear(update=True)
+            self.draw_utility_geometry(geo=geo)
 
-                            if new_geo_el:
-                                grb_obj.apertures[storage_apcode][k].append(deepcopy(new_geo_el))
-                    else:
-                        grb_obj.apertures[storage_apcode][k] = val
+    def draw_utility_geometry(self, geo):
+        if type(geo.geo) == list:
+            for el in geo.geo:
+                geometric_data = el['solid']
+                # Add the new utility shape
+                self.tool_shape.add(
+                    shape=geometric_data, color=(self.app.defaults["global_draw_color"] + '80'),
+                    # face_color=self.app.defaults['global_alt_sel_fill'],
+                    update=False, layer=0, tolerance=None
+                )
+        else:
+            geometric_data = geo.geo['solid']
+            # Add the new utility shape
+            self.tool_shape.add(
+                shape=geometric_data,
+                color=(self.app.defaults["global_draw_color"] + '80'),
+                # face_color=self.app.defaults['global_alt_sel_fill'],
+                update=False, layer=0, tolerance=None
+            )
 
-            grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
+        self.tool_shape.redraw()
 
-            new_poly = MultiPolygon(poly_buffer)
-            new_poly = new_poly.buffer(0.00000001)
-            new_poly = new_poly.buffer(-0.00000001)
+    def plot_all(self):
+        """
+        Plots all shapes in the editor.
 
-            # for ad in grb_obj.apertures:
-            #     print(ad, grb_obj.apertures[ad])
+        :return: None
+        :rtype: None
+        """
+        with self.app.proc_container.new('%s ...' % _("Plotting")):
+            self.shapes.clear(update=True)
 
-            try:
-                __ = iter(new_poly)
-            except TypeError:
-                new_poly = [new_poly]
+            for storage in self.storage_dict:
+                # fix for apertures with no geometry inside
+                if 'geometry' in self.storage_dict[storage]:
+                    for elem in self.storage_dict[storage]['geometry']:
+                        if 'solid' in elem.geo:
+                            geometric_data = elem.geo['solid']
+                            if geometric_data is None:
+                                continue
 
-            grb_obj.solid_geometry = deepcopy(new_poly)
-            grb_obj.follow_geometry = deepcopy(follow_buffer)
+                            if elem in self.selected:
+                                self.plot_shape(geometry=geometric_data,
+                                                color=self.app.defaults['global_sel_draw_color'] + 'FF',
+                                                linewidth=2)
+                            else:
+                                self.plot_shape(geometry=geometric_data,
+                                                color=self.app.defaults['global_draw_color'] + 'FF')
 
-            for k, v in self.gerber_obj_options.items():
-                if k == 'name':
-                    grb_obj.options[k] = out_name
-                else:
-                    grb_obj.options[k] = deepcopy(v)
+            if self.utility:
+                for elem in self.utility:
+                    geometric_data = elem.geo['solid']
+                    self.plot_shape(geometry=geometric_data, linewidth=1)
+                    continue
 
-            grb_obj.multigeo = False
-            grb_obj.follow = False
-            grb_obj.units = app_obj.defaults['units']
+            self.shapes.redraw()
 
-            try:
-                grb_obj.create_geometry()
-            except KeyError:
-                self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                     _("There are no Aperture definitions in the file. Aborting Gerber creation."))
-            except Exception:
-                msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
-                msg += traceback.format_exc()
-                app_obj.inform.emit(msg)
-                raise
+    def plot_shape(self, geometry=None, color='#000000FF', linewidth=1):
+        """
+        Plots a geometric object or list of objects without rendering. Plotted objects
+        are returned as a list. This allows for efficient/animated rendering.
 
-            grb_obj.source_file = self.app.f_handlers.export_gerber(obj_name=out_name, filename=None,
-                                                                    local_use=grb_obj, use_thread=False)
+        :param geometry:    Geometry to be plotted (Any Shapely.geom kind or list of such)
+        :param color:       Shape color
+        :param linewidth:   Width of lines in # of pixels.
+        :return:            List of plotted elements.
+        """
 
-        with self.app.proc_container.new(_("Working ...")):
-            try:
-                self.app.app_obj.new_object("gerber", outname, obj_init)
-            except Exception as e:
-                log.error("Error on Edited object creation: %s" % str(e))
-                # make sure to clean the previous results
-                self.results = []
+        if geometry is None:
+            geometry = self.active_tool.geometry
+
+        try:
+            self.shapes.add(shape=geometry.geo, color=color, face_color=color, layer=0, tolerance=self.tolerance)
+        except AttributeError:
+            if type(geometry) == Point:
                 return
+            if len(color) == 9:
+                color = color[:7] + 'AF'
+            self.shapes.add(shape=geometry, color=color, face_color=color, layer=0, tolerance=self.tolerance)
 
-            # make sure to clean the previous results
-            self.results = []
-            self.deactivate_grb_editor()
-            self.app.inform.emit('[success] %s' % _("Done."))
+    # def start_delayed_plot(self, check_period):
+    #     """
+    #     This function starts an QTImer and it will periodically check if all the workers finish the plotting functions
+    #
+    #     :param check_period: time at which to check periodically if all plots finished to be plotted
+    #     :return:
+    #     """
+    #
+    #     # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
+    #     # self.plot_thread.start()
+    #     log.debug("AppGerberEditor --> Delayed Plot started.")
+    #     self.plot_thread = QtCore.QTimer()
+    #     self.plot_thread.setInterval(check_period)
+    #     self.plot_finished.connect(self.setup_ui_after_delayed_plot)
+    #     self.plot_thread.timeout.connect(self.check_plot_finished)
+    #     self.plot_thread.start()
+    #
+    # def check_plot_finished(self):
+    #     """
+    #     If all the promises made are finished then all the shapes are in shapes_storage and can be plotted safely and
+    #     then the UI is rebuilt accordingly.
+    #     :return:
+    #     """
+    #
+    #     try:
+    #         if not self.grb_plot_promises:
+    #             self.plot_thread.stop()
+    #             self.plot_finished.emit()
+    #             log.debug("AppGerberEditor --> delayed_plot finished")
+    #     except Exception as e:
+    #         traceback.print_exc()
+    #
+    # def setup_ui_after_delayed_plot(self):
+    #     self.plot_finished.disconnect()
+    #
+    #     # now that we have data, create the GUI interface and add it to the Tool Tab
+    #     self.build_ui(first_run=True)
+    #     self.plot_all()
+    #
+    #     # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
+    #     # - perhaps is a bug in VisPy implementation
+    #     self.app.app_cursor.enabled = False
+    #     self.app.app_cursor.enabled = True
 
-    def on_tool_select(self, tool):
+    def on_zoom_fit(self):
         """
-        Behavior of the toolbar. Tool initialization.
+        Callback for zoom-fit request in Gerber Editor
 
-        :rtype : None
+        :return:        None
         """
-        current_tool = tool
-
-        self.app.log.debug("on_tool_select('%s')" % tool)
-
-        if self.last_aperture_selected is None and current_tool != 'select':
-            # self.draw_app.select_tool('select')
-            self.complete = True
-            current_tool = 'select'
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No aperture is selected"))
+        log.debug("AppGerberEditor.on_zoom_fit()")
 
-        # This is to make the group behave as radio group
-        if current_tool in self.tools_gerber:
-            if self.tools_gerber[current_tool]["button"].isChecked():
-                self.app.log.debug("%s is checked." % current_tool)
-                for t in self.tools_gerber:
-                    if t != current_tool:
-                        self.tools_gerber[t]["button"].setChecked(False)
+        # calculate all the geometry in the edited Gerber object
+        edit_geo = []
+        for ap_code in self.storage_dict:
+            for geo_el in self.storage_dict[ap_code]['geometry']:
+                actual_geo = geo_el.geo
+                if 'solid' in actual_geo:
+                    edit_geo.append(actual_geo['solid'])
 
-                # this is where the Editor toolbar classes (button's) are instantiated
-                self.active_tool = self.tools_gerber[current_tool]["constructor"](self)
-                # self.app.inform.emit(self.active_tool.start_msg)
-            else:
-                self.app.log.debug("%s is NOT checked." % current_tool)
-                for t in self.tools_gerber:
-                    self.tools_gerber[t]["button"].setChecked(False)
+        all_geo = unary_union(edit_geo)
 
-                self.select_tool('select')
-                self.active_tool = FCApertureSelect(self)
+        # calculate the bounds values for the edited Gerber object
+        xmin, ymin, xmax, ymax = all_geo.bounds
 
-    def on_row_selected(self, row, col):
-        # if col == 0:
-        key_modifier = QtWidgets.QApplication.keyboardModifiers()
-        if self.app.defaults["global_mselect_key"] == 'Control':
-            modifier_to_use = Qt.ControlModifier
+        if self.app.is_legacy is False:
+            new_rect = Rect(xmin, ymin, xmax, ymax)
+            self.app.plotcanvas.fit_view(rect=new_rect)
         else:
-            modifier_to_use = Qt.ShiftModifier
+            width = xmax - xmin
+            height = ymax - ymin
+            xmin -= 0.05 * width
+            xmax += 0.05 * width
+            ymin -= 0.05 * height
+            ymax += 0.05 * height
+            self.app.plotcanvas.adjust_axes(xmin, ymin, xmax, ymax)
 
-        if key_modifier == modifier_to_use:
-            pass
-        else:
-            self.selected = []
+    def get_selected(self):
+        """
+        Returns list of shapes that are selected in the editor.
+
+        :return: List of shapes.
+        """
+        # return [shape for shape in self.shape_buffer if shape["selected"]]
+        return self.selected
 
-        try:
-            selected_ap_code = self.apertures_table.item(row, 1).text()
-            self.last_aperture_selected = copy(selected_ap_code)
+    def delete_selected(self):
+        temp_ref = [s for s in self.selected]
 
-            for obj in self.storage_dict[selected_ap_code]['geometry']:
-                self.selected.append(obj)
-        except Exception as e:
-            self.app.log.debug(str(e))
+        if len(temp_ref) == 0:
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Failed. No aperture geometry is selected."))
+            return
 
-        self.plot_all()
+        for shape_sel in temp_ref:
+            self.delete_shape(shape_sel)
 
-    # def toolbar_tool_toggle(self, key):
-    #     """
-    #
-    #     :param key: key to update in self.options dictionary
-    #     :return:
-    #     """
-    #     self.options[key] = self.sender().isChecked()
-    #     return self.options[key]
+        self.selected = []
+        self.build_ui()
+        self.app.inform.emit('[success] %s' % _("Done."))
 
-    def on_grb_shape_complete(self, storage=None, specific_shape=None, no_plot=False):
-        """
+    def delete_shape(self, geo_el):
+        self.is_modified = True
 
-        :param storage: where to store the shape
-        :param specific_shape: optional, the shape to be stored
-        :param no_plot: use this if you want the added shape not plotted
-        :return:
-        """
-        self.app.log.debug("on_grb_shape_complete()")
+        if geo_el in self.utility:
+            self.utility.remove(geo_el)
+            return
 
-        if specific_shape:
-            geo = specific_shape
-        else:
-            geo = deepcopy(self.active_tool.geometry)
-            if geo is None:
-                return
+        for storage in self.storage_dict:
+            try:
+                if geo_el in self.storage_dict[storage]['geometry']:
+                    self.storage_dict[storage]['geometry'].remove(geo_el)
+            except KeyError:
+                pass
+        if geo_el in self.selected:
+            self.selected.remove(geo_el)  # TODO: Check performance
 
-        if storage is not None:
-            # Add shape
-            self.add_gerber_shape(geo, storage)
-        else:
-            stora = self.storage_dict[self.last_aperture_selected]['geometry']
-            self.add_gerber_shape(geo, storage=stora)
+    def delete_utility_geometry(self):
+        # for_deletion = [shape for shape in self.shape_buffer if shape.utility]
+        # for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
+        for_deletion = [geo_el for geo_el in self.utility]
+        for geo_el in for_deletion:
+            self.delete_shape(geo_el)
 
-        # Remove any utility shapes
-        self.delete_utility_geometry()
         self.tool_shape.clear(update=True)
+        self.tool_shape.redraw()
 
-        if no_plot is False:
-            # Re-plot and reset tool.
-            self.plot_all()
+    def on_delete_btn(self):
+        self.delete_selected()
+        self.plot_all()
 
-    def add_gerber_shape(self, shape_element, storage):
+    def select_tool(self, toolname):
         """
-        Adds a shape to the shape storage.
+        Selects a drawing tool. Impacts the object and appGUI.
 
-        :param shape_element: Shape to be added.
-        :type shape_element: DrawToolShape or DrawToolUtilityShape Geometry is stored as a dict with keys: solid,
-        follow, clear, each value being a list of Shapely objects. The dict can have at least one of the mentioned keys
-        :param storage: Where to store the shape
+        :param toolname: Name of the tool.
         :return: None
         """
-        # List of DrawToolShape?
+        self.tools_gerber[toolname]["button"].setChecked(True)
+        self.on_tool_select(toolname)
 
-        if isinstance(shape_element, list):
-            for subshape in shape_element:
-                self.add_gerber_shape(subshape, storage)
-            return
+    def set_selected(self, geo_el):
 
-        assert isinstance(shape_element, DrawToolShape), \
-            "Expected a DrawToolShape, got %s" % str(type(shape_element))
+        # Remove and add to the end.
+        if geo_el in self.selected:
+            self.selected.remove(geo_el)
 
-        assert shape_element.geo is not None, \
-            "Shape object has empty geometry (None)"
+        self.selected.append(geo_el)
 
-        assert(isinstance(shape_element.geo, list) and len(shape_element.geo) > 0) or not \
-            isinstance(shape_element.geo, list), "Shape objects has empty geometry ([])"
+    def set_unselected(self, geo_el):
+        if geo_el in self.selected:
+            self.selected.remove(geo_el)
 
-        if isinstance(shape_element, DrawToolUtilityShape):
-            self.utility.append(shape_element)
+    def on_array_type_combo(self):
+        if self.ui.array_type_combo.currentIndex() == 0:
+            self.ui.array_circular_frame.hide()
+            self.ui.array_linear_frame.show()
         else:
-            storage.append(shape_element)
-
-    def on_canvas_click(self, event):
-        """
-        event.x and .y have canvas coordinates
-        event.xdata and .ydata have plot coordinates
+            self.delete_utility_geometry()
+            self.ui.array_circular_frame.show()
+            self.ui.array_linear_frame.hide()
+            self.app.inform.emit(_("Click on the circular array Center position"))
 
-        :param event: Event object dispatched by VisPy
-        :return: None
-        """
-        if self.app.is_legacy is False:
-            event_pos = event.pos
-            # event_is_dragging = event.is_dragging
-            # right_button = 2
+    def on_linear_angle_radio(self):
+        val = self.ui.pad_axis_radio.get_value()
+        if val == 'A':
+            self.ui.linear_angle_spinner.show()
+            self.ui.linear_angle_label.show()
         else:
-            event_pos = (event.xdata, event.ydata)
-            # event_is_dragging = self.app.plotcanvas.is_dragging
-            # right_button = 3
+            self.ui.linear_angle_spinner.hide()
+            self.ui.linear_angle_label.hide()
 
-        self.pos = self.canvas.translate_coords(event_pos)
+    def on_copy_button(self):
+        self.select_tool('copy')
+        return
 
-        if self.app.grid_status():
-            self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
-        else:
-            self.pos = (self.pos[0], self.pos[1])
+    def on_move_button(self):
+        self.select_tool('move')
+        return
 
-        if event.button == 1:
-            self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
+    def on_pad_add(self):
+        self.select_tool('pad')
 
-            # Selection with left mouse button
-            if self.active_tool is not None:
-                modifiers = QtWidgets.QApplication.keyboardModifiers()
+    def on_pad_add_array(self):
+        self.select_tool('array')
 
-                # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
-                if modifiers == QtCore.Qt.ShiftModifier:
-                    self.app.clipboard.setText(
-                        self.app.defaults["global_point_clipboard_format"] %
-                        (self.decimals, self.pos[0], self.decimals, self.pos[1])
-                    )
-                    self.app.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
-                    return
+    def on_track_add(self):
+        self.select_tool('track')
 
-                # Dispatch event to active_tool
-                self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
+    def on_region_add(self):
+        self.select_tool('region')
 
-                # If it is a shape generating tool
-                if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
-                    if self.current_storage is not None:
-                        self.on_grb_shape_complete(self.current_storage)
-                        self.build_ui()
+    def on_poligonize(self):
+        self.select_tool('poligonize')
 
-                    # MS: always return to the Select Tool if modifier key is not pressed
-                    # else return to the current tool
-                    key_modifier = QtWidgets.QApplication.keyboardModifiers()
-                    if self.app.defaults["global_mselect_key"] == 'Control':
-                        modifier_to_use = Qt.ControlModifier
-                    else:
-                        modifier_to_use = Qt.ShiftModifier
+    def on_disc_add(self):
+        self.select_tool('disc')
 
-                    # if modifier key is pressed then we add to the selected list the current shape but if it's already
-                    # in the selected list, we removed it. Therefore first click selects, second deselects.
-                    if key_modifier == modifier_to_use:
-                        self.select_tool(self.active_tool.name)
-                    else:
-                        # return to Select tool but not for FCPad
-                        if isinstance(self.active_tool, FCPad):
-                            self.select_tool(self.active_tool.name)
-                        else:
-                            self.select_tool("select")
-                        return
+    def on_add_semidisc(self):
+        self.select_tool('semidisc')
 
-                # if isinstance(self.active_tool, FCApertureSelect):
-                #     self.plot_all()
-            else:
-                self.app.log.debug("No active tool to respond to click!")
+    def on_buffer(self):
+        buff_value = 0.01
+        log.debug("AppGerberEditor.on_buffer()")
 
-    def on_grb_click_release(self, event):
-        self.modifiers = QtWidgets.QApplication.keyboardModifiers()
-        if self.app.is_legacy is False:
-            event_pos = event.pos
-            # event_is_dragging = event.is_dragging
-            right_button = 2
-        else:
-            event_pos = (event.xdata, event.ydata)
-            # event_is_dragging = self.app.plotcanvas.is_dragging
-            right_button = 3
+        try:
+            buff_value = float(self.ui.buffer_distance_entry.get_value())
+        except ValueError:
+            # try to convert comma to decimal point. if it's still not working error message and return
+            try:
+                buff_value = float(self.ui.buffer_distance_entry.get_value().replace(',', '.'))
+                self.ui.buffer_distance_entry.set_value(buff_value)
+            except ValueError:
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
+                return
 
-        pos_canvas = self.canvas.translate_coords(event_pos)
-        if self.app.grid_status():
-            pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
-        else:
-            pos = (pos_canvas[0], pos_canvas[1])
+        # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
+        # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
+        join_style = self.ui.buffer_corner_cb.currentIndex() + 1
 
-        # if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context
-        # canvas menu
-        try:
-            if event.button == right_button:  # right click
-                if self.app.ui.popMenu.mouse_is_panning is False:
-                    if self.in_action is False:
-                        try:
-                            QtGui.QGuiApplication.restoreOverrideCursor()
-                        except Exception as e:
-                            log.debug("AppGerberEditor.on_grb_click_release() --> %s" % str(e))
+        def buffer_recursion(geom_el, selection):
+            if type(geom_el) == list:
+                geoms = []
+                for local_geom in geom_el:
+                    geoms.append(buffer_recursion(local_geom, selection=selection))
+                return geoms
+            else:
+                if geom_el in selection:
+                    geometric_data = geom_el.geo
+                    buffered_geom_el = {}
+                    if 'solid' in geometric_data:
+                        buffered_geom_el['solid'] = geometric_data['solid'].buffer(buff_value, join_style=join_style)
+                    if 'follow' in geometric_data:
+                        buffered_geom_el['follow'] = geometric_data['follow'].buffer(buff_value, join_style=join_style)
+                    if 'clear' in geometric_data:
+                        buffered_geom_el['clear'] = geometric_data['clear'].buffer(buff_value, join_style=join_style)
+                    return DrawToolShape(buffered_geom_el)
+                else:
+                    return geom_el
+
+        if not self.ui.apertures_table.selectedItems():
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No aperture to buffer. Select at least one aperture and try again."))
+            return
 
-                        if self.active_tool.complete is False and not isinstance(self.active_tool, FCApertureSelect):
-                            self.active_tool.complete = True
-                            self.in_action = False
-                            self.delete_utility_geometry()
-                            self.app.inform.emit('[success] %s' %
-                                                 _("Done."))
-                            self.select_tool('select')
-                        else:
-                            self.app.cursor = QtGui.QCursor()
-                            self.app.populate_cmenu_grids()
-                            self.app.ui.popMenu.popup(self.app.cursor.pos())
-                    else:
-                        # if right click on canvas and the active tool need to be finished (like Path or Polygon)
-                        # right mouse click will finish the action
-                        if isinstance(self.active_tool, FCShapeTool):
-                            if isinstance(self.active_tool, FCTrack):
-                                self.active_tool.make()
-                            else:
-                                self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
-                                self.active_tool.make()
-                            if self.active_tool.complete:
-                                self.on_grb_shape_complete()
-                                self.app.inform.emit('[success] %s' % _("Done."))
+        for x in self.ui.apertures_table.selectedItems():
+            try:
+                apcode = self.ui.apertures_table.item(x.row(), 1).text()
 
-                                # MS: always return to the Select Tool if modifier key is not pressed
-                                # else return to the current tool but not for FCTrack
+                temp_storage = deepcopy(buffer_recursion(self.storage_dict[apcode]['geometry'], self.selected))
+                self.storage_dict[apcode]['geometry'] = []
+                self.storage_dict[apcode]['geometry'] = temp_storage
+            except Exception as e:
+                log.debug("AppGerberEditor.buffer() --> %s" % str(e))
+                self.app.inform.emit('[ERROR_NOTCL] %s\n%s' % (_("Failed."), str(traceback.print_exc())))
+                return
 
-                                if isinstance(self.active_tool, FCTrack):
-                                    self.select_tool(self.active_tool.name)
-                                else:
-                                    key_modifier = QtWidgets.QApplication.keyboardModifiers()
-                                    if (self.app.defaults["global_mselect_key"] == 'Control' and
-                                        key_modifier == Qt.ControlModifier) or \
-                                            (self.app.defaults["global_mselect_key"] == 'Shift' and
-                                             key_modifier == Qt.ShiftModifier):
+        self.plot_all()
+        self.app.inform.emit('[success] %s' % _("Done."))
 
-                                        self.select_tool(self.active_tool.name)
-                                    else:
-                                        self.select_tool("select")
-        except Exception as e:
-            log.warning("AppGerberEditor.on_grb_click_release() RMB click --> Error: %s" % str(e))
-            raise
+    def on_scale(self):
+        scale_factor = 1.0
+        log.debug("AppGerberEditor.on_scale()")
 
-        # if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right
-        # selection and then select a type of selection ("enclosing" or "touching")
         try:
-            if event.button == 1:  # left click
-                if self.app.selection_type is not None:
-                    self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
-                    self.app.selection_type = None
+            scale_factor = float(self.ui.scale_factor_entry.get_value())
+        except ValueError:
+            # try to convert comma to decimal point. if it's still not working error message and return
+            try:
+                scale_factor = float(self.ui.scale_factor_entry.get_value().replace(',', '.'))
+                self.ui.scale_factor_entry.set_value(scale_factor)
+            except ValueError:
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Scale factor value is missing or wrong format. Add it and retry."))
+                return
 
-                elif isinstance(self.active_tool, FCApertureSelect):
-                    self.active_tool.click_release((self.pos[0], self.pos[1]))
+        def scale_recursion(geom_el, selection):
+            if type(geom_el) == list:
+                geoms = []
+                for local_geom in geom_el:
+                    geoms.append(scale_recursion(local_geom, selection=selection))
+                return geoms
+            else:
+                if geom_el in selection:
+                    geometric_data = geom_el.geo
+                    scaled_geom_el = {}
+                    if 'solid' in geometric_data:
+                        scaled_geom_el['solid'] = affinity.scale(
+                            geometric_data['solid'], scale_factor, scale_factor, origin='center'
+                        )
+                    if 'follow' in geometric_data:
+                        scaled_geom_el['follow'] = affinity.scale(
+                            geometric_data['follow'], scale_factor, scale_factor, origin='center'
+                        )
+                    if 'clear' in geometric_data:
+                        scaled_geom_el['clear'] = affinity.scale(
+                            geometric_data['clear'], scale_factor, scale_factor, origin='center'
+                        )
 
-                    # # if there are selected objects then plot them
-                    # if self.selected:
-                    #     self.plot_all()
-        except Exception as e:
-            log.warning("AppGerberEditor.on_grb_click_release() LMB click --> Error: %s" % str(e))
-            raise
+                    return DrawToolShape(scaled_geom_el)
+                else:
+                    return geom_el
 
-    def draw_selection_area_handler(self, start_pos, end_pos, sel_type):
-        """
-        :param start_pos: mouse position when the selection LMB click was done
-        :param end_pos: mouse position when the left mouse button is released
-        :param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
-        :return:
-        """
+        if not self.ui.apertures_table.selectedItems():
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No aperture to scale. Select at least one aperture and try again."))
+            return
 
-        poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
-        sel_aperture = set()
-        self.apertures_table.clearSelection()
+        for x in self.ui.apertures_table.selectedItems():
+            try:
+                apcode = self.ui.apertures_table.item(x.row(), 1).text()
 
-        self.app.delete_selection_shape()
-        for storage in self.storage_dict:
-            for obj in self.storage_dict[storage]['geometry']:
-                if 'solid' in obj.geo:
-                    geometric_data = obj.geo['solid']
-                    if (sel_type is True and poly_selection.contains(geometric_data)) or \
-                            (sel_type is False and poly_selection.intersects(geometric_data)):
-                        if self.key == self.app.defaults["global_mselect_key"]:
-                            if obj in self.selected:
-                                self.selected.remove(obj)
-                            else:
-                                # add the object to the selected shapes
-                                self.selected.append(obj)
-                                sel_aperture.add(storage)
-                        else:
-                            self.selected.append(obj)
-                            sel_aperture.add(storage)
+                temp_storage = deepcopy(scale_recursion(self.storage_dict[apcode]['geometry'], self.selected))
+                self.storage_dict[apcode]['geometry'] = []
+                self.storage_dict[apcode]['geometry'] = temp_storage
 
-        try:
-            self.apertures_table.cellPressed.disconnect()
-        except Exception as e:
-            log.debug("AppGerberEditor.draw_selection_Area_handler() --> %s" % str(e))
-        # select the aperture code of the selected geometry, in the tool table
-        self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
-        for aper in sel_aperture:
-            for row_to_sel in range(self.apertures_table.rowCount()):
-                if str(aper) == self.apertures_table.item(row_to_sel, 1).text():
-                    if row_to_sel not in set(index.row() for index in self.apertures_table.selectedIndexes()):
-                        self.apertures_table.selectRow(row_to_sel)
-                    self.last_aperture_selected = aper
-        self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+            except Exception as e:
+                log.debug("AppGerberEditor.on_scale() --> %s" % str(e))
 
-        self.apertures_table.cellPressed.connect(self.on_row_selected)
         self.plot_all()
+        self.app.inform.emit('[success] %s' % _("Done."))
 
-    def on_canvas_move(self, event):
-        """
-        Called on 'mouse_move' event
+    def on_markarea(self):
+        # clear previous marking
+        self.ma_annotation.clear(update=True)
 
-        event.pos have canvas screen coordinates
+        self.units = self.app.defaults['units'].upper()
 
-        :param event: Event object dispatched by VisPy SceneCavas
-        :return: None
-        """
-        if self.app.is_legacy is False:
-            event_pos = event.pos
-            event_is_dragging = event.is_dragging
-            right_button = 2
-        else:
-            event_pos = (event.xdata, event.ydata)
-            event_is_dragging = self.app.plotcanvas.is_dragging
-            right_button = 3
+        text = []
+        position = []
 
-        pos_canvas = self.canvas.translate_coords(event_pos)
-        event.xdata, event.ydata = pos_canvas[0], pos_canvas[1]
+        for apcode in self.storage_dict:
+            if 'geometry' in self.storage_dict[apcode]:
+                for geo_el in self.storage_dict[apcode]['geometry']:
+                    if 'solid' in geo_el.geo:
+                        area = geo_el.geo['solid'].area
+                        try:
+                            upper_threshold_val = self.ui.ma_upper_threshold_entry.get_value()
+                        except Exception:
+                            return
 
-        self.x = event.xdata
-        self.y = event.ydata
+                        try:
+                            lower_threshold_val = self.ui.ma_lower_threshold_entry.get_value()
+                        except Exception:
+                            lower_threshold_val = 0.0
 
-        self.app.ui.popMenu.mouse_is_panning = False
+                        if float(upper_threshold_val) > area > float(lower_threshold_val):
+                            current_pos = geo_el.geo['solid'].exterior.coords[-1]
+                            text_elem = '%.*f' % (self.decimals, area)
+                            text.append(text_elem)
+                            position.append(current_pos)
+                            self.geo_to_delete.append(geo_el)
 
-        # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
-        if event.button == right_button and event_is_dragging == 1:
-            self.app.ui.popMenu.mouse_is_panning = True
-            return
+        if text:
+            self.ma_annotation.set(text=text, pos=position, visible=True,
+                                   font_size=self.app.defaults["cncjob_annotation_fontsize"],
+                                   color='#000000FF')
+            self.app.inform.emit('[success] %s' %
+                                 _("Polygons marked."))
+        else:
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No polygons were marked. None fit within the limits."))
 
-        try:
-            x = float(event.xdata)
-            y = float(event.ydata)
-        except TypeError:
-            return
+    def delete_marked_polygons(self):
+        for shape_sel in self.geo_to_delete:
+            self.delete_shape(shape_sel)
 
-        if self.active_tool is None:
-            return
+        self.build_ui()
+        self.plot_all()
+        self.app.inform.emit('[success] %s' % _("Done."))
 
-        # # ## Snap coordinates
-        if self.app.grid_status():
-            x, y = self.app.geo_editor.snap(x, y)
+    def on_eraser(self):
+        self.select_tool('eraser')
 
-            # Update cursor
-            self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color=self.app.cursor_color_3D,
-                                         edge_width=self.app.defaults["global_cursor_width"],
-                                         size=self.app.defaults["global_cursor_size"])
+    def on_transform(self):
+        if type(self.active_tool) == FCTransform:
+            self.select_tool('select')
+        else:
+            self.select_tool('transform')
 
-        self.snap_x = x
-        self.snap_y = y
+    def hide_tool(self, tool_name):
+        # self.app.ui.notebook.setTabText(2, _("Tools"))
+        try:
+            if tool_name == 'all':
+                self.ui.apertures_frame.hide()
+            if tool_name == 'select':
+                self.ui.apertures_frame.show()
+            if tool_name == 'buffer' or tool_name == 'all':
+                self.ui.buffer_tool_frame.hide()
+            if tool_name == 'scale' or tool_name == 'all':
+                self.ui.scale_tool_frame.hide()
+            if tool_name == 'markarea' or tool_name == 'all':
+                self.ui.ma_tool_frame.hide()
+        except Exception as e:
+            log.debug("AppGerberEditor.hide_tool() --> %s" % str(e))
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
 
-        self.app.mouse = [x, y]
 
-        if self.pos is None:
-            self.pos = (0, 0)
-        self.app.dx = x - self.pos[0]
-        self.app.dy = y - self.pos[1]
+class AppGerberEditorUI:
+    def __init__(self, app):
+        self.app = app
 
-        # # update the position label in the infobar since the APP mouse event handlers are disconnected
-        self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f&nbsp;" % (x, y))
+        # Number of decimals used by tools in this class
+        self.decimals = self.app.decimals
 
-        # update the reference position label in the infobar since the APP mouse event handlers are disconnected
-        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
+        # ## Current application units in Upper Case
+        self.units = self.app.defaults['units'].upper()
+
+        self.grb_edit_widget = QtWidgets.QWidget()
+
+        layout = QtWidgets.QVBoxLayout()
+        self.grb_edit_widget.setLayout(layout)
 
-        units = self.app.defaults["units"].lower()
-        self.app.plotcanvas.text_hud.text = \
-            'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX:  \t{:<.4f} [{:s}]\nY:  \t{:<.4f} [{:s}]'.format(
-                self.app.dx, units, self.app.dy, units, x, units, y, units)
+        # Page Title box (spacing between children)
+        self.title_box = QtWidgets.QHBoxLayout()
+        layout.addLayout(self.title_box)
 
-        self.update_utility_geometry(data=(x, y))
+        # Page Title icon
+        pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
+        self.icon = QtWidgets.QLabel()
+        self.icon.setPixmap(pixmap)
+        self.title_box.addWidget(self.icon, stretch=0)
 
-        # # ## Selection area on canvas section # ##
-        if event_is_dragging == 1 and event.button == 1:
-            # I make an exception for FCRegion and FCTrack because clicking and dragging while making regions can
-            # create strange issues like missing a point in a track/region
-            if isinstance(self.active_tool, FCRegion) or isinstance(self.active_tool, FCTrack):
-                pass
-            else:
-                dx = pos_canvas[0] - self.pos[0]
-                self.app.delete_selection_shape()
-                if dx < 0:
-                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
-                                                         color=self.app.defaults["global_alt_sel_line"],
-                                                         face_color=self.app.defaults['global_alt_sel_fill'])
-                    self.app.selection_type = False
-                else:
-                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y))
-                    self.app.selection_type = True
-        else:
-            self.app.selection_type = None
+        # Title label
+        self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Gerber Editor'))
+        self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+        self.title_box.addWidget(self.title_label, stretch=1)
 
-    def update_utility_geometry(self, data):
-        # # ## Utility geometry (animated)
-        geo = self.active_tool.utility_geometry(data=data)
+        # Object name
+        self.name_box = QtWidgets.QHBoxLayout()
+        layout.addLayout(self.name_box)
+        name_label = QtWidgets.QLabel(_("Name:"))
+        self.name_box.addWidget(name_label)
+        self.name_entry = FCEntry()
+        self.name_box.addWidget(self.name_entry)
 
-        if isinstance(geo, DrawToolShape) and geo.geo is not None:
-            # Remove any previous utility shape
-            self.tool_shape.clear(update=True)
-            self.draw_utility_geometry(geo=geo)
+        # Box for custom widgets
+        # This gets populated in offspring implementations.
+        self.custom_box = QtWidgets.QVBoxLayout()
+        layout.addLayout(self.custom_box)
 
-    def draw_utility_geometry(self, geo):
-        if type(geo.geo) == list:
-            for el in geo.geo:
-                geometric_data = el['solid']
-                # Add the new utility shape
-                self.tool_shape.add(
-                    shape=geometric_data, color=(self.app.defaults["global_draw_color"] + '80'),
-                    # face_color=self.app.defaults['global_alt_sel_fill'],
-                    update=False, layer=0, tolerance=None
-                )
-        else:
-            geometric_data = geo.geo['solid']
-            # Add the new utility shape
-            self.tool_shape.add(
-                shape=geometric_data,
-                color=(self.app.defaults["global_draw_color"] + '80'),
-                # face_color=self.app.defaults['global_alt_sel_fill'],
-                update=False, layer=0, tolerance=None
-            )
+        # #########################
+        # ### Gerber Apertures ####
+        # #########################
+        self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
+        self.apertures_table_label.setToolTip(
+            _("Apertures Table for the Gerber Object.")
+        )
+        self.custom_box.addWidget(self.apertures_table_label)
 
-        self.tool_shape.redraw()
+        self.apertures_table = FCTable()
+        # delegate = SpinBoxDelegate(units=self.units)
+        # self.apertures_table.setItemDelegateForColumn(1, delegate)
 
-    def plot_all(self):
-        """
-        Plots all shapes in the editor.
+        self.custom_box.addWidget(self.apertures_table)
 
-        :return: None
-        :rtype: None
-        """
-        with self.app.proc_container.new('%s ...' % _("Plotting")):
-            self.shapes.clear(update=True)
+        self.apertures_table.setColumnCount(5)
+        self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
+        self.apertures_table.setSortingEnabled(False)
+        self.apertures_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
 
-            for storage in self.storage_dict:
-                # fix for apertures with no geometry inside
-                if 'geometry' in self.storage_dict[storage]:
-                    for elem in self.storage_dict[storage]['geometry']:
-                        if 'solid' in elem.geo:
-                            geometric_data = elem.geo['solid']
-                            if geometric_data is None:
-                                continue
+        self.apertures_table.horizontalHeaderItem(0).setToolTip(
+            _("Index"))
+        self.apertures_table.horizontalHeaderItem(1).setToolTip(
+            _("Aperture Code"))
+        self.apertures_table.horizontalHeaderItem(2).setToolTip(
+            _("Type of aperture: circular, rectangle, macros etc"))
+        self.apertures_table.horizontalHeaderItem(4).setToolTip(
+            _("Aperture Size:"))
+        self.apertures_table.horizontalHeaderItem(4).setToolTip(
+            _("Aperture Dimensions:\n"
+              " - (width, height) for R, O type.\n"
+              " - (dia, nVertices) for P type"))
 
-                            if elem in self.selected:
-                                self.plot_shape(geometry=geometric_data,
-                                                color=self.app.defaults['global_sel_draw_color'] + 'FF',
-                                                linewidth=2)
-                            else:
-                                self.plot_shape(geometry=geometric_data,
-                                                color=self.app.defaults['global_draw_color'] + 'FF')
+        self.empty_label = QtWidgets.QLabel('')
+        self.custom_box.addWidget(self.empty_label)
 
-            if self.utility:
-                for elem in self.utility:
-                    geometric_data = elem.geo['solid']
-                    self.plot_shape(geometry=geometric_data, linewidth=1)
-                    continue
+        # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Apertures widgets
+        # this way I can hide/show the frame
+        self.apertures_frame = QtWidgets.QFrame()
+        self.apertures_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.apertures_frame)
+        self.apertures_box = QtWidgets.QVBoxLayout()
+        self.apertures_box.setContentsMargins(0, 0, 0, 0)
+        self.apertures_frame.setLayout(self.apertures_box)
 
-            self.shapes.redraw()
+        # # ## Add/Delete an new Aperture ## ##
 
-    def plot_shape(self, geometry=None, color='#000000FF', linewidth=1):
-        """
-        Plots a geometric object or list of objects without rendering. Plotted objects
-        are returned as a list. This allows for efficient/animated rendering.
+        grid1 = QtWidgets.QGridLayout()
+        self.apertures_box.addLayout(grid1)
+        grid1.setColumnStretch(0, 0)
+        grid1.setColumnStretch(1, 1)
 
-        :param geometry:    Geometry to be plotted (Any Shapely.geom kind or list of such)
-        :param color:       Shape color
-        :param linewidth:   Width of lines in # of pixels.
-        :return:            List of plotted elements.
-        """
+        apcode_lbl = QtWidgets.QLabel('%s:' % _('Aperture Code'))
+        apcode_lbl.setToolTip(_("Code for the new aperture"))
+        grid1.addWidget(apcode_lbl, 1, 0)
 
-        if geometry is None:
-            geometry = self.active_tool.geometry
+        self.apcode_entry = FCSpinner()
+        self.apcode_entry.set_range(0, 999)
+        self.apcode_entry.setWrapping(True)
 
-        try:
-            self.shapes.add(shape=geometry.geo, color=color, face_color=color, layer=0, tolerance=self.tolerance)
-        except AttributeError:
-            if type(geometry) == Point:
-                return
-            if len(color) == 9:
-                color = color[:7] + 'AF'
-            self.shapes.add(shape=geometry, color=color, face_color=color, layer=0, tolerance=self.tolerance)
+        grid1.addWidget(self.apcode_entry, 1, 1)
 
-    # def start_delayed_plot(self, check_period):
-    #     """
-    #     This function starts an QTImer and it will periodically check if all the workers finish the plotting functions
-    #
-    #     :param check_period: time at which to check periodically if all plots finished to be plotted
-    #     :return:
-    #     """
-    #
-    #     # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
-    #     # self.plot_thread.start()
-    #     log.debug("AppGerberEditor --> Delayed Plot started.")
-    #     self.plot_thread = QtCore.QTimer()
-    #     self.plot_thread.setInterval(check_period)
-    #     self.plot_finished.connect(self.setup_ui_after_delayed_plot)
-    #     self.plot_thread.timeout.connect(self.check_plot_finished)
-    #     self.plot_thread.start()
-    #
-    # def check_plot_finished(self):
-    #     """
-    #     If all the promises made are finished then all the shapes are in shapes_storage and can be plotted safely and
-    #     then the UI is rebuilt accordingly.
-    #     :return:
-    #     """
-    #
-    #     try:
-    #         if not self.grb_plot_promises:
-    #             self.plot_thread.stop()
-    #             self.plot_finished.emit()
-    #             log.debug("AppGerberEditor --> delayed_plot finished")
-    #     except Exception as e:
-    #         traceback.print_exc()
-    #
-    # def setup_ui_after_delayed_plot(self):
-    #     self.plot_finished.disconnect()
-    #
-    #     # now that we have data, create the GUI interface and add it to the Tool Tab
-    #     self.build_ui(first_run=True)
-    #     self.plot_all()
-    #
-    #     # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
-    #     # - perhaps is a bug in VisPy implementation
-    #     self.app.app_cursor.enabled = False
-    #     self.app.app_cursor.enabled = True
+        apsize_lbl = QtWidgets.QLabel('%s' % _('Aperture Size:'))
+        apsize_lbl.setToolTip(
+            _("Size for the new aperture.\n"
+              "If aperture type is 'R' or 'O' then\n"
+              "this value is automatically\n"
+              "calculated as:\n"
+              "sqrt(width**2 + height**2)")
+        )
+        grid1.addWidget(apsize_lbl, 2, 0)
 
-    def on_zoom_fit(self):
-        """
-        Callback for zoom-fit request in Gerber Editor
+        self.apsize_entry = FCDoubleSpinner()
+        self.apsize_entry.set_precision(self.decimals)
+        self.apsize_entry.set_range(0.0, 9999)
 
-        :return:        None
-        """
-        log.debug("AppGerberEditor.on_zoom_fit()")
+        grid1.addWidget(self.apsize_entry, 2, 1)
 
-        # calculate all the geometry in the edited Gerber object
-        edit_geo = []
-        for ap_code in self.storage_dict:
-            for geo_el in self.storage_dict[ap_code]['geometry']:
-                actual_geo = geo_el.geo
-                if 'solid' in actual_geo:
-                    edit_geo.append(actual_geo['solid'])
+        aptype_lbl = QtWidgets.QLabel('%s:' % _('Aperture Type'))
+        aptype_lbl.setToolTip(
+            _("Select the type of new aperture. Can be:\n"
+              "C = circular\n"
+              "R = rectangular\n"
+              "O = oblong")
+        )
+        grid1.addWidget(aptype_lbl, 3, 0)
 
-        all_geo = unary_union(edit_geo)
+        self.aptype_cb = FCComboBox()
+        self.aptype_cb.addItems(['C', 'R', 'O'])
+        grid1.addWidget(self.aptype_cb, 3, 1)
+
+        self.apdim_lbl = QtWidgets.QLabel('%s:' % _('Aperture Dim'))
+        self.apdim_lbl.setToolTip(
+            _("Dimensions for the new aperture.\n"
+              "Active only for rectangular apertures (type R).\n"
+              "The format is (width, height)")
+        )
+        grid1.addWidget(self.apdim_lbl, 4, 0)
 
-        # calculate the bounds values for the edited Gerber object
-        xmin, ymin, xmax, ymax = all_geo.bounds
+        self.apdim_entry = EvalEntry2()
+        grid1.addWidget(self.apdim_entry, 4, 1)
 
-        if self.app.is_legacy is False:
-            new_rect = Rect(xmin, ymin, xmax, ymax)
-            self.app.plotcanvas.fit_view(rect=new_rect)
-        else:
-            width = xmax - xmin
-            height = ymax - ymin
-            xmin -= 0.05 * width
-            xmax += 0.05 * width
-            ymin -= 0.05 * height
-            ymax += 0.05 * height
-            self.app.plotcanvas.adjust_axes(xmin, ymin, xmax, ymax)
+        apadd_del_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Add/Delete Aperture'))
+        apadd_del_lbl.setToolTip(
+            _("Add/Delete an aperture in the aperture table")
+        )
+        self.apertures_box.addWidget(apadd_del_lbl)
 
-    def get_selected(self):
-        """
-        Returns list of shapes that are selected in the editor.
+        hlay_ad = QtWidgets.QHBoxLayout()
+        self.apertures_box.addLayout(hlay_ad)
 
-        :return: List of shapes.
-        """
-        # return [shape for shape in self.shape_buffer if shape["selected"]]
-        return self.selected
+        self.addaperture_btn = QtWidgets.QPushButton(_('Add'))
+        self.addaperture_btn.setToolTip(
+            _("Add a new aperture to the aperture list.")
+        )
 
-    def delete_selected(self):
-        temp_ref = [s for s in self.selected]
+        self.delaperture_btn = QtWidgets.QPushButton(_('Delete'))
+        self.delaperture_btn.setToolTip(
+            _("Delete a aperture in the aperture list")
+        )
+        hlay_ad.addWidget(self.addaperture_btn)
+        hlay_ad.addWidget(self.delaperture_btn)
 
-        if len(temp_ref) == 0:
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("Failed. No aperture geometry is selected."))
-            return
+        # ###################
+        # ### BUFFER TOOL ###
+        # ###################
+        self.buffer_tool_frame = QtWidgets.QFrame()
+        self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.buffer_tool_frame)
+        self.buffer_tools_box = QtWidgets.QVBoxLayout()
+        self.buffer_tools_box.setContentsMargins(0, 0, 0, 0)
+        self.buffer_tool_frame.setLayout(self.buffer_tools_box)
+        self.buffer_tool_frame.hide()
 
-        for shape_sel in temp_ref:
-            self.delete_shape(shape_sel)
+        # Title
+        buf_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Buffer Aperture'))
+        buf_title_lbl.setToolTip(
+            _("Buffer a aperture in the aperture list")
+        )
+        self.buffer_tools_box.addWidget(buf_title_lbl)
 
-        self.selected = []
-        self.build_ui()
-        self.app.inform.emit('[success] %s' % _("Done."))
+        # Form Layout
+        buf_form_layout = QtWidgets.QFormLayout()
+        self.buffer_tools_box.addLayout(buf_form_layout)
 
-    def delete_shape(self, geo_el):
-        self.is_modified = True
+        # Buffer distance
+        self.buffer_distance_entry = FCDoubleSpinner()
+        self.buffer_distance_entry.set_precision(self.decimals)
+        self.buffer_distance_entry.set_range(-10000.0000, 10000.0000)
 
-        if geo_el in self.utility:
-            self.utility.remove(geo_el)
-            return
+        buf_form_layout.addRow('%s:' % _("Buffer distance"), self.buffer_distance_entry)
+        self.buffer_corner_lbl = QtWidgets.QLabel('%s:' % _("Buffer corner"))
+        self.buffer_corner_lbl.setToolTip(
+            _("There are 3 types of corners:\n"
+              " - 'Round': the corner is rounded.\n"
+              " - 'Square': the corner is met in a sharp angle.\n"
+              " - 'Beveled': the corner is a line that directly connects the features meeting in the corner")
+        )
+        self.buffer_corner_cb = FCComboBox()
+        self.buffer_corner_cb.addItem(_("Round"))
+        self.buffer_corner_cb.addItem(_("Square"))
+        self.buffer_corner_cb.addItem(_("Beveled"))
+        buf_form_layout.addRow(self.buffer_corner_lbl, self.buffer_corner_cb)
 
-        for storage in self.storage_dict:
-            try:
-                if geo_el in self.storage_dict[storage]['geometry']:
-                    self.storage_dict[storage]['geometry'].remove(geo_el)
-            except KeyError:
-                pass
-        if geo_el in self.selected:
-            self.selected.remove(geo_el)  # TODO: Check performance
+        # Buttons
+        hlay_buf = QtWidgets.QHBoxLayout()
+        self.buffer_tools_box.addLayout(hlay_buf)
 
-    def delete_utility_geometry(self):
-        # for_deletion = [shape for shape in self.shape_buffer if shape.utility]
-        # for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
-        for_deletion = [geo_el for geo_el in self.utility]
-        for geo_el in for_deletion:
-            self.delete_shape(geo_el)
+        self.buffer_button = QtWidgets.QPushButton(_("Buffer"))
+        hlay_buf.addWidget(self.buffer_button)
 
-        self.tool_shape.clear(update=True)
-        self.tool_shape.redraw()
+        # ##################
+        # ### SCALE TOOL ###
+        # ##################
+        self.scale_tool_frame = QtWidgets.QFrame()
+        self.scale_tool_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.scale_tool_frame)
+        self.scale_tools_box = QtWidgets.QVBoxLayout()
+        self.scale_tools_box.setContentsMargins(0, 0, 0, 0)
+        self.scale_tool_frame.setLayout(self.scale_tools_box)
+        self.scale_tool_frame.hide()
 
-    def on_delete_btn(self):
-        self.delete_selected()
-        self.plot_all()
+        # Title
+        scale_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Scale Aperture'))
+        scale_title_lbl.setToolTip(
+            _("Scale a aperture in the aperture list")
+        )
+        self.scale_tools_box.addWidget(scale_title_lbl)
 
-    def select_tool(self, toolname):
-        """
-        Selects a drawing tool. Impacts the object and appGUI.
+        # Form Layout
+        scale_form_layout = QtWidgets.QFormLayout()
+        self.scale_tools_box.addLayout(scale_form_layout)
 
-        :param toolname: Name of the tool.
-        :return: None
-        """
-        self.tools_gerber[toolname]["button"].setChecked(True)
-        self.on_tool_select(toolname)
+        self.scale_factor_lbl = QtWidgets.QLabel('%s:' % _("Scale factor"))
+        self.scale_factor_lbl.setToolTip(
+            _("The factor by which to scale the selected aperture.\n"
+              "Values can be between 0.0000 and 999.9999")
+        )
+        self.scale_factor_entry = FCDoubleSpinner()
+        self.scale_factor_entry.set_precision(self.decimals)
+        self.scale_factor_entry.set_range(0.0000, 10000.0000)
 
-    def set_selected(self, geo_el):
+        scale_form_layout.addRow(self.scale_factor_lbl, self.scale_factor_entry)
 
-        # Remove and add to the end.
-        if geo_el in self.selected:
-            self.selected.remove(geo_el)
+        # Buttons
+        hlay_scale = QtWidgets.QHBoxLayout()
+        self.scale_tools_box.addLayout(hlay_scale)
 
-        self.selected.append(geo_el)
+        self.scale_button = QtWidgets.QPushButton(_("Scale"))
+        hlay_scale.addWidget(self.scale_button)
 
-    def set_unselected(self, geo_el):
-        if geo_el in self.selected:
-            self.selected.remove(geo_el)
+        # ######################
+        # ### Mark Area TOOL ###
+        # ######################
+        self.ma_tool_frame = QtWidgets.QFrame()
+        self.ma_tool_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.ma_tool_frame)
+        self.ma_tools_box = QtWidgets.QVBoxLayout()
+        self.ma_tools_box.setContentsMargins(0, 0, 0, 0)
+        self.ma_tool_frame.setLayout(self.ma_tools_box)
+        self.ma_tool_frame.hide()
 
-    def on_array_type_combo(self):
-        if self.array_type_combo.currentIndex() == 0:
-            self.array_circular_frame.hide()
-            self.array_linear_frame.show()
-        else:
-            self.delete_utility_geometry()
-            self.array_circular_frame.show()
-            self.array_linear_frame.hide()
-            self.app.inform.emit(_("Click on the circular array Center position"))
+        # Title
+        ma_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Mark polygons'))
+        ma_title_lbl.setToolTip(
+            _("Mark the polygon areas.")
+        )
+        self.ma_tools_box.addWidget(ma_title_lbl)
 
-    def on_linear_angle_radio(self):
-        val = self.pad_axis_radio.get_value()
-        if val == 'A':
-            self.linear_angle_spinner.show()
-            self.linear_angle_label.show()
-        else:
-            self.linear_angle_spinner.hide()
-            self.linear_angle_label.hide()
+        # Form Layout
+        ma_form_layout = QtWidgets.QFormLayout()
+        self.ma_tools_box.addLayout(ma_form_layout)
 
-    def on_copy_button(self):
-        self.select_tool('copy')
-        return
+        self.ma_upper_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area UPPER threshold"))
+        self.ma_upper_threshold_lbl.setToolTip(
+            _("The threshold value, all areas less than this are marked.\n"
+              "Can have a value between 0.0000 and 10000.0000")
+        )
+        self.ma_upper_threshold_entry = FCDoubleSpinner()
+        self.ma_upper_threshold_entry.set_precision(self.decimals)
+        self.ma_upper_threshold_entry.set_range(0, 10000)
 
-    def on_move_button(self):
-        self.select_tool('move')
-        return
+        self.ma_lower_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area LOWER threshold"))
+        self.ma_lower_threshold_lbl.setToolTip(
+            _("The threshold value, all areas more than this are marked.\n"
+              "Can have a value between 0.0000 and 10000.0000")
+        )
+        self.ma_lower_threshold_entry = FCDoubleSpinner()
+        self.ma_lower_threshold_entry.set_precision(self.decimals)
+        self.ma_lower_threshold_entry.set_range(0, 10000)
 
-    def on_pad_add(self):
-        self.select_tool('pad')
+        ma_form_layout.addRow(self.ma_lower_threshold_lbl, self.ma_lower_threshold_entry)
+        ma_form_layout.addRow(self.ma_upper_threshold_lbl, self.ma_upper_threshold_entry)
 
-    def on_pad_add_array(self):
-        self.select_tool('array')
+        # Buttons
+        hlay_ma = QtWidgets.QHBoxLayout()
+        self.ma_tools_box.addLayout(hlay_ma)
 
-    def on_track_add(self):
-        self.select_tool('track')
+        self.ma_threshold_button = QtWidgets.QPushButton(_("Mark"))
+        self.ma_threshold_button.setToolTip(
+            _("Mark the polygons that fit within limits.")
+        )
+        hlay_ma.addWidget(self.ma_threshold_button)
 
-    def on_region_add(self):
-        self.select_tool('region')
+        self.ma_delete_button = QtWidgets.QPushButton(_("Delete"))
+        self.ma_delete_button.setToolTip(
+            _("Delete all the marked polygons.")
+        )
+        hlay_ma.addWidget(self.ma_delete_button)
 
-    def on_poligonize(self):
-        self.select_tool('poligonize')
+        self.ma_clear_button = QtWidgets.QPushButton(_("Clear"))
+        self.ma_clear_button.setToolTip(
+            _("Clear all the markings.")
+        )
+        hlay_ma.addWidget(self.ma_clear_button)
 
-    def on_disc_add(self):
-        self.select_tool('disc')
+        # ######################
+        # ### Add Pad Array ####
+        # ######################
+        # add a frame and inside add a vertical box layout. Inside this vbox layout I add
+        # all the add Pad array  widgets
+        # this way I can hide/show the frame
+        self.array_frame = QtWidgets.QFrame()
+        self.array_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.array_frame)
+        self.array_box = QtWidgets.QVBoxLayout()
+        self.array_box.setContentsMargins(0, 0, 0, 0)
+        self.array_frame.setLayout(self.array_box)
 
-    def on_add_semidisc(self):
-        self.select_tool('semidisc')
+        self.emptyarray_label = QtWidgets.QLabel('')
+        self.array_box.addWidget(self.emptyarray_label)
 
-    def on_buffer(self):
-        buff_value = 0.01
-        log.debug("AppGerberEditor.on_buffer()")
+        self.padarray_label = QtWidgets.QLabel('<b>%s</b>' % _("Add Pad Array"))
+        self.padarray_label.setToolTip(
+            _("Add an array of pads (linear or circular array)")
+        )
+        self.array_box.addWidget(self.padarray_label)
 
-        try:
-            buff_value = float(self.buffer_distance_entry.get_value())
-        except ValueError:
-            # try to convert comma to decimal point. if it's still not working error message and return
-            try:
-                buff_value = float(self.buffer_distance_entry.get_value().replace(',', '.'))
-                self.buffer_distance_entry.set_value(buff_value)
-            except ValueError:
-                self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
-                return
-        # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
-        # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
-        join_style = self.buffer_corner_cb.currentIndex() + 1
+        self.array_type_combo = FCComboBox()
+        self.array_type_combo.setToolTip(
+            _("Select the type of pads array to create.\n"
+              "It can be Linear X(Y) or Circular")
+        )
+        self.array_type_combo.addItem(_("Linear"))
+        self.array_type_combo.addItem(_("Circular"))
 
-        def buffer_recursion(geom_el, selection):
-            if type(geom_el) == list:
-                geoms = []
-                for local_geom in geom_el:
-                    geoms.append(buffer_recursion(local_geom, selection=selection))
-                return geoms
-            else:
-                if geom_el in selection:
-                    geometric_data = geom_el.geo
-                    buffered_geom_el = {}
-                    if 'solid' in geometric_data:
-                        buffered_geom_el['solid'] = geometric_data['solid'].buffer(buff_value, join_style=join_style)
-                    if 'follow' in geometric_data:
-                        buffered_geom_el['follow'] = geometric_data['follow'].buffer(buff_value, join_style=join_style)
-                    if 'clear' in geometric_data:
-                        buffered_geom_el['clear'] = geometric_data['clear'].buffer(buff_value, join_style=join_style)
-                    return DrawToolShape(buffered_geom_el)
-                else:
-                    return geom_el
+        self.array_box.addWidget(self.array_type_combo)
 
-        if not self.apertures_table.selectedItems():
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("No aperture to buffer. Select at least one aperture and try again."))
-            return
+        self.array_form = QtWidgets.QFormLayout()
+        self.array_box.addLayout(self.array_form)
 
-        for x in self.apertures_table.selectedItems():
-            try:
-                apcode = self.apertures_table.item(x.row(), 1).text()
+        self.pad_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of pads'))
+        self.pad_array_size_label.setToolTip(
+            _("Specify how many pads to be in the array.")
+        )
+        self.pad_array_size_label.setMinimumWidth(100)
 
-                temp_storage = deepcopy(buffer_recursion(self.storage_dict[apcode]['geometry'], self.selected))
-                self.storage_dict[apcode]['geometry'] = []
-                self.storage_dict[apcode]['geometry'] = temp_storage
-            except Exception as e:
-                log.debug("AppGerberEditor.buffer() --> %s" % str(e))
-                self.app.inform.emit('[ERROR_NOTCL] %s\n%s' % (_("Failed."), str(traceback.print_exc())))
-                return
+        self.pad_array_size_entry = FCSpinner()
+        self.pad_array_size_entry.set_range(1, 9999)
 
-        self.plot_all()
-        self.app.inform.emit('[success] %s' % _("Done."))
+        self.array_form.addRow(self.pad_array_size_label, self.pad_array_size_entry)
 
-    def on_scale(self):
-        scale_factor = 1.0
-        log.debug("AppGerberEditor.on_scale()")
+        self.array_linear_frame = QtWidgets.QFrame()
+        self.array_linear_frame.setContentsMargins(0, 0, 0, 0)
+        self.array_box.addWidget(self.array_linear_frame)
+        self.linear_box = QtWidgets.QVBoxLayout()
+        self.linear_box.setContentsMargins(0, 0, 0, 0)
+        self.array_linear_frame.setLayout(self.linear_box)
 
-        try:
-            scale_factor = float(self.scale_factor_entry.get_value())
-        except ValueError:
-            # try to convert comma to decimal point. if it's still not working error message and return
-            try:
-                scale_factor = float(self.scale_factor_entry.get_value().replace(',', '.'))
-                self.scale_factor_entry.set_value(scale_factor)
-            except ValueError:
-                self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                     _("Scale factor value is missing or wrong format. Add it and retry."))
-                return
+        self.linear_form = QtWidgets.QFormLayout()
+        self.linear_box.addLayout(self.linear_form)
 
-        def scale_recursion(geom_el, selection):
-            if type(geom_el) == list:
-                geoms = []
-                for local_geom in geom_el:
-                    geoms.append(scale_recursion(local_geom, selection=selection))
-                return geoms
-            else:
-                if geom_el in selection:
-                    geometric_data = geom_el.geo
-                    scaled_geom_el = {}
-                    if 'solid' in geometric_data:
-                        scaled_geom_el['solid'] = affinity.scale(
-                            geometric_data['solid'], scale_factor, scale_factor, origin='center'
-                        )
-                    if 'follow' in geometric_data:
-                        scaled_geom_el['follow'] = affinity.scale(
-                            geometric_data['follow'], scale_factor, scale_factor, origin='center'
-                        )
-                    if 'clear' in geometric_data:
-                        scaled_geom_el['clear'] = affinity.scale(
-                            geometric_data['clear'], scale_factor, scale_factor, origin='center'
-                        )
+        self.pad_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
+        self.pad_axis_label.setToolTip(
+            _("Direction on which the linear array is oriented:\n"
+              "- 'X' - horizontal axis \n"
+              "- 'Y' - vertical axis or \n"
+              "- 'Angle' - a custom angle for the array inclination")
+        )
+        self.pad_axis_label.setMinimumWidth(100)
 
-                    return DrawToolShape(scaled_geom_el)
-                else:
-                    return geom_el
+        self.pad_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
+                                        {'label': _('Y'), 'value': 'Y'},
+                                        {'label': _('Angle'), 'value': 'A'}])
+        self.pad_axis_radio.set_value('X')
+        self.linear_form.addRow(self.pad_axis_label, self.pad_axis_radio)
 
-        if not self.apertures_table.selectedItems():
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("No aperture to scale. Select at least one aperture and try again."))
-            return
+        self.pad_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
+        self.pad_pitch_label.setToolTip(
+            _("Pitch = Distance between elements of the array.")
+        )
+        self.pad_pitch_label.setMinimumWidth(100)
 
-        for x in self.apertures_table.selectedItems():
-            try:
-                apcode = self.apertures_table.item(x.row(), 1).text()
+        self.pad_pitch_entry = FCDoubleSpinner()
+        self.pad_pitch_entry.set_precision(self.decimals)
+        self.pad_pitch_entry.set_range(0.0000, 10000.0000)
+        self.pad_pitch_entry.setSingleStep(0.1)
 
-                temp_storage = deepcopy(scale_recursion(self.storage_dict[apcode]['geometry'], self.selected))
-                self.storage_dict[apcode]['geometry'] = []
-                self.storage_dict[apcode]['geometry'] = temp_storage
+        self.linear_form.addRow(self.pad_pitch_label, self.pad_pitch_entry)
 
-            except Exception as e:
-                log.debug("AppGerberEditor.on_scale() --> %s" % str(e))
+        self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
+        self.linear_angle_label.setToolTip(
+            _("Angle at which the linear array is placed.\n"
+              "The precision is of max 2 decimals.\n"
+              "Min value is: -360.00 degrees.\n"
+              "Max value is: 360.00 degrees.")
+        )
+        self.linear_angle_label.setMinimumWidth(100)
 
-        self.plot_all()
-        self.app.inform.emit('[success] %s' % _("Done."))
+        self.linear_angle_spinner = FCDoubleSpinner()
+        self.linear_angle_spinner.set_precision(self.decimals)
+        self.linear_angle_spinner.setRange(-360.00, 360.00)
+        self.linear_form.addRow(self.linear_angle_label, self.linear_angle_spinner)
 
-    def on_markarea(self):
-        # clear previous marking
-        self.ma_annotation.clear(update=True)
+        self.array_circular_frame = QtWidgets.QFrame()
+        self.array_circular_frame.setContentsMargins(0, 0, 0, 0)
+        self.array_box.addWidget(self.array_circular_frame)
+        self.circular_box = QtWidgets.QVBoxLayout()
+        self.circular_box.setContentsMargins(0, 0, 0, 0)
+        self.array_circular_frame.setLayout(self.circular_box)
 
-        self.units = self.app.defaults['units'].upper()
+        self.pad_direction_label = QtWidgets.QLabel('%s:' % _('Direction'))
+        self.pad_direction_label.setToolTip(
+            _("Direction for circular array.\n"
+              "Can be CW = clockwise or CCW = counter clockwise.")
+        )
+        self.pad_direction_label.setMinimumWidth(100)
 
-        text = []
-        position = []
+        self.circular_form = QtWidgets.QFormLayout()
+        self.circular_box.addLayout(self.circular_form)
 
-        for apcode in self.storage_dict:
-            if 'geometry' in self.storage_dict[apcode]:
-                for geo_el in self.storage_dict[apcode]['geometry']:
-                    if 'solid' in geo_el.geo:
-                        area = geo_el.geo['solid'].area
-                        try:
-                            upper_threshold_val = self.ma_upper_threshold_entry.get_value()
-                        except Exception:
-                            return
+        self.pad_direction_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
+                                             {'label': _('CCW'), 'value': 'CCW'}])
+        self.pad_direction_radio.set_value('CW')
+        self.circular_form.addRow(self.pad_direction_label, self.pad_direction_radio)
 
-                        try:
-                            lower_threshold_val = self.ma_lower_threshold_entry.get_value()
-                        except Exception:
-                            lower_threshold_val = 0.0
+        self.pad_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
+        self.pad_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.pad_angle_label.setMinimumWidth(100)
 
-                        if float(upper_threshold_val) > area > float(lower_threshold_val):
-                            current_pos = geo_el.geo['solid'].exterior.coords[-1]
-                            text_elem = '%.*f' % (self.decimals, area)
-                            text.append(text_elem)
-                            position.append(current_pos)
-                            self.geo_to_delete.append(geo_el)
+        self.pad_angle_entry = FCDoubleSpinner()
+        self.pad_angle_entry.set_precision(self.decimals)
+        self.pad_angle_entry.set_range(-360.00, 360.00)
+        self.pad_angle_entry.setSingleStep(0.1)
 
-        if text:
-            self.ma_annotation.set(text=text, pos=position, visible=True,
-                                   font_size=self.app.defaults["cncjob_annotation_fontsize"],
-                                   color='#000000FF')
-            self.app.inform.emit('[success] %s' %
-                                 _("Polygons marked."))
-        else:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("No polygons were marked. None fit within the limits."))
+        self.circular_form.addRow(self.pad_angle_label, self.pad_angle_entry)
 
-    def delete_marked_polygons(self):
-        for shape_sel in self.geo_to_delete:
-            self.delete_shape(shape_sel)
+        self.array_circular_frame.hide()
 
-        self.build_ui()
-        self.plot_all()
-        self.app.inform.emit('[success] %s' % _("Done."))
+        self.linear_angle_spinner.hide()
+        self.linear_angle_label.hide()
 
-    def on_eraser(self):
-        self.select_tool('eraser')
+        self.array_frame.hide()
+        self.custom_box.addStretch()
 
-    def on_transform(self):
-        if type(self.active_tool) == FCTransform:
-            self.select_tool('select')
-        else:
-            self.select_tool('transform')
+        layout.addStretch()
 
-    def hide_tool(self, tool_name):
-        # self.app.ui.notebook.setTabText(2, _("Tools"))
-        try:
-            if tool_name == 'all':
-                self.apertures_frame.hide()
-            if tool_name == 'select':
-                self.apertures_frame.show()
-            if tool_name == 'buffer' or tool_name == 'all':
-                self.buffer_tool_frame.hide()
-            if tool_name == 'scale' or tool_name == 'all':
-                self.scale_tool_frame.hide()
-            if tool_name == 'markarea' or tool_name == 'all':
-                self.ma_tool_frame.hide()
-        except Exception as e:
-            log.debug("AppGerberEditor.hide_tool() --> %s" % str(e))
-        self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
+        # Editor
+        self.exit_editor_button = QtWidgets.QPushButton(_('Exit Editor'))
+        self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
+        self.exit_editor_button.setToolTip(
+            _("Exit from Editor.")
+        )
+        self.exit_editor_button.setStyleSheet("""
+                                              QPushButton
+                                              {
+                                                  font-weight: bold;
+                                              }
+                                              """)
+        layout.addWidget(self.exit_editor_button)
 
 
 class TransformEditorTool(AppTool):