Przeglądaj źródła

- in Tools: Transform, SUb, RulesCheck, DistanceMin, Distance - moved the Tool UI in its own class

Marius Stanciu 5 lat temu
rodzic
commit
cebffe34b1
6 zmienionych plików z 2676 dodań i 2522 usunięć
  1. 2 1
      CHANGELOG.md
  2. 163 130
      appTools/ToolDistance.py
  3. 148 117
      appTools/ToolDistanceMin.py
  4. 1359 1325
      appTools/ToolRulesCheck.py
  5. 211 186
      appTools/ToolSub.py
  6. 793 763
      appTools/ToolTransform.py

+ 2 - 1
CHANGELOG.md

@@ -16,9 +16,10 @@ CHANGELOG for FlatCAM beta
 - in Tool Cutout: modified the UI in preparation for adding the Mouse Bites feature
 - Turkish translation strings were updated by the translator, Mehmet Kaya
 - Film Tool - moved the Tool UI in its own class
-- in Tools: Film, Image, InvertGerber, Optimal, PcbWizard - moved the Tool UI in its own class
+- in Tools: Image, InvertGerber, Optimal, PcbWizard - moved the Tool UI in its own class
 - Tool Isolation - made sure that the app can load from Tools Database only tools marked for Isolation tool
 - Tool Isolation - on Tool start it will attempt to load the Preferences set tools by diameter from Tools Database. If it can't find one there it will add a default tool.
+- in Tools: Transform, SUb, RulesCheck, DistanceMin, Distance - moved the Tool UI in its own class
 
 26.08.2020
 

+ 163 - 130
appTools/ToolDistance.py

@@ -9,7 +9,7 @@ from PyQt5 import QtWidgets, QtCore
 
 from appTool import AppTool
 from appGUI.VisPyVisuals import *
-from appGUI.GUIElements import FCEntry, FCButton, FCCheckBox
+from appGUI.GUIElements import FCEntry, FCButton, FCCheckBox, FCLabel
 
 from shapely.geometry import Point, MultiLineString, Polygon
 
@@ -32,8 +32,6 @@ log = logging.getLogger('base')
 
 class Distance(AppTool):
 
-    toolName = _("Distance Tool")
-
     def __init__(self, app):
         AppTool.__init__(self, app)
 
@@ -43,107 +41,11 @@ class Distance(AppTool):
         self.canvas = self.app.plotcanvas
         self.units = self.app.defaults['units'].lower()
 
-        # ## Title
-        title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
-        self.layout.addWidget(title_label)
-
-        # ## Form Layout
-        grid0 = QtWidgets.QGridLayout()
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-        self.layout.addLayout(grid0)
-
-        self.units_label = QtWidgets.QLabel('%s:' % _("Units"))
-        self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
-        self.units_value = QtWidgets.QLabel("%s" % str({'mm': _("METRIC (mm)"), 'in': _("INCH (in)")}[self.units]))
-        self.units_value.setDisabled(True)
-
-        grid0.addWidget(self.units_label, 0, 0)
-        grid0.addWidget(self.units_value, 0, 1)
-
-        self.snap_center_cb = FCCheckBox(_("Snap to center"))
-        self.snap_center_cb.setToolTip(
-            _("Mouse cursor will snap to the center of the pad/drill\n"
-              "when it is hovering over the geometry of the pad/drill.")
-        )
-        grid0.addWidget(self.snap_center_cb, 1, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 2, 0, 1, 2)
-
-        self.start_label = QtWidgets.QLabel("%s:" % _('Start Coords'))
-        self.start_label.setToolTip(_("This is measuring Start point coordinates."))
-
-        self.start_entry = FCEntry()
-        self.start_entry.setReadOnly(True)
-        self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.start_entry.setToolTip(_("This is measuring Start point coordinates."))
-
-        grid0.addWidget(self.start_label, 3, 0)
-        grid0.addWidget(self.start_entry, 3, 1)
-
-        self.stop_label = QtWidgets.QLabel("%s:" % _('Stop Coords'))
-        self.stop_label.setToolTip(_("This is the measuring Stop point coordinates."))
-
-        self.stop_entry = FCEntry()
-        self.stop_entry.setReadOnly(True)
-        self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.stop_entry.setToolTip(_("This is the measuring Stop point coordinates."))
-
-        grid0.addWidget(self.stop_label, 4, 0)
-        grid0.addWidget(self.stop_entry, 4, 1)
-
-        self.distance_x_label = QtWidgets.QLabel('%s:' % _("Dx"))
-        self.distance_x_label.setToolTip(_("This is the distance measured over the X axis."))
-
-        self.distance_x_entry = FCEntry()
-        self.distance_x_entry.setReadOnly(True)
-        self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis."))
-
-        grid0.addWidget(self.distance_x_label, 5, 0)
-        grid0.addWidget(self.distance_x_entry, 5, 1)
-
-        self.distance_y_label = QtWidgets.QLabel('%s:' % _("Dy"))
-        self.distance_y_label.setToolTip(_("This is the distance measured over the Y axis."))
-
-        self.distance_y_entry = FCEntry()
-        self.distance_y_entry.setReadOnly(True)
-        self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis."))
-
-        grid0.addWidget(self.distance_y_label, 6, 0)
-        grid0.addWidget(self.distance_y_entry, 6, 1)
-
-        self.angle_label = QtWidgets.QLabel('%s:' % _("Angle"))
-        self.angle_label.setToolTip(_("This is orientation angle of the measuring line."))
-
-        self.angle_entry = FCEntry()
-        self.angle_entry.setReadOnly(True)
-        self.angle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.angle_entry.setToolTip(_("This is orientation angle of the measuring line."))
-
-        grid0.addWidget(self.angle_label, 7, 0)
-        grid0.addWidget(self.angle_entry, 7, 1)
-
-        self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))
-        self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance."))
-
-        self.total_distance_entry = FCEntry()
-        self.total_distance_entry.setReadOnly(True)
-        self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.total_distance_entry.setToolTip(_("This is the point to point Euclidian distance."))
-
-        grid0.addWidget(self.total_distance_label, 8, 0)
-        grid0.addWidget(self.total_distance_entry, 8, 1)
-
-        self.measure_btn = FCButton(_("Measure"))
-        # self.measure_btn.setFixedWidth(70)
-        self.layout.addWidget(self.measure_btn)
-
-        self.layout.addStretch()
+        # #############################################################################
+        # ######################### Tool GUI ##########################################
+        # #############################################################################
+        self.ui = DistUI(layout=self.layout, app=self.app)
+        self.toolName = self.ui.toolName
 
         # store here the first click and second click of the measurement process
         self.points = []
@@ -178,8 +80,9 @@ class Distance(AppTool):
         else:
             from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy
             self.sel_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='measurement')
-
-        self.measure_btn.clicked.connect(self.activate_measure_tool)
+        
+        # Signals
+        self.ui.measure_btn.clicked.connect(self.activate_measure_tool)
 
     def run(self, toggle=False):
         self.app.defaults.report_usage("ToolDistance()")
@@ -224,27 +127,27 @@ class Distance(AppTool):
         self.app.command_active = "Distance"
 
         # initial view of the layout
-        self.start_entry.set_value('(0, 0)')
-        self.stop_entry.set_value('(0, 0)')
+        self.ui.start_entry.set_value('(0, 0)')
+        self.ui.stop_entry.set_value('(0, 0)')
 
-        self.distance_x_entry.set_value('0.0')
-        self.distance_y_entry.set_value('0.0')
-        self.angle_entry.set_value('0.0')
-        self.total_distance_entry.set_value('0.0')
+        self.ui.distance_x_entry.set_value('0.0')
+        self.ui.distance_y_entry.set_value('0.0')
+        self.ui.angle_entry.set_value('0.0')
+        self.ui.total_distance_entry.set_value('0.0')
 
-        self.snap_center_cb.set_value(self.app.defaults['tools_dist_snap_center'])
+        self.ui.snap_center_cb.set_value(self.app.defaults['tools_dist_snap_center'])
 
         # snap center works only for Gerber and Execellon Editor's
         if self.original_call_source == 'exc_editor' or self.original_call_source == 'grb_editor':
-            self.snap_center_cb.show()
+            self.ui.snap_center_cb.show()
             snap_center = self.app.defaults['tools_dist_snap_center']
             self.on_snap_toggled(snap_center)
 
-            self.snap_center_cb.toggled.connect(self.on_snap_toggled)
+            self.ui.snap_center_cb.toggled.connect(self.on_snap_toggled)
         else:
-            self.snap_center_cb.hide()
+            self.ui.snap_center_cb.hide()
             try:
-                self.snap_center_cb.toggled.disconnect(self.on_snap_toggled)
+                self.ui.snap_center_cb.toggled.disconnect(self.on_snap_toggled)
             except (TypeError, AttributeError):
                 pass
 
@@ -270,8 +173,8 @@ class Distance(AppTool):
         self.active = True
 
         # disable the measuring button
-        self.measure_btn.setDisabled(True)
-        self.measure_btn.setText('%s...' % _("Working"))
+        self.ui.measure_btn.setDisabled(True)
+        self.ui.measure_btn.setText('%s...' % _("Working"))
 
         self.clicked_meas = 0
         self.original_call_source = copy(self.app.call_source)
@@ -335,8 +238,8 @@ class Distance(AppTool):
         self.points = []
 
         # disable the measuring button
-        self.measure_btn.setDisabled(False)
-        self.measure_btn.setText(_("Measure"))
+        self.ui.measure_btn.setDisabled(False)
+        self.ui.measure_btn.setText(_("Measure"))
 
         self.app.call_source = copy(self.original_call_source)
         if self.original_call_source == 'app':
@@ -406,7 +309,7 @@ class Distance(AppTool):
         if event.button == 1:
             pos_canvas = self.canvas.translate_coords(event_pos)
 
-            if self.snap_center_cb.get_value() is False:
+            if self.ui.snap_center_cb.get_value() is False:
                 # if GRID is active we need to get the snapped positions
                 if self.app.grid_status():
                     pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
@@ -490,14 +393,14 @@ class Distance(AppTool):
 
     def calculate_distance(self, pos):
         if len(self.points) == 1:
-            self.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
+            self.ui.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
             self.app.inform.emit(_("MEASURING: Click on the Destination point ..."))
         elif len(self.points) == 2:
             # self.app.app_cursor.enabled = False
             dx = self.points[1][0] - self.points[0][0]
             dy = self.points[1][1] - self.points[0][1]
             d = math.sqrt(dx ** 2 + dy ** 2)
-            self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
+            self.ui.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
 
             self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | {tx3} = {d_z}".format(
                 tx1=_("MEASURING"),
@@ -508,18 +411,18 @@ class Distance(AppTool):
                 d_z='%*f' % (self.decimals, abs(d)))
             )
 
-            self.distance_x_entry.set_value('%.*f' % (self.decimals, abs(dx)))
-            self.distance_y_entry.set_value('%.*f' % (self.decimals, abs(dy)))
+            self.ui.distance_x_entry.set_value('%.*f' % (self.decimals, abs(dx)))
+            self.ui.distance_y_entry.set_value('%.*f' % (self.decimals, abs(dy)))
 
             try:
                 angle = math.degrees(math.atan2(dy, dx))
                 if angle < 0:
                     angle += 360
-                self.angle_entry.set_value('%.*f' % (self.decimals, angle))
+                self.ui.angle_entry.set_value('%.*f' % (self.decimals, angle))
             except Exception:
                 pass
 
-            self.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d)))
+            self.ui.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d)))
             self.app.ui.rel_position_label.setText(
                 "<b>Dx</b>: {}&nbsp;&nbsp;  <b>Dy</b>: {}&nbsp;&nbsp;&nbsp;&nbsp;".format(
                     '%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1])
@@ -587,7 +490,7 @@ class Distance(AppTool):
                     angle = math.degrees(math.atan2(dy, dx))
                     if angle < 0:
                         angle += 360
-                    self.angle_entry.set_value('%.*f' % (self.decimals, angle))
+                    self.ui.angle_entry.set_value('%.*f' % (self.decimals, angle))
                 except Exception as e:
                     log.debug("Distance.on_mouse_move_meas() -> update utility geometry -> %s" % str(e))
                     pass
@@ -635,4 +538,134 @@ class Distance(AppTool):
     # def set_meas_units(self, units):
     #     self.meas.units_label.setText("[" + self.app.options["units"].lower() + "]")
 
-# end of file
+
+class DistUI:
+    
+    toolName = _("Distance Tool")
+
+    def __init__(self, layout, app):
+        self.app = app
+        self.decimals = self.app.decimals
+        self.layout = layout
+
+        # ## Title
+        title_label = FCLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
+        self.layout.addWidget(title_label)
+
+        # ## Form Layout
+        grid0 = QtWidgets.QGridLayout()
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+        self.layout.addLayout(grid0)
+
+        self.units_label = FCLabel('%s:' % _("Units"))
+        self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
+        self.units_value = FCLabel("%s" % str({'mm': _("METRIC (mm)"), 'in': _("INCH (in)")}[self.units]))
+        self.units_value.setDisabled(True)
+
+        grid0.addWidget(self.units_label, 0, 0)
+        grid0.addWidget(self.units_value, 0, 1)
+
+        self.snap_center_cb = FCCheckBox(_("Snap to center"))
+        self.snap_center_cb.setToolTip(
+            _("Mouse cursor will snap to the center of the pad/drill\n"
+              "when it is hovering over the geometry of the pad/drill.")
+        )
+        grid0.addWidget(self.snap_center_cb, 1, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 2, 0, 1, 2)
+
+        self.start_label = FCLabel("%s:" % _('Start Coords'))
+        self.start_label.setToolTip(_("This is measuring Start point coordinates."))
+
+        self.start_entry = FCEntry()
+        self.start_entry.setReadOnly(True)
+        self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.start_entry.setToolTip(_("This is measuring Start point coordinates."))
+
+        grid0.addWidget(self.start_label, 3, 0)
+        grid0.addWidget(self.start_entry, 3, 1)
+
+        self.stop_label = FCLabel("%s:" % _('Stop Coords'))
+        self.stop_label.setToolTip(_("This is the measuring Stop point coordinates."))
+
+        self.stop_entry = FCEntry()
+        self.stop_entry.setReadOnly(True)
+        self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.stop_entry.setToolTip(_("This is the measuring Stop point coordinates."))
+
+        grid0.addWidget(self.stop_label, 4, 0)
+        grid0.addWidget(self.stop_entry, 4, 1)
+
+        self.distance_x_label = FCLabel('%s:' % _("Dx"))
+        self.distance_x_label.setToolTip(_("This is the distance measured over the X axis."))
+
+        self.distance_x_entry = FCEntry()
+        self.distance_x_entry.setReadOnly(True)
+        self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis."))
+
+        grid0.addWidget(self.distance_x_label, 5, 0)
+        grid0.addWidget(self.distance_x_entry, 5, 1)
+
+        self.distance_y_label = FCLabel('%s:' % _("Dy"))
+        self.distance_y_label.setToolTip(_("This is the distance measured over the Y axis."))
+
+        self.distance_y_entry = FCEntry()
+        self.distance_y_entry.setReadOnly(True)
+        self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis."))
+
+        grid0.addWidget(self.distance_y_label, 6, 0)
+        grid0.addWidget(self.distance_y_entry, 6, 1)
+
+        self.angle_label = FCLabel('%s:' % _("Angle"))
+        self.angle_label.setToolTip(_("This is orientation angle of the measuring line."))
+
+        self.angle_entry = FCEntry()
+        self.angle_entry.setReadOnly(True)
+        self.angle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.angle_entry.setToolTip(_("This is orientation angle of the measuring line."))
+
+        grid0.addWidget(self.angle_label, 7, 0)
+        grid0.addWidget(self.angle_entry, 7, 1)
+
+        self.total_distance_label = FCLabel("<b>%s:</b>" % _('DISTANCE'))
+        self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance."))
+
+        self.total_distance_entry = FCEntry()
+        self.total_distance_entry.setReadOnly(True)
+        self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.total_distance_entry.setToolTip(_("This is the point to point Euclidian distance."))
+
+        grid0.addWidget(self.total_distance_label, 8, 0)
+        grid0.addWidget(self.total_distance_entry, 8, 1)
+
+        self.measure_btn = FCButton(_("Measure"))
+        # self.measure_btn.setFixedWidth(70)
+        self.layout.addWidget(self.measure_btn)
+
+        self.layout.addStretch()
+
+        # #################################### FINSIHED GUI ###########################
+        # #############################################################################
+
+    def confirmation_message(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
+                                                                                  self.decimals,
+                                                                                  minval,
+                                                                                  self.decimals,
+                                                                                  maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+
+    def confirmation_message_int(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                            (_("Edited value is out of range"), minval, maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

+ 148 - 117
appTools/ToolDistanceMin.py

@@ -28,8 +28,6 @@ log = logging.getLogger('base')
 
 class DistanceMin(AppTool):
 
-    toolName = _("Minimum Distance Tool")
-
     def __init__(self, app):
         AppTool.__init__(self, app)
 
@@ -38,101 +36,16 @@ class DistanceMin(AppTool):
         self.units = self.app.defaults['units'].lower()
         self.decimals = self.app.decimals
 
-        # ## Title
-        title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
-        self.layout.addWidget(title_label)
-
-        # ## Form Layout
-        form_layout = QtWidgets.QFormLayout()
-        self.layout.addLayout(form_layout)
-
-        self.units_label = QtWidgets.QLabel('%s:' % _("Units"))
-        self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
-        self.units_value = QtWidgets.QLabel("%s" % str({'mm': _("METRIC (mm)"), 'in': _("INCH (in)")}[self.units]))
-        self.units_value.setDisabled(True)
-
-        self.start_label = QtWidgets.QLabel("%s:" % _('First object point'))
-        self.start_label.setToolTip(_("This is first object point coordinates.\n"
-                                      "This is the start point for measuring distance."))
-
-        self.stop_label = QtWidgets.QLabel("%s:" % _('Second object point'))
-        self.stop_label.setToolTip(_("This is second object point coordinates.\n"
-                                      "This is the end point for measuring distance."))
-
-        self.distance_x_label = QtWidgets.QLabel('%s:' % _("Dx"))
-        self.distance_x_label.setToolTip(_("This is the distance measured over the X axis."))
-
-        self.distance_y_label = QtWidgets.QLabel('%s:' % _("Dy"))
-        self.distance_y_label.setToolTip(_("This is the distance measured over the Y axis."))
-
-        self.angle_label = QtWidgets.QLabel('%s:' % _("Angle"))
-        self.angle_label.setToolTip(_("This is orientation angle of the measuring line."))
-
-        self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))
-        self.total_distance_label.setToolTip(_("This is the point to point Euclidean distance."))
-
-        self.half_point_label = QtWidgets.QLabel("<b>%s:</b>" % _('Half Point'))
-        self.half_point_label.setToolTip(_("This is the middle point of the point to point Euclidean distance."))
-
-        self.start_entry = FCEntry()
-        self.start_entry.setReadOnly(True)
-        self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.start_entry.setToolTip(_("This is first object point coordinates.\n"
-                                      "This is the start point for measuring distance."))
-
-        self.stop_entry = FCEntry()
-        self.stop_entry.setReadOnly(True)
-        self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.stop_entry.setToolTip(_("This is second object point coordinates.\n"
-                                      "This is the end point for measuring distance."))
-
-        self.distance_x_entry = FCEntry()
-        self.distance_x_entry.setReadOnly(True)
-        self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis."))
-
-        self.distance_y_entry = FCEntry()
-        self.distance_y_entry.setReadOnly(True)
-        self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis."))
-
-        self.angle_entry = FCEntry()
-        self.angle_entry.setReadOnly(True)
-        self.angle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.angle_entry.setToolTip(_("This is orientation angle of the measuring line."))
-
-        self.total_distance_entry = FCEntry()
-        self.total_distance_entry.setReadOnly(True)
-        self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.total_distance_entry.setToolTip(_("This is the point to point Euclidean distance."))
-
-        self.half_point_entry = FCEntry()
-        self.half_point_entry.setReadOnly(True)
-        self.half_point_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.half_point_entry.setToolTip(_("This is the middle point of the point to point Euclidean distance."))
-
-        self.measure_btn = QtWidgets.QPushButton(_("Measure"))
-        self.layout.addWidget(self.measure_btn)
-
-        self.jump_hp_btn = QtWidgets.QPushButton(_("Jump to Half Point"))
-        self.layout.addWidget(self.jump_hp_btn)
-        self.jump_hp_btn.setDisabled(True)
-
-        form_layout.addRow(self.units_label, self.units_value)
-        form_layout.addRow(self.start_label, self.start_entry)
-        form_layout.addRow(self.stop_label, self.stop_entry)
-        form_layout.addRow(self.distance_x_label, self.distance_x_entry)
-        form_layout.addRow(self.distance_y_label, self.distance_y_entry)
-        form_layout.addRow(self.angle_label, self.angle_entry)
-        form_layout.addRow(self.total_distance_label, self.total_distance_entry)
-        form_layout.addRow(self.half_point_label, self.half_point_entry)
-
-        self.layout.addStretch()
+        # #############################################################################
+        # ######################### Tool GUI ##########################################
+        # #############################################################################
+        self.ui = DistMinUI(layout=self.layout, app=self.app)
+        self.toolName = self.ui.toolName
 
         self.h_point = (0, 0)
 
-        self.measure_btn.clicked.connect(self.activate_measure_tool)
-        self.jump_hp_btn.clicked.connect(self.on_jump_to_half_point)
+        self.ui.measure_btn.clicked.connect(self.activate_measure_tool)
+        self.ui.jump_hp_btn.clicked.connect(self.on_jump_to_half_point)
 
     def run(self, toggle=False):
         self.app.defaults.report_usage("ToolDistanceMin()")
@@ -169,22 +82,22 @@ class DistanceMin(AppTool):
         self.units = self.app.defaults['units'].lower()
 
         # initial view of the layout
-        self.start_entry.set_value('(0, 0)')
-        self.stop_entry.set_value('(0, 0)')
+        self.ui.start_entry.set_value('(0, 0)')
+        self.ui.stop_entry.set_value('(0, 0)')
 
-        self.distance_x_entry.set_value('0.0')
-        self.distance_y_entry.set_value('0.0')
-        self.angle_entry.set_value('0.0')
-        self.total_distance_entry.set_value('0.0')
-        self.half_point_entry.set_value('(0, 0)')
+        self.ui.distance_x_entry.set_value('0.0')
+        self.ui.distance_y_entry.set_value('0.0')
+        self.ui.angle_entry.set_value('0.0')
+        self.ui.total_distance_entry.set_value('0.0')
+        self.ui.half_point_entry.set_value('(0, 0)')
 
-        self.jump_hp_btn.setDisabled(True)
+        self.ui.jump_hp_btn.setDisabled(True)
 
         log.debug("Minimum Distance Tool --> tool initialized")
 
     def activate_measure_tool(self):
         # ENABLE the Measuring TOOL
-        self.jump_hp_btn.setDisabled(False)
+        self.ui.jump_hp_btn.setDisabled(False)
 
         self.units = self.app.defaults['units'].lower()
 
@@ -193,7 +106,7 @@ class DistanceMin(AppTool):
             if len(selected_objs) != 2:
                 self.app.inform.emit('[WARNING_NOTCL] %s %s' %
                                      (_("Select two objects and no more. Currently the selection has objects: "),
-                                     str(len(selected_objs))))
+                                      str(len(selected_objs))))
                 return
             else:
                 if isinstance(selected_objs[0].solid_geometry, list):
@@ -214,7 +127,7 @@ class DistanceMin(AppTool):
             if len(selected_objs) != 2:
                 self.app.inform.emit('[WARNING_NOTCL] %s %s' %
                                      (_("Select two objects and no more. Currently the selection has objects: "),
-                                     str(len(selected_objs))))
+                                      str(len(selected_objs))))
                 return
             else:
                 first_pos, last_pos = nearest_points(selected_objs[0].geo, selected_objs[1].geo)
@@ -251,31 +164,31 @@ class DistanceMin(AppTool):
         else:
             first_pos, last_pos = 0, 0
 
-        self.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, first_pos.x, self.decimals, first_pos.y))
-        self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, last_pos.x, self.decimals, last_pos.y))
+        self.ui.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, first_pos.x, self.decimals, first_pos.y))
+        self.ui.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, last_pos.x, self.decimals, last_pos.y))
 
         dx = first_pos.x - last_pos.x
         dy = first_pos.y - last_pos.y
 
-        self.distance_x_entry.set_value('%.*f' % (self.decimals, abs(dx)))
-        self.distance_y_entry.set_value('%.*f' % (self.decimals, abs(dy)))
+        self.ui.distance_x_entry.set_value('%.*f' % (self.decimals, abs(dx)))
+        self.ui.distance_y_entry.set_value('%.*f' % (self.decimals, abs(dy)))
 
         try:
             angle = math.degrees(math.atan(dy / dx))
-            self.angle_entry.set_value('%.*f' % (self.decimals, angle))
-        except Exception as e:
+            self.ui.angle_entry.set_value('%.*f' % (self.decimals, angle))
+        except Exception:
             pass
 
         d = math.sqrt(dx ** 2 + dy ** 2)
-        self.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d)))
+        self.ui.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d)))
 
         self.h_point = (min(first_pos.x, last_pos.x) + (abs(dx) / 2), min(first_pos.y, last_pos.y) + (abs(dy) / 2))
         if d != 0:
-            self.half_point_entry.set_value(
+            self.ui.half_point_entry.set_value(
                 "(%.*f, %.*f)" % (self.decimals, self.h_point[0], self.decimals, self.h_point[1])
             )
         else:
-            self.half_point_entry.set_value(
+            self.ui.half_point_entry.set_value(
                 "(%.*f, %.*f)" % (self.decimals, 0.0, self.decimals, 0.0)
             )
 
@@ -299,7 +212,125 @@ class DistanceMin(AppTool):
                              (_("Jumped to the half point between the two selected objects"),
                               "(%.*f, %.*f)" % (self.decimals, self.h_point[0], self.decimals, self.h_point[1])))
 
-    def set_meas_units(self, units):
-        self.meas.units_label.setText("[" + self.app.options["units"].lower() + "]")
+    # def set_meas_units(self, units):
+    #     self.meas.units_label.setText("[" + self.app.options["units"].lower() + "]")
+
 
-# end of file
+class DistMinUI:
+
+    toolName = _("Minimum Distance Tool")
+
+    def __init__(self, layout, app):
+        self.app = app
+        self.decimals = self.app.decimals
+        self.layout = layout
+
+# ## Title
+        title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
+        self.layout.addWidget(title_label)
+
+        # ## Form Layout
+        form_layout = QtWidgets.QFormLayout()
+        self.layout.addLayout(form_layout)
+
+        self.units_label = QtWidgets.QLabel('%s:' % _("Units"))
+        self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
+        self.units_value = QtWidgets.QLabel("%s" % str({'mm': _("METRIC (mm)"), 'in': _("INCH (in)")}[self.units]))
+        self.units_value.setDisabled(True)
+
+        self.start_label = QtWidgets.QLabel("%s:" % _('First object point'))
+        self.start_label.setToolTip(_("This is first object point coordinates.\n"
+                                      "This is the start point for measuring distance."))
+
+        self.stop_label = QtWidgets.QLabel("%s:" % _('Second object point'))
+        self.stop_label.setToolTip(_("This is second object point coordinates.\n"
+                                     "This is the end point for measuring distance."))
+
+        self.distance_x_label = QtWidgets.QLabel('%s:' % _("Dx"))
+        self.distance_x_label.setToolTip(_("This is the distance measured over the X axis."))
+
+        self.distance_y_label = QtWidgets.QLabel('%s:' % _("Dy"))
+        self.distance_y_label.setToolTip(_("This is the distance measured over the Y axis."))
+
+        self.angle_label = QtWidgets.QLabel('%s:' % _("Angle"))
+        self.angle_label.setToolTip(_("This is orientation angle of the measuring line."))
+
+        self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))
+        self.total_distance_label.setToolTip(_("This is the point to point Euclidean distance."))
+
+        self.half_point_label = QtWidgets.QLabel("<b>%s:</b>" % _('Half Point'))
+        self.half_point_label.setToolTip(_("This is the middle point of the point to point Euclidean distance."))
+
+        self.start_entry = FCEntry()
+        self.start_entry.setReadOnly(True)
+        self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.start_entry.setToolTip(_("This is first object point coordinates.\n"
+                                      "This is the start point for measuring distance."))
+
+        self.stop_entry = FCEntry()
+        self.stop_entry.setReadOnly(True)
+        self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.stop_entry.setToolTip(_("This is second object point coordinates.\n"
+                                     "This is the end point for measuring distance."))
+
+        self.distance_x_entry = FCEntry()
+        self.distance_x_entry.setReadOnly(True)
+        self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis."))
+
+        self.distance_y_entry = FCEntry()
+        self.distance_y_entry.setReadOnly(True)
+        self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis."))
+
+        self.angle_entry = FCEntry()
+        self.angle_entry.setReadOnly(True)
+        self.angle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.angle_entry.setToolTip(_("This is orientation angle of the measuring line."))
+
+        self.total_distance_entry = FCEntry()
+        self.total_distance_entry.setReadOnly(True)
+        self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.total_distance_entry.setToolTip(_("This is the point to point Euclidean distance."))
+
+        self.half_point_entry = FCEntry()
+        self.half_point_entry.setReadOnly(True)
+        self.half_point_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.half_point_entry.setToolTip(_("This is the middle point of the point to point Euclidean distance."))
+
+        self.measure_btn = QtWidgets.QPushButton(_("Measure"))
+        self.layout.addWidget(self.measure_btn)
+
+        self.jump_hp_btn = QtWidgets.QPushButton(_("Jump to Half Point"))
+        self.layout.addWidget(self.jump_hp_btn)
+        self.jump_hp_btn.setDisabled(True)
+
+        form_layout.addRow(self.units_label, self.units_value)
+        form_layout.addRow(self.start_label, self.start_entry)
+        form_layout.addRow(self.stop_label, self.stop_entry)
+        form_layout.addRow(self.distance_x_label, self.distance_x_entry)
+        form_layout.addRow(self.distance_y_label, self.distance_y_entry)
+        form_layout.addRow(self.angle_label, self.angle_entry)
+        form_layout.addRow(self.total_distance_label, self.total_distance_entry)
+        form_layout.addRow(self.half_point_label, self.half_point_entry)
+
+        self.layout.addStretch()
+        # #################################### FINSIHED GUI ###########################
+        # #############################################################################
+
+    def confirmation_message(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
+                                                                                  self.decimals,
+                                                                                  minval,
+                                                                                  self.decimals,
+                                                                                  maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+
+    def confirmation_message_int(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                            (_("Edited value is out of range"), minval, maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

+ 1359 - 1325
appTools/ToolRulesCheck.py

@@ -8,7 +8,7 @@
 from PyQt5 import QtWidgets, QtGui
 
 from appTool import AppTool
-from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCComboBox
+from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCComboBox, FCLabel, FCButton
 from copy import deepcopy
 
 from appPool import *
@@ -30,8 +30,6 @@ log = logging.getLogger('base')
 
 class RulesCheck(AppTool):
 
-    toolName = _("Check Rules")
-
     tool_finished = QtCore.pyqtSignal(list)
 
     def __init__(self, app):
@@ -39,1596 +37,1632 @@ class RulesCheck(AppTool):
 
         AppTool.__init__(self, app)
 
-        # ## Title
-        title_label = QtWidgets.QLabel("%s" % self.toolName)
-        title_label.setStyleSheet("""
-                        QLabel
-                        {
-                            font-size: 16px;
-                            font-weight: bold;
-                        }
-                        """)
-        self.layout.addWidget(title_label)
-
-        # Form Layout
-        self.grid_layout = QtWidgets.QGridLayout()
-        self.layout.addLayout(self.grid_layout)
-
-        self.grid_layout.setColumnStretch(0, 0)
-        self.grid_layout.setColumnStretch(1, 3)
-        self.grid_layout.setColumnStretch(2, 0)
+        # #############################################################################
+        # ######################### Tool GUI ##########################################
+        # #############################################################################
+        self.ui = RulesUI(layout=self.layout, app=self.app)
+        self.toolName = self.ui.toolName
 
-        self.gerber_title_lbl = QtWidgets.QLabel('<b>%s</b>:' % _("GERBER"))
-        self.gerber_title_lbl.setToolTip(
-            _("Gerber objects for which to check rules.")
-        )
+        # #######################################################
+        # ################ SIGNALS ##############################
+        # #######################################################
+        self.ui.copper_t_cb.stateChanged.connect(lambda st: self.ui.copper_t_object.setDisabled(not st))
+        self.ui.copper_b_cb.stateChanged.connect(lambda st: self.ui.copper_b_object.setDisabled(not st))
 
-        self.all_obj_cb = FCCheckBox()
+        self.ui.sm_t_cb.stateChanged.connect(lambda st: self.ui.sm_t_object.setDisabled(not st))
+        self.ui.sm_b_cb.stateChanged.connect(lambda st: self.ui.sm_b_object.setDisabled(not st))
 
-        self.grid_layout.addWidget(self.gerber_title_lbl, 0, 0, 1, 2)
-        self.grid_layout.addWidget(self.all_obj_cb, 0, 2)
+        self.ui.ss_t_cb.stateChanged.connect(lambda st: self.ui.ss_t_object.setDisabled(not st))
+        self.ui.ss_b_cb.stateChanged.connect(lambda st: self.ui.ss_b_object.setDisabled(not st))
 
-        # Copper Top object
-        self.copper_t_object = FCComboBox()
-        self.copper_t_object.setModel(self.app.collection)
-        self.copper_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.copper_t_object.is_last = True
-        self.copper_t_object.obj_type = "Gerber"
+        self.ui.out_cb.stateChanged.connect(lambda st: self.ui.outline_object.setDisabled(not st))
 
-        self.copper_t_object_lbl = QtWidgets.QLabel('%s:' % _("Top"))
-        self.copper_t_object_lbl.setToolTip(
-            _("The Top Gerber Copper object for which rules are checked.")
-        )
+        self.ui.e1_cb.stateChanged.connect(lambda st: self.ui.e1_object.setDisabled(not st))
+        self.ui.e2_cb.stateChanged.connect(lambda st: self.ui.e2_object.setDisabled(not st))
 
-        self.copper_t_cb = FCCheckBox()
+        self.ui.all_obj_cb.stateChanged.connect(self.ui.on_all_objects_cb_changed)
+        self.ui.all_cb.stateChanged.connect(self.ui.on_all_cb_changed)
+        self.ui.run_button.clicked.connect(self.execute)
 
-        self.grid_layout.addWidget(self.copper_t_object_lbl, 1, 0)
-        self.grid_layout.addWidget(self.copper_t_object, 1, 1)
-        self.grid_layout.addWidget(self.copper_t_cb, 1, 2)
+        self.ui.reset_button.clicked.connect(self.set_tool_ui)
+        
+        # Custom Signals
+        self.tool_finished.connect(self.on_tool_finished)
 
-        # Copper Bottom object
-        self.copper_b_object = FCComboBox()
-        self.copper_b_object.setModel(self.app.collection)
-        self.copper_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.copper_b_object.is_last = True
-        self.copper_b_object.obj_type = "Gerber"
+        # list to hold the temporary objects
+        self.objs = []
 
-        self.copper_b_object_lbl = QtWidgets.QLabel('%s:' % _("Bottom"))
-        self.copper_b_object_lbl.setToolTip(
-            _("The Bottom Gerber Copper object for which rules are checked.")
-        )
+        # final name for the panel object
+        self.outname = ""
 
-        self.copper_b_cb = FCCheckBox()
+        # flag to signal the constrain was activated
+        self.constrain_flag = False
 
-        self.grid_layout.addWidget(self.copper_b_object_lbl, 2, 0)
-        self.grid_layout.addWidget(self.copper_b_object, 2, 1)
-        self.grid_layout.addWidget(self.copper_b_cb, 2, 2)
+        # Multiprocessing Process Pool
+        self.pool = self.app.pool
+        self.results = None
 
-        # SolderMask Top object
-        self.sm_t_object = FCComboBox()
-        self.sm_t_object.setModel(self.app.collection)
-        self.sm_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.sm_t_object.is_last = True
-        self.sm_t_object.obj_type = "Gerber"
+        self.decimals = 4
 
-        self.sm_t_object_lbl = QtWidgets.QLabel('%s:' % _("SM Top"))
-        self.sm_t_object_lbl.setToolTip(
-            _("The Top Gerber Solder Mask object for which rules are checked.")
-        )
+    # def on_object_loaded(self, index, row):
+    #     print(index.internalPointer().child_items[row].obj.options['name'], index.data())
 
-        self.sm_t_cb = FCCheckBox()
+    def run(self, toggle=True):
+        self.app.defaults.report_usage("ToolRulesCheck()")
 
-        self.grid_layout.addWidget(self.sm_t_object_lbl, 3, 0)
-        self.grid_layout.addWidget(self.sm_t_object, 3, 1)
-        self.grid_layout.addWidget(self.sm_t_cb, 3, 2)
+        if toggle:
+            # if the splitter is hidden, display it, else hide it but only if the current widget is the same
+            if self.app.ui.splitter.sizes()[0] == 0:
+                self.app.ui.splitter.setSizes([1, 1])
+            else:
+                try:
+                    if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
+                        # if tab is populated with the tool but it does not have the focus, focus on it
+                        if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
+                            # focus on Tool Tab
+                            self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
+                        else:
+                            self.app.ui.splitter.setSizes([0, 1])
+                except AttributeError:
+                    pass
+        else:
+            if self.app.ui.splitter.sizes()[0] == 0:
+                self.app.ui.splitter.setSizes([1, 1])
 
-        # SolderMask Bottom object
-        self.sm_b_object = FCComboBox()
-        self.sm_b_object.setModel(self.app.collection)
-        self.sm_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.sm_b_object.is_last = True
-        self.sm_b_object.obj_type = "Gerber"
+        AppTool.run(self)
+        self.set_tool_ui()
 
-        self.sm_b_object_lbl = QtWidgets.QLabel('%s:' % _("SM Bottom"))
-        self.sm_b_object_lbl.setToolTip(
-            _("The Bottom Gerber Solder Mask object for which rules are checked.")
-        )
+        self.app.ui.notebook.setTabText(2, _("Rules Tool"))
 
-        self.sm_b_cb = FCCheckBox()
+    def install(self, icon=None, separator=None, **kwargs):
+        AppTool.install(self, icon, separator, shortcut='Alt+R', **kwargs)
 
-        self.grid_layout.addWidget(self.sm_b_object_lbl, 4, 0)
-        self.grid_layout.addWidget(self.sm_b_object, 4, 1)
-        self.grid_layout.addWidget(self.sm_b_cb, 4, 2)
+    def set_tool_ui(self):
 
-        # SilkScreen Top object
-        self.ss_t_object = FCComboBox()
-        self.ss_t_object.setModel(self.app.collection)
-        self.ss_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.ss_t_object.is_last = True
-        self.ss_t_object.obj_type = "Gerber"
+        # all object combobox default as disabled
+        self.ui.copper_t_object.setDisabled(True)
+        self.ui.copper_b_object.setDisabled(True)
+
+        self.ui.sm_t_object.setDisabled(True)
+        self.ui.sm_b_object.setDisabled(True)
+
+        self.ui.ss_t_object.setDisabled(True)
+        self.ui.ss_b_object.setDisabled(True)
+
+        self.ui.outline_object.setDisabled(True)
+
+        self.ui.e1_object.setDisabled(True)
+        self.ui.e2_object.setDisabled(True)
+
+        self.ui.trace_size_cb.set_value(self.app.defaults["tools_cr_trace_size"])
+        self.ui.trace_size_entry.set_value(float(self.app.defaults["tools_cr_trace_size_val"]))
+        self.ui.clearance_copper2copper_cb.set_value(self.app.defaults["tools_cr_c2c"])
+        self.ui.clearance_copper2copper_entry.set_value(float(self.app.defaults["tools_cr_c2c_val"]))
+        self.ui.clearance_copper2ol_cb.set_value(self.app.defaults["tools_cr_c2o"])
+        self.ui.clearance_copper2ol_entry.set_value(float(self.app.defaults["tools_cr_c2o_val"]))
+        self.ui.clearance_silk2silk_cb.set_value(self.app.defaults["tools_cr_s2s"])
+        self.ui.clearance_silk2silk_entry.set_value(float(self.app.defaults["tools_cr_s2s_val"]))
+        self.ui.clearance_silk2sm_cb.set_value(self.app.defaults["tools_cr_s2sm"])
+        self.ui.clearance_silk2sm_entry.set_value(float(self.app.defaults["tools_cr_s2sm_val"]))
+        self.ui.clearance_silk2ol_cb.set_value(self.app.defaults["tools_cr_s2o"])
+        self.ui.clearance_silk2ol_entry.set_value(float(self.app.defaults["tools_cr_s2o_val"]))
+        self.ui.clearance_sm2sm_cb.set_value(self.app.defaults["tools_cr_sm2sm"])
+        self.ui.clearance_sm2sm_entry.set_value(float(self.app.defaults["tools_cr_sm2sm_val"]))
+        self.ui.ring_integrity_cb.set_value(self.app.defaults["tools_cr_ri"])
+        self.ui.ring_integrity_entry.set_value(float(self.app.defaults["tools_cr_ri_val"]))
+        self.ui.clearance_d2d_cb.set_value(self.app.defaults["tools_cr_h2h"])
+        self.ui.clearance_d2d_entry.set_value(float(self.app.defaults["tools_cr_h2h_val"]))
+        self.ui.drill_size_cb.set_value(self.app.defaults["tools_cr_dh"])
+        self.ui.drill_size_entry.set_value(float(self.app.defaults["tools_cr_dh_val"]))
 
-        self.ss_t_object_lbl = QtWidgets.QLabel('%s:' % _("Silk Top"))
-        self.ss_t_object_lbl.setToolTip(
-            _("The Top Gerber Silkscreen object for which rules are checked.")
-        )
+        self.reset_fields()
 
-        self.ss_t_cb = FCCheckBox()
+    @staticmethod
+    def check_inside_gerber_clearance(gerber_obj, size, rule):
+        log.debug("RulesCheck.check_inside_gerber_clearance()")
 
-        self.grid_layout.addWidget(self.ss_t_object_lbl, 5, 0)
-        self.grid_layout.addWidget(self.ss_t_object, 5, 1)
-        self.grid_layout.addWidget(self.ss_t_cb, 5, 2)
+        rule_title = rule
 
-        # SilkScreen Bottom object
-        self.ss_b_object = FCComboBox()
-        self.ss_b_object.setModel(self.app.collection)
-        self.ss_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.ss_b_object.is_last = True
-        self.ss_b_object.obj_type = "Gerber"
+        violations = []
+        obj_violations = {}
+        obj_violations.update({
+            'name': '',
+            'points': list()
+        })
 
-        self.ss_b_object_lbl = QtWidgets.QLabel('%s:' % _("Silk Bottom"))
-        self.ss_b_object_lbl.setToolTip(
-            _("The Bottom Gerber Silkscreen object for which rules are checked.")
-        )
+        if not gerber_obj:
+            return 'Fail. Not enough Gerber objects to check Gerber 2 Gerber clearance'
 
-        self.ss_b_cb = FCCheckBox()
+        obj_violations['name'] = gerber_obj['name']
 
-        self.grid_layout.addWidget(self.ss_b_object_lbl, 6, 0)
-        self.grid_layout.addWidget(self.ss_b_object, 6, 1)
-        self.grid_layout.addWidget(self.ss_b_cb, 6, 2)
+        solid_geo = []
+        clear_geo = []
+        for apid in gerber_obj['apertures']:
+            if 'geometry' in gerber_obj['apertures'][apid]:
+                geometry = gerber_obj['apertures'][apid]['geometry']
+                for geo_el in geometry:
+                    if 'solid' in geo_el and geo_el['solid'] is not None:
+                        solid_geo.append(geo_el['solid'])
+                    if 'clear' in geo_el and geo_el['clear'] is not None:
+                        clear_geo.append(geo_el['clear'])
 
-        # Outline object
-        self.outline_object = FCComboBox()
-        self.outline_object.setModel(self.app.collection)
-        self.outline_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.outline_object.is_last = True
-        self.outline_object.obj_type = "Gerber"
+        if clear_geo:
+            total_geo = []
+            for geo_c in clear_geo:
+                for geo_s in solid_geo:
+                    if geo_c.within(geo_s):
+                        total_geo.append(geo_s.difference(geo_c))
+        else:
+            total_geo = MultiPolygon(solid_geo)
+            total_geo = total_geo.buffer(0.000001)
 
-        self.outline_object_lbl = QtWidgets.QLabel('%s:' % _("Outline"))
-        self.outline_object_lbl.setToolTip(
-            _("The Gerber Outline (Cutout) object for which rules are checked.")
-        )
+        if isinstance(total_geo, Polygon):
+            obj_violations['points'] = ['Failed. Only one polygon.']
+            return rule_title, [obj_violations]
+        else:
+            iterations = len(total_geo)
+            iterations = (iterations * (iterations - 1)) / 2
+        log.debug("RulesCheck.check_gerber_clearance(). Iterations: %s" % str(iterations))
 
-        self.out_cb = FCCheckBox()
+        min_dict = {}
+        idx = 1
+        for geo in total_geo:
+            for s_geo in total_geo[idx:]:
+                # minimize the number of distances by not taking into considerations those that are too small
+                dist = geo.distance(s_geo)
+                if float(dist) < float(size):
+                    loc_1, loc_2 = nearest_points(geo, s_geo)
 
-        self.grid_layout.addWidget(self.outline_object_lbl, 7, 0)
-        self.grid_layout.addWidget(self.outline_object, 7, 1)
-        self.grid_layout.addWidget(self.out_cb, 7, 2)
+                    dx = loc_1.x - loc_2.x
+                    dy = loc_1.y - loc_2.y
+                    loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
 
-        self.grid_layout.addWidget(QtWidgets.QLabel(""), 8, 0, 1, 3)
+                    if dist in min_dict:
+                        min_dict[dist].append(loc)
+                    else:
+                        min_dict[dist] = [loc]
+            idx += 1
+        points_list = set()
+        for dist in min_dict.keys():
+            for location in min_dict[dist]:
+                points_list.add(location)
 
-        self.excellon_title_lbl = QtWidgets.QLabel('<b>%s</b>:' % _("EXCELLON"))
-        self.excellon_title_lbl.setToolTip(
-            _("Excellon objects for which to check rules.")
-        )
+        obj_violations['points'] = list(points_list)
+        violations.append(deepcopy(obj_violations))
 
-        self.grid_layout.addWidget(self.excellon_title_lbl, 9, 0, 1, 3)
+        return rule_title, violations
 
-        # Excellon 1 object
-        self.e1_object = FCComboBox()
-        self.e1_object.setModel(self.app.collection)
-        self.e1_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
-        self.e1_object.is_last = True
-        self.e1_object.obj_type = "Excellon"
+    @staticmethod
+    def check_gerber_clearance(gerber_list, size, rule):
+        log.debug("RulesCheck.check_gerber_clearance()")
+        rule_title = rule
 
-        self.e1_object_lbl = QtWidgets.QLabel('%s:' % _("Excellon 1"))
-        self.e1_object_lbl.setToolTip(
-            _("Excellon object for which to check rules.\n"
-              "Holds the plated holes or a general Excellon file content.")
-        )
+        violations = []
+        obj_violations = {}
+        obj_violations.update({
+            'name': '',
+            'points': list()
+        })
 
-        self.e1_cb = FCCheckBox()
+        if len(gerber_list) == 2:
+            gerber_1 = gerber_list[0]
+            # added it so I won't have errors of using before declaring
+            gerber_2 = {}
 
-        self.grid_layout.addWidget(self.e1_object_lbl, 10, 0)
-        self.grid_layout.addWidget(self.e1_object, 10, 1)
-        self.grid_layout.addWidget(self.e1_cb, 10, 2)
+            gerber_3 = gerber_list[1]
+        elif len(gerber_list) == 3:
+            gerber_1 = gerber_list[0]
+            gerber_2 = gerber_list[1]
+            gerber_3 = gerber_list[2]
+        else:
+            return 'Fail. Not enough Gerber objects to check Gerber 2 Gerber clearance'
 
-        # Excellon 2 object
-        self.e2_object = FCComboBox()
-        self.e2_object.setModel(self.app.collection)
-        self.e2_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
-        self.e2_object.is_last = True
-        self.e2_object.obj_type = "Excellon"
+        total_geo_grb_1 = []
+        for apid in gerber_1['apertures']:
+            if 'geometry' in gerber_1['apertures'][apid]:
+                geometry = gerber_1['apertures'][apid]['geometry']
+                for geo_el in geometry:
+                    if 'solid' in geo_el and geo_el['solid'] is not None:
+                        total_geo_grb_1.append(geo_el['solid'])
 
-        self.e2_object_lbl = QtWidgets.QLabel('%s:' % _("Excellon 2"))
-        self.e2_object_lbl.setToolTip(
-            _("Excellon object for which to check rules.\n"
-              "Holds the non-plated holes.")
-        )
+        if len(gerber_list) == 3:
+            # add the second Gerber geometry to the first one if it exists
+            for apid in gerber_2['apertures']:
+                if 'geometry' in gerber_2['apertures'][apid]:
+                    geometry = gerber_2['apertures'][apid]['geometry']
+                    for geo_el in geometry:
+                        if 'solid' in geo_el and geo_el['solid'] is not None:
+                            total_geo_grb_1.append(geo_el['solid'])
 
-        self.e2_cb = FCCheckBox()
+        total_geo_grb_3 = []
+        for apid in gerber_3['apertures']:
+            if 'geometry' in gerber_3['apertures'][apid]:
+                geometry = gerber_3['apertures'][apid]['geometry']
+                for geo_el in geometry:
+                    if 'solid' in geo_el and geo_el['solid'] is not None:
+                        total_geo_grb_3.append(geo_el['solid'])
 
-        self.grid_layout.addWidget(self.e2_object_lbl, 11, 0)
-        self.grid_layout.addWidget(self.e2_object, 11, 1)
-        self.grid_layout.addWidget(self.e2_cb, 11, 2)
+        total_geo_grb_1 = MultiPolygon(total_geo_grb_1)
+        total_geo_grb_1 = total_geo_grb_1.buffer(0)
 
-        self.grid_layout.addWidget(QtWidgets.QLabel(""), 12, 0, 1, 3)
+        total_geo_grb_3 = MultiPolygon(total_geo_grb_3)
+        total_geo_grb_3 = total_geo_grb_3.buffer(0)
 
-        # Control All
-        self.all_cb = FCCheckBox('%s' % _("All Rules"))
-        self.all_cb.setToolTip(
-            _("This check/uncheck all the rules below.")
-        )
-        self.all_cb.setStyleSheet(
-            """
-            QCheckBox {font-weight: bold; color: green}
-            """
-        )
-        self.layout.addWidget(self.all_cb)
+        if isinstance(total_geo_grb_1, Polygon):
+            len_1 = 1
+            total_geo_grb_1 = [total_geo_grb_1]
+        else:
+            len_1 = len(total_geo_grb_1)
 
-        # Form Layout
-        self.form_layout_1 = QtWidgets.QFormLayout()
-        self.layout.addLayout(self.form_layout_1)
+        if isinstance(total_geo_grb_3, Polygon):
+            len_3 = 1
+            total_geo_grb_3 = [total_geo_grb_3]
+        else:
+            len_3 = len(total_geo_grb_3)
 
-        self.form_layout_1.addRow(QtWidgets.QLabel(""))
+        iterations = len_1 * len_3
+        log.debug("RulesCheck.check_gerber_clearance(). Iterations: %s" % str(iterations))
 
-        # Trace size
-        self.trace_size_cb = FCCheckBox('%s:' % _("Trace Size"))
-        self.trace_size_cb.setToolTip(
-            _("This checks if the minimum size for traces is met.")
-        )
-        self.form_layout_1.addRow(self.trace_size_cb)
+        min_dict = {}
+        for geo in total_geo_grb_1:
+            for s_geo in total_geo_grb_3:
+                # minimize the number of distances by not taking into considerations those that are too small
+                dist = geo.distance(s_geo)
+                if float(dist) < float(size):
+                    loc_1, loc_2 = nearest_points(geo, s_geo)
 
-        # Trace size value
-        self.trace_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.trace_size_entry.set_range(0.00001, 999.99999)
-        self.trace_size_entry.set_precision(self.decimals)
-        self.trace_size_entry.setSingleStep(0.1)
+                    dx = loc_1.x - loc_2.x
+                    dy = loc_1.y - loc_2.y
+                    loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
 
-        self.trace_size_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.trace_size_lbl.setToolTip(
-            _("Minimum acceptable trace size.")
-        )
-        self.form_layout_1.addRow(self.trace_size_lbl, self.trace_size_entry)
+                    if dist in min_dict:
+                        min_dict[dist].append(loc)
+                    else:
+                        min_dict[dist] = [loc]
 
-        self.ts = OptionalInputSection(self.trace_size_cb, [self.trace_size_lbl, self.trace_size_entry])
+        points_list = set()
+        for dist in min_dict.keys():
+            for location in min_dict[dist]:
+                points_list.add(location)
 
-        # Copper2copper clearance
-        self.clearance_copper2copper_cb = FCCheckBox('%s:' % _("Copper to Copper clearance"))
-        self.clearance_copper2copper_cb.setToolTip(
-            _("This checks if the minimum clearance between copper\n"
-              "features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2copper_cb)
+        name_list = []
+        if gerber_1:
+            name_list.append(gerber_1['name'])
+        if gerber_2:
+            name_list.append(gerber_2['name'])
+        if gerber_3:
+            name_list.append(gerber_3['name'])
 
-        # Copper2copper clearance value
-        self.clearance_copper2copper_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.clearance_copper2copper_entry.set_range(0.00001, 999.99999)
-        self.clearance_copper2copper_entry.set_precision(self.decimals)
-        self.clearance_copper2copper_entry.setSingleStep(0.1)
+        obj_violations['name'] = name_list
+        obj_violations['points'] = list(points_list)
+        violations.append(deepcopy(obj_violations))
 
-        self.clearance_copper2copper_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_copper2copper_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry)
+        return rule_title, violations
 
-        self.c2c = OptionalInputSection(
-            self.clearance_copper2copper_cb, [self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry])
+    @staticmethod
+    def check_holes_size(elements, size):
+        log.debug("RulesCheck.check_holes_size()")
 
-        # Copper2outline clearance
-        self.clearance_copper2ol_cb = FCCheckBox('%s:' % _("Copper to Outline clearance"))
-        self.clearance_copper2ol_cb.setToolTip(
-            _("This checks if the minimum clearance between copper\n"
-              "features and the outline is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2ol_cb)
+        rule = _("Hole Size")
 
-        # Copper2outline clearance value
-        self.clearance_copper2ol_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.clearance_copper2ol_entry.set_range(0.00001, 999.99999)
-        self.clearance_copper2ol_entry.set_precision(self.decimals)
-        self.clearance_copper2ol_entry.setSingleStep(0.1)
+        violations = []
+        obj_violations = {}
+        obj_violations.update({
+            'name': '',
+            'dia': list()
+        })
 
-        self.clearance_copper2ol_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_copper2ol_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry)
+        for elem in elements:
+            dia_list = []
 
-        self.c2ol = OptionalInputSection(
-            self.clearance_copper2ol_cb, [self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry])
+            name = elem['name']
+            for tool in elem['tools']:
+                tool_dia = float('%.*f' % (4, float(elem['tools'][tool]['C'])))
+                if tool_dia < float(size):
+                    dia_list.append(tool_dia)
+            obj_violations['name'] = name
+            obj_violations['dia'] = dia_list
+            violations.append(deepcopy(obj_violations))
 
-        # Silkscreen2silkscreen clearance
-        self.clearance_silk2silk_cb = FCCheckBox('%s:' % _("Silk to Silk Clearance"))
-        self.clearance_silk2silk_cb.setToolTip(
-            _("This checks if the minimum clearance between silkscreen\n"
-              "features and silkscreen features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2silk_cb)
+        return rule, violations
 
-        # Copper2silkscreen clearance value
-        self.clearance_silk2silk_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.clearance_silk2silk_entry.set_range(0.00001, 999.99999)
-        self.clearance_silk2silk_entry.set_precision(self.decimals)
-        self.clearance_silk2silk_entry.setSingleStep(0.1)
+    @staticmethod
+    def check_holes_clearance(elements, size):
+        log.debug("RulesCheck.check_holes_clearance()")
+        rule = _("Hole to Hole Clearance")
 
-        self.clearance_silk2silk_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_silk2silk_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry)
+        violations = []
+        obj_violations = {}
+        obj_violations.update({
+            'name': '',
+            'points': list()
+        })
 
-        self.s2s = OptionalInputSection(
-            self.clearance_silk2silk_cb, [self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry])
+        total_geo = []
+        for elem in elements:
+            for tool in elem['tools']:
+                if 'solid_geometry' in elem['tools'][tool]:
+                    geometry = elem['tools'][tool]['solid_geometry']
+                    for geo in geometry:
+                        total_geo.append(geo)
 
-        # Silkscreen2soldermask clearance
-        self.clearance_silk2sm_cb = FCCheckBox('%s:' % _("Silk to Solder Mask Clearance"))
-        self.clearance_silk2sm_cb.setToolTip(
-            _("This checks if the minimum clearance between silkscreen\n"
-              "features and soldermask features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2sm_cb)
+        min_dict = {}
+        idx = 1
+        for geo in total_geo:
+            for s_geo in total_geo[idx:]:
 
-        # Silkscreen2soldermask clearance value
-        self.clearance_silk2sm_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.clearance_silk2sm_entry.set_range(0.00001, 999.99999)
-        self.clearance_silk2sm_entry.set_precision(self.decimals)
-        self.clearance_silk2sm_entry.setSingleStep(0.1)
+                # minimize the number of distances by not taking into considerations those that are too small
+                dist = geo.distance(s_geo)
+                loc_1, loc_2 = nearest_points(geo, s_geo)
 
-        self.clearance_silk2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_silk2sm_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry)
+                dx = loc_1.x - loc_2.x
+                dy = loc_1.y - loc_2.y
+                loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
 
-        self.s2sm = OptionalInputSection(
-            self.clearance_silk2sm_cb, [self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry])
+                if dist in min_dict:
+                    min_dict[dist].append(loc)
+                else:
+                    min_dict[dist] = [loc]
+            idx += 1
 
-        # Silk2outline clearance
-        self.clearance_silk2ol_cb = FCCheckBox('%s:' % _("Silk to Outline Clearance"))
-        self.clearance_silk2ol_cb.setToolTip(
-            _("This checks if the minimum clearance between silk\n"
-              "features and the outline is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2ol_cb)
+        points_list = set()
+        for dist in min_dict.keys():
+            if float(dist) < size:
+                for location in min_dict[dist]:
+                    points_list.add(location)
 
-        # Silk2outline clearance value
-        self.clearance_silk2ol_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.clearance_silk2ol_entry.set_range(0.00001, 999.99999)
-        self.clearance_silk2ol_entry.set_precision(self.decimals)
-        self.clearance_silk2ol_entry.setSingleStep(0.1)
+        name_list = []
+        for elem in elements:
+            name_list.append(elem['name'])
 
-        self.clearance_silk2ol_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_silk2ol_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2ol_lbl, self.clearance_silk2ol_entry)
+        obj_violations['name'] = name_list
+        obj_violations['points'] = list(points_list)
+        violations.append(deepcopy(obj_violations))
 
-        self.s2ol = OptionalInputSection(
-            self.clearance_silk2ol_cb, [self.clearance_silk2ol_lbl, self.clearance_silk2ol_entry])
+        return rule, violations
 
-        # Soldermask2soldermask clearance
-        self.clearance_sm2sm_cb = FCCheckBox('%s:' % _("Minimum Solder Mask Sliver"))
-        self.clearance_sm2sm_cb.setToolTip(
-            _("This checks if the minimum clearance between soldermask\n"
-              "features and soldermask features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_sm2sm_cb)
+    @staticmethod
+    def check_traces_size(elements, size):
+        log.debug("RulesCheck.check_traces_size()")
 
-        # Soldermask2soldermask clearance value
-        self.clearance_sm2sm_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.clearance_sm2sm_entry.set_range(0.00001, 999.99999)
-        self.clearance_sm2sm_entry.set_precision(self.decimals)
-        self.clearance_sm2sm_entry.setSingleStep(0.1)
+        rule = _("Trace Size")
 
-        self.clearance_sm2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_sm2sm_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry)
+        violations = []
+        obj_violations = {}
+        obj_violations.update({
+            'name': '',
+            'size': list(),
+            'points': list()
+        })
 
-        self.sm2sm = OptionalInputSection(
-            self.clearance_sm2sm_cb, [self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry])
+        for elem in elements:
+            dia_list = []
+            points_list = []
+            name = elem['name']
 
-        # Ring integrity check
-        self.ring_integrity_cb = FCCheckBox('%s:' % _("Minimum Annular Ring"))
-        self.ring_integrity_cb.setToolTip(
-            _("This checks if the minimum copper ring left by drilling\n"
-              "a hole into a pad is met.")
-        )
-        self.form_layout_1.addRow(self.ring_integrity_cb)
+            for apid in elem['apertures']:
+                try:
+                    tool_dia = float(elem['apertures'][apid]['size'])
+                    if tool_dia < float(size) and tool_dia != 0.0:
+                        dia_list.append(tool_dia)
+                        for geo_el in elem['apertures'][apid]['geometry']:
+                            if 'solid' in geo_el.keys():
+                                geo = geo_el['solid']
+                                pt = geo.representative_point()
+                                points_list.append((pt.x, pt.y))
+                except Exception:
+                    # An exception  will be raised for the 'size' key in case of apertures of type AM (macro) which does
+                    # not have the size key
+                    pass
 
-        # Ring integrity value
-        self.ring_integrity_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.ring_integrity_entry.set_range(0.00001, 999.99999)
-        self.ring_integrity_entry.set_precision(self.decimals)
-        self.ring_integrity_entry.setSingleStep(0.1)
+            obj_violations['name'] = name
+            obj_violations['size'] = dia_list
+            obj_violations['points'] = points_list
+            violations.append(deepcopy(obj_violations))
+        return rule, violations
 
-        self.ring_integrity_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.ring_integrity_lbl.setToolTip(
-            _("Minimum acceptable ring value.")
-        )
-        self.form_layout_1.addRow(self.ring_integrity_lbl, self.ring_integrity_entry)
+    @staticmethod
+    def check_gerber_annular_ring(obj_list, size, rule):
+        rule_title = rule
 
-        self.anr = OptionalInputSection(
-            self.ring_integrity_cb, [self.ring_integrity_lbl, self.ring_integrity_entry])
+        violations = []
+        obj_violations = {}
+        obj_violations.update({
+            'name': '',
+            'points': list()
+        })
 
-        self.form_layout_1.addRow(QtWidgets.QLabel(""))
+        # added it so I won't have errors of using before declaring
+        gerber_obj = {}
+        gerber_extra_obj = {}
+        exc_obj = {}
+        exc_extra_obj = {}
 
-        # Hole2Hole clearance
-        self.clearance_d2d_cb = FCCheckBox('%s:' % _("Hole to Hole Clearance"))
-        self.clearance_d2d_cb.setToolTip(
-            _("This checks if the minimum clearance between a drill hole\n"
-              "and another drill hole is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_d2d_cb)
+        if len(obj_list) == 2:
+            gerber_obj = obj_list[0]
+            exc_obj = obj_list[1]
+            if 'apertures' in gerber_obj and 'tools' in exc_obj:
+                pass
+            else:
+                return 'Fail. At least one Gerber and one Excellon object is required to check Minimum Annular Ring'
+        elif len(obj_list) == 3:
+            o1 = obj_list[0]
+            o2 = obj_list[1]
+            o3 = obj_list[2]
+            if 'apertures' in o1 and 'apertures' in o2:
+                gerber_obj = o1
+                gerber_extra_obj = o2
+                exc_obj = o3
+            elif 'tools' in o2 and 'tools' in o3:
+                gerber_obj = o1
+                exc_obj = o2
+                exc_extra_obj = o3
+        elif len(obj_list) == 4:
+            gerber_obj = obj_list[0]
+            gerber_extra_obj = obj_list[1]
+            exc_obj = obj_list[2]
+            exc_extra_obj = obj_list[3]
+        else:
+            return 'Fail. Not enough objects to check Minimum Annular Ring'
 
-        # Hole2Hole clearance value
-        self.clearance_d2d_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.clearance_d2d_entry.set_range(0.00001, 999.99999)
-        self.clearance_d2d_entry.set_precision(self.decimals)
-        self.clearance_d2d_entry.setSingleStep(0.1)
+        total_geo_grb = []
+        for apid in gerber_obj['apertures']:
+            if 'geometry' in gerber_obj['apertures'][apid]:
+                geometry = gerber_obj['apertures'][apid]['geometry']
+                for geo_el in geometry:
+                    if 'solid' in geo_el and geo_el['solid'] is not None:
+                        total_geo_grb.append(geo_el['solid'])
 
-        self.clearance_d2d_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_d2d_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_d2d_lbl, self.clearance_d2d_entry)
+        if len(obj_list) == 3 and gerber_extra_obj:
+            # add the second Gerber geometry to the first one if it exists
+            for apid in gerber_extra_obj['apertures']:
+                if 'geometry' in gerber_extra_obj['apertures'][apid]:
+                    geometry = gerber_extra_obj['apertures'][apid]['geometry']
+                    for geo_el in geometry:
+                        if 'solid' in geo_el and geo_el['solid'] is not None:
+                            total_geo_grb.append(geo_el['solid'])
 
-        self.d2d = OptionalInputSection(
-            self.clearance_d2d_cb, [self.clearance_d2d_lbl, self.clearance_d2d_entry])
+        total_geo_grb = MultiPolygon(total_geo_grb)
+        total_geo_grb = total_geo_grb.buffer(0)
 
-        # Drill holes size check
-        self.drill_size_cb = FCCheckBox('%s:' % _("Hole Size"))
-        self.drill_size_cb.setToolTip(
-            _("This checks if the drill holes\n"
-              "sizes are above the threshold.")
-        )
-        self.form_layout_1.addRow(self.drill_size_cb)
+        total_geo_exc = []
+        for tool in exc_obj['tools']:
+            if 'solid_geometry' in exc_obj['tools'][tool]:
+                geometry = exc_obj['tools'][tool]['solid_geometry']
+                for geo in geometry:
+                    total_geo_exc.append(geo)
 
-        # Drile holes value
-        self.drill_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.drill_size_entry.set_range(0.00001, 999.99999)
-        self.drill_size_entry.set_precision(self.decimals)
-        self.drill_size_entry.setSingleStep(0.1)
+        if len(obj_list) == 3 and exc_extra_obj:
+            # add the second Excellon geometry to the first one if it exists
+            for tool in exc_extra_obj['tools']:
+                if 'solid_geometry' in exc_extra_obj['tools'][tool]:
+                    geometry = exc_extra_obj['tools'][tool]['solid_geometry']
+                    for geo in geometry:
+                        total_geo_exc.append(geo)
 
-        self.drill_size_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.drill_size_lbl.setToolTip(
-            _("Minimum acceptable drill size.")
-        )
-        self.form_layout_1.addRow(self.drill_size_lbl, self.drill_size_entry)
+        if isinstance(total_geo_grb, Polygon):
+            len_1 = 1
+            total_geo_grb = [total_geo_grb]
+        else:
+            len_1 = len(total_geo_grb)
 
-        self.ds = OptionalInputSection(
-            self.drill_size_cb, [self.drill_size_lbl, self.drill_size_entry])
+        if isinstance(total_geo_exc, Polygon):
+            len_2 = 1
+            total_geo_exc = [total_geo_exc]
+        else:
+            len_2 = len(total_geo_exc)
 
-        # Buttons
-        hlay_2 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay_2)
+        iterations = len_1 * len_2
+        log.debug("RulesCheck.check_gerber_annular_ring(). Iterations: %s" % str(iterations))
 
-        # hlay_2.addStretch()
-        self.run_button = QtWidgets.QPushButton(_("Run Rules Check"))
-        self.run_button.setToolTip(
-            _("Panelize the specified object around the specified box.\n"
-              "In other words it creates multiple copies of the source object,\n"
-              "arranged in a 2D array of rows and columns.")
-        )
-        self.run_button.setStyleSheet("""
-                        QPushButton
-                        {
-                            font-weight: bold;
-                        }
-                        """)
-        hlay_2.addWidget(self.run_button)
+        min_dict = {}
+        dist = None
+        for geo in total_geo_grb:
+            for s_geo in total_geo_exc:
+                try:
+                    # minimize the number of distances by not taking into considerations those that are too small
+                    dist = abs(geo.exterior.distance(s_geo))
+                except Exception as e:
+                    log.debug("RulesCheck.check_gerber_annular_ring() --> %s" % str(e))
 
-        self.layout.addStretch()
+                if dist > 0:
+                    if float(dist) < float(size):
+                        loc_1, loc_2 = nearest_points(geo.exterior, s_geo)
 
-        # ## Reset Tool
-        self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
-        self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
-        self.reset_button.setToolTip(
-            _("Will reset the tool parameters.")
-        )
-        self.reset_button.setStyleSheet("""
-                        QPushButton
-                        {
-                            font-weight: bold;
-                        }
-                        """)
-        self.layout.addWidget(self.reset_button)
+                        dx = loc_1.x - loc_2.x
+                        dy = loc_1.y - loc_2.y
+                        loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
 
-        # #######################################################
-        # ################ SIGNALS ##############################
-        # #######################################################
-        self.copper_t_cb.stateChanged.connect(lambda st: self.copper_t_object.setDisabled(not st))
-        self.copper_b_cb.stateChanged.connect(lambda st: self.copper_b_object.setDisabled(not st))
+                        if dist in min_dict:
+                            min_dict[dist].append(loc)
+                        else:
+                            min_dict[dist] = [loc]
+                else:
+                    if dist in min_dict:
+                        min_dict[dist].append(s_geo.representative_point())
+                    else:
+                        min_dict[dist] = [s_geo.representative_point()]
 
-        self.sm_t_cb.stateChanged.connect(lambda st: self.sm_t_object.setDisabled(not st))
-        self.sm_b_cb.stateChanged.connect(lambda st: self.sm_b_object.setDisabled(not st))
+        points_list = []
+        for dist in min_dict.keys():
+            for location in min_dict[dist]:
+                points_list.append(location)
 
-        self.ss_t_cb.stateChanged.connect(lambda st: self.ss_t_object.setDisabled(not st))
-        self.ss_b_cb.stateChanged.connect(lambda st: self.ss_b_object.setDisabled(not st))
+        name_list = []
+        try:
+            if gerber_obj:
+                name_list.append(gerber_obj['name'])
+        except KeyError:
+            pass
+        try:
+            if gerber_extra_obj:
+                name_list.append(gerber_extra_obj['name'])
+        except KeyError:
+            pass
+
+        try:
+            if exc_obj:
+                name_list.append(exc_obj['name'])
+        except KeyError:
+            pass
 
-        self.out_cb.stateChanged.connect(lambda st: self.outline_object.setDisabled(not st))
+        try:
+            if exc_extra_obj:
+                name_list.append(exc_extra_obj['name'])
+        except KeyError:
+            pass
 
-        self.e1_cb.stateChanged.connect(lambda st: self.e1_object.setDisabled(not st))
-        self.e2_cb.stateChanged.connect(lambda st: self.e2_object.setDisabled(not st))
+        obj_violations['name'] = name_list
+        obj_violations['points'] = points_list
+        violations.append(deepcopy(obj_violations))
+        return rule_title, violations
 
-        self.all_obj_cb.stateChanged.connect(self.on_all_objects_cb_changed)
-        self.all_cb.stateChanged.connect(self.on_all_cb_changed)
-        self.run_button.clicked.connect(self.execute)
-        # self.app.collection.rowsInserted.connect(self.on_object_loaded)
+    def execute(self):
+        self.results = []
 
-        self.tool_finished.connect(self.on_tool_finished)
-        self.reset_button.clicked.connect(self.set_tool_ui)
+        log.debug("RuleCheck() executing")
 
-        # list to hold the temporary objects
-        self.objs = []
+        def worker_job(app_obj):
+            self.app.proc_container.new(_("Working..."))
 
-        # final name for the panel object
-        self.outname = ""
+            # RULE: Check Trace Size
+            if self.ui.trace_size_cb.get_value():
+                copper_list = []
+                copper_name_1 = self.ui.copper_t_object.currentText()
+                if copper_name_1 != '' and self.ui.copper_t_cb.get_value():
+                    elem_dict = {}
+                    elem_dict['name'] = deepcopy(copper_name_1)
+                    elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_1).apertures)
+                    copper_list.append(elem_dict)
 
-        # flag to signal the constrain was activated
-        self.constrain_flag = False
+                copper_name_2 = self.ui.copper_b_object.currentText()
+                if copper_name_2 != '' and self.ui.copper_b_cb.get_value():
+                    elem_dict = {}
+                    elem_dict['name'] = deepcopy(copper_name_2)
+                    elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_2).apertures)
+                    copper_list.append(elem_dict)
 
-        # Multiprocessing Process Pool
-        self.pool = self.app.pool
-        self.results = None
+                trace_size = float(self.ui.trace_size_entry.get_value())
+                self.results.append(self.pool.apply_async(self.check_traces_size, args=(copper_list, trace_size)))
 
-        self.decimals = 4
+            # RULE: Check Copper to Copper Clearance
+            if self.ui.clearance_copper2copper_cb.get_value():
 
-    # def on_object_loaded(self, index, row):
-    #     print(index.internalPointer().child_items[row].obj.options['name'], index.data())
+                try:
+                    copper_copper_clearance = float(self.ui.clearance_copper2copper_entry.get_value())
+                except Exception as e:
+                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Copper to Copper clearance"),
+                        _("Value is not valid.")))
+                    return
 
-    def on_all_cb_changed(self, state):
-        cb_items = [self.form_layout_1.itemAt(i).widget() for i in range(self.form_layout_1.count())
-                    if isinstance(self.form_layout_1.itemAt(i).widget(), FCCheckBox)]
+                if self.copper_t_cb.get_value():
+                    copper_t_obj = self.ui.copper_t_object.currentText()
+                    copper_t_dict = {}
 
-        for cb in cb_items:
-            if state:
-                cb.setChecked(True)
-            else:
-                cb.setChecked(False)
+                    if copper_t_obj != '':
+                        copper_t_dict['name'] = deepcopy(copper_t_obj)
+                        copper_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_t_obj).apertures)
 
-    def on_all_objects_cb_changed(self, state):
-        cb_items = [self.grid_layout.itemAt(i).widget() for i in range(self.grid_layout.count())
-                    if isinstance(self.grid_layout.itemAt(i).widget(), FCCheckBox)]
+                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
+                                                                  args=(copper_t_dict,
+                                                                        copper_copper_clearance,
+                                                                        _("TOP -> Copper to Copper clearance"))))
+                if self.ui.copper_b_cb.get_value():
+                    copper_b_obj = self.ui.copper_b_object.currentText()
+                    copper_b_dict = {}
+                    if copper_b_obj != '':
+                        copper_b_dict['name'] = deepcopy(copper_b_obj)
+                        copper_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_b_obj).apertures)
 
-        for cb in cb_items:
-            if state:
-                cb.setChecked(True)
-            else:
-                cb.setChecked(False)
+                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
+                                                                  args=(copper_b_dict,
+                                                                        copper_copper_clearance,
+                                                                        _("BOTTOM -> Copper to Copper clearance"))))
 
-    def run(self, toggle=True):
-        self.app.defaults.report_usage("ToolRulesCheck()")
+                if self.ui.copper_t_cb.get_value() is False and self.ui.copper_b_cb.get_value() is False:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Copper to Copper clearance"),
+                        _("At least one Gerber object has to be selected for this rule but none is selected.")))
+                    return
 
-        if toggle:
-            # if the splitter is hidden, display it, else hide it but only if the current widget is the same
-            if self.app.ui.splitter.sizes()[0] == 0:
-                self.app.ui.splitter.setSizes([1, 1])
-            else:
-                try:
-                    if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
-                        # if tab is populated with the tool but it does not have the focus, focus on it
-                        if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
-                            # focus on Tool Tab
-                            self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
-                        else:
-                            self.app.ui.splitter.setSizes([0, 1])
-                except AttributeError:
-                    pass
-        else:
-            if self.app.ui.splitter.sizes()[0] == 0:
-                self.app.ui.splitter.setSizes([1, 1])
+            # RULE: Check Copper to Outline Clearance
+            if self.ui.clearance_copper2ol_cb.get_value() and self.ui.out_cb.get_value():
+                top_dict = {}
+                bottom_dict = {}
+                outline_dict = {}
 
-        AppTool.run(self)
-        self.set_tool_ui()
+                copper_top = self.ui.copper_t_object.currentText()
+                if copper_top != '' and self.ui.copper_t_cb.get_value():
+                    top_dict['name'] = deepcopy(copper_top)
+                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_top).apertures)
 
-        self.app.ui.notebook.setTabText(2, _("Rules Tool"))
+                copper_bottom = self.ui.copper_b_object.currentText()
+                if copper_bottom != '' and self.ui.copper_b_cb.get_value():
+                    bottom_dict['name'] = deepcopy(copper_bottom)
+                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_bottom).apertures)
 
-    def install(self, icon=None, separator=None, **kwargs):
-        AppTool.install(self, icon, separator, shortcut='Alt+R', **kwargs)
+                copper_outline = self.ui.outline_object.currentText()
+                if copper_outline != '' and self.ui.out_cb.get_value():
+                    outline_dict['name'] = deepcopy(copper_outline)
+                    outline_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_outline).apertures)
 
-    def set_tool_ui(self):
+                try:
+                    copper_outline_clearance = float(self.ui.clearance_copper2ol_entry.get_value())
+                except Exception as e:
+                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Copper to Outline clearance"),
+                        _("Value is not valid.")))
+                    return
 
-        # all object combobox default as disabled
-        self.copper_t_object.setDisabled(True)
-        self.copper_b_object.setDisabled(True)
-
-        self.sm_t_object.setDisabled(True)
-        self.sm_b_object.setDisabled(True)
-
-        self.ss_t_object.setDisabled(True)
-        self.ss_b_object.setDisabled(True)
-
-        self.outline_object.setDisabled(True)
-
-        self.e1_object.setDisabled(True)
-        self.e2_object.setDisabled(True)
-
-        self.trace_size_cb.set_value(self.app.defaults["tools_cr_trace_size"])
-        self.trace_size_entry.set_value(float(self.app.defaults["tools_cr_trace_size_val"]))
-        self.clearance_copper2copper_cb.set_value(self.app.defaults["tools_cr_c2c"])
-        self.clearance_copper2copper_entry.set_value(float(self.app.defaults["tools_cr_c2c_val"]))
-        self.clearance_copper2ol_cb.set_value(self.app.defaults["tools_cr_c2o"])
-        self.clearance_copper2ol_entry.set_value(float(self.app.defaults["tools_cr_c2o_val"]))
-        self.clearance_silk2silk_cb.set_value(self.app.defaults["tools_cr_s2s"])
-        self.clearance_silk2silk_entry.set_value(float(self.app.defaults["tools_cr_s2s_val"]))
-        self.clearance_silk2sm_cb.set_value(self.app.defaults["tools_cr_s2sm"])
-        self.clearance_silk2sm_entry.set_value(float(self.app.defaults["tools_cr_s2sm_val"]))
-        self.clearance_silk2ol_cb.set_value(self.app.defaults["tools_cr_s2o"])
-        self.clearance_silk2ol_entry.set_value(float(self.app.defaults["tools_cr_s2o_val"]))
-        self.clearance_sm2sm_cb.set_value(self.app.defaults["tools_cr_sm2sm"])
-        self.clearance_sm2sm_entry.set_value(float(self.app.defaults["tools_cr_sm2sm_val"]))
-        self.ring_integrity_cb.set_value(self.app.defaults["tools_cr_ri"])
-        self.ring_integrity_entry.set_value(float(self.app.defaults["tools_cr_ri_val"]))
-        self.clearance_d2d_cb.set_value(self.app.defaults["tools_cr_h2h"])
-        self.clearance_d2d_entry.set_value(float(self.app.defaults["tools_cr_h2h_val"]))
-        self.drill_size_cb.set_value(self.app.defaults["tools_cr_dh"])
-        self.drill_size_entry.set_value(float(self.app.defaults["tools_cr_dh_val"]))
+                if not top_dict and not bottom_dict or not outline_dict:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Copper to Outline clearance"),
+                        _("One of the copper Gerber objects or the Outline Gerber object is not valid.")))
+                    return
+                objs = []
+                if top_dict:
+                    objs.append(top_dict)
+                if bottom_dict:
+                    objs.append(bottom_dict)
 
-        self.reset_fields()
+                if outline_dict:
+                    objs.append(outline_dict)
+                else:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Copper to Outline clearance"),
+                        _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
+                    return
 
-    @staticmethod
-    def check_inside_gerber_clearance(gerber_obj, size, rule):
-        log.debug("RulesCheck.check_inside_gerber_clearance()")
+                self.results.append(self.pool.apply_async(self.check_gerber_clearance,
+                                                          args=(objs,
+                                                                copper_outline_clearance,
+                                                                _("Copper to Outline clearance"))))
 
-        rule_title = rule
+            # RULE: Check Silk to Silk Clearance
+            if self.ui.clearance_silk2silk_cb.get_value():
+                silk_dict = {}
 
-        violations = []
-        obj_violations = {}
-        obj_violations.update({
-            'name': '',
-            'points': list()
-        })
+                try:
+                    silk_silk_clearance = float(self.ui.clearance_silk2silk_entry.get_value())
+                except Exception as e:
+                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Silk clearance"),
+                        _("Value is not valid.")))
+                    return
 
-        if not gerber_obj:
-            return 'Fail. Not enough Gerber objects to check Gerber 2 Gerber clearance'
+                if self.ss_t_cb.get_value():
+                    silk_obj = self.ui.ss_t_object.currentText()
+                    if silk_obj != '':
+                        silk_dict['name'] = deepcopy(silk_obj)
+                        silk_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_obj).apertures)
 
-        obj_violations['name'] = gerber_obj['name']
+                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
+                                                                  args=(silk_dict,
+                                                                        silk_silk_clearance,
+                                                                        _("TOP -> Silk to Silk clearance"))))
+                if self.ui.ss_b_cb.get_value():
+                    silk_obj = self.ui.ss_b_object.currentText()
+                    if silk_obj != '':
+                        silk_dict['name'] = deepcopy(silk_obj)
+                        silk_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_obj).apertures)
 
-        solid_geo = []
-        clear_geo = []
-        for apid in gerber_obj['apertures']:
-            if 'geometry' in gerber_obj['apertures'][apid]:
-                geometry = gerber_obj['apertures'][apid]['geometry']
-                for geo_el in geometry:
-                    if 'solid' in geo_el and geo_el['solid'] is not None:
-                        solid_geo.append(geo_el['solid'])
-                    if 'clear' in geo_el and geo_el['clear'] is not None:
-                        clear_geo.append(geo_el['clear'])
+                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
+                                                                  args=(silk_dict,
+                                                                        silk_silk_clearance,
+                                                                        _("BOTTOM -> Silk to Silk clearance"))))
 
-        if clear_geo:
-            total_geo = []
-            for geo_c in clear_geo:
-                for geo_s in solid_geo:
-                    if geo_c.within(geo_s):
-                        total_geo.append(geo_s.difference(geo_c))
-        else:
-            total_geo = MultiPolygon(solid_geo)
-            total_geo = total_geo.buffer(0.000001)
+                if self.ui.ss_t_cb.get_value() is False and self.ui.ss_b_cb.get_value() is False:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Silk clearance"),
+                        _("At least one Gerber object has to be selected for this rule but none is selected.")))
+                    return
 
-        if isinstance(total_geo, Polygon):
-            obj_violations['points'] = ['Failed. Only one polygon.']
-            return rule_title, [obj_violations]
-        else:
-            iterations = len(total_geo)
-            iterations = (iterations * (iterations - 1)) / 2
-        log.debug("RulesCheck.check_gerber_clearance(). Iterations: %s" % str(iterations))
+            # RULE: Check Silk to Solder Mask Clearance
+            if self.ui.clearance_silk2sm_cb.get_value():
+                silk_t_dict = {}
+                sm_t_dict = {}
+                silk_b_dict = {}
+                sm_b_dict = {}
 
-        min_dict = {}
-        idx = 1
-        for geo in total_geo:
-            for s_geo in total_geo[idx:]:
-                # minimize the number of distances by not taking into considerations those that are too small
-                dist = geo.distance(s_geo)
-                if float(dist) < float(size):
-                    loc_1, loc_2 = nearest_points(geo, s_geo)
+                top_ss = False
+                bottom_ss = False
+                top_sm = False
+                bottom_sm = False
 
-                    dx = loc_1.x - loc_2.x
-                    dy = loc_1.y - loc_2.y
-                    loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
+                silk_top = self.ui.ss_t_object.currentText()
+                if silk_top != '' and self.ui.ss_t_cb.get_value():
+                    silk_t_dict['name'] = deepcopy(silk_top)
+                    silk_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_top).apertures)
+                    top_ss = True
 
-                    if dist in min_dict:
-                        min_dict[dist].append(loc)
-                    else:
-                        min_dict[dist] = [loc]
-            idx += 1
-        points_list = set()
-        for dist in min_dict.keys():
-            for location in min_dict[dist]:
-                points_list.add(location)
+                silk_bottom = self.ui.ss_b_object.currentText()
+                if silk_bottom != '' and self.ui.ss_b_cb.get_value():
+                    silk_b_dict['name'] = deepcopy(silk_bottom)
+                    silk_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_bottom).apertures)
+                    bottom_ss = True
 
-        obj_violations['points'] = list(points_list)
-        violations.append(deepcopy(obj_violations))
+                sm_top = self.ui.sm_t_object.currentText()
+                if sm_top != '' and self.ui.sm_t_cb.get_value():
+                    sm_t_dict['name'] = deepcopy(sm_top)
+                    sm_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(sm_top).apertures)
+                    top_sm = True
 
-        return rule_title, violations
+                sm_bottom = self.ui.sm_b_object.currentText()
+                if sm_bottom != '' and self.ui.sm_b_cb.get_value():
+                    sm_b_dict['name'] = deepcopy(sm_bottom)
+                    sm_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(sm_bottom).apertures)
+                    bottom_sm = True
 
-    @staticmethod
-    def check_gerber_clearance(gerber_list, size, rule):
-        log.debug("RulesCheck.check_gerber_clearance()")
-        rule_title = rule
+                try:
+                    silk_sm_clearance = float(self.ui.clearance_silk2sm_entry.get_value())
+                except Exception as e:
+                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Solder Mask Clearance"),
+                        _("Value is not valid.")))
+                    return
 
-        violations = []
-        obj_violations = {}
-        obj_violations.update({
-            'name': '',
-            'points': list()
-        })
+                if (not silk_t_dict and not silk_b_dict) or (not sm_t_dict and not sm_b_dict):
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Solder Mask Clearance"),
+                        _("One or more of the Gerber objects is not valid.")))
+                    return
 
-        if len(gerber_list) == 2:
-            gerber_1 = gerber_list[0]
-            # added it so I won't have errors of using before declaring
-            gerber_2 = {}
+                if top_ss is True and top_sm is True:
+                    objs = [silk_t_dict, sm_t_dict]
+                    self.results.append(self.pool.apply_async(self.check_gerber_clearance,
+                                                              args=(objs,
+                                                                    silk_sm_clearance,
+                                                                    _("TOP -> Silk to Solder Mask Clearance"))))
+                elif bottom_ss is True and bottom_sm is True:
+                    objs = [silk_b_dict, sm_b_dict]
+                    self.results.append(self.pool.apply_async(self.check_gerber_clearance,
+                                                              args=(objs,
+                                                                    silk_sm_clearance,
+                                                                    _("BOTTOM -> Silk to Solder Mask Clearance"))))
+                else:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Solder Mask Clearance"),
+                        _("Both Silk and Solder Mask Gerber objects has to be either both Top or both Bottom.")))
+                    return
 
-            gerber_3 = gerber_list[1]
-        elif len(gerber_list) == 3:
-            gerber_1 = gerber_list[0]
-            gerber_2 = gerber_list[1]
-            gerber_3 = gerber_list[2]
-        else:
-            return 'Fail. Not enough Gerber objects to check Gerber 2 Gerber clearance'
+            # RULE: Check Silk to Outline Clearance
+            if self.ui.clearance_silk2ol_cb.get_value():
+                top_dict = {}
+                bottom_dict = {}
+                outline_dict = {}
 
-        total_geo_grb_1 = []
-        for apid in gerber_1['apertures']:
-            if 'geometry' in gerber_1['apertures'][apid]:
-                geometry = gerber_1['apertures'][apid]['geometry']
-                for geo_el in geometry:
-                    if 'solid' in geo_el and geo_el['solid'] is not None:
-                        total_geo_grb_1.append(geo_el['solid'])
+                silk_top = self.ui.ss_t_object.currentText()
+                if silk_top != '' and self.ui.ss_t_cb.get_value():
+                    top_dict['name'] = deepcopy(silk_top)
+                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_top).apertures)
 
-        if len(gerber_list) == 3:
-            # add the second Gerber geometry to the first one if it exists
-            for apid in gerber_2['apertures']:
-                if 'geometry' in gerber_2['apertures'][apid]:
-                    geometry = gerber_2['apertures'][apid]['geometry']
-                    for geo_el in geometry:
-                        if 'solid' in geo_el and geo_el['solid'] is not None:
-                            total_geo_grb_1.append(geo_el['solid'])
+                silk_bottom = self.ui.ss_b_object.currentText()
+                if silk_bottom != '' and self.ui.ss_b_cb.get_value():
+                    bottom_dict['name'] = deepcopy(silk_bottom)
+                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_bottom).apertures)
 
-        total_geo_grb_3 = []
-        for apid in gerber_3['apertures']:
-            if 'geometry' in gerber_3['apertures'][apid]:
-                geometry = gerber_3['apertures'][apid]['geometry']
-                for geo_el in geometry:
-                    if 'solid' in geo_el and geo_el['solid'] is not None:
-                        total_geo_grb_3.append(geo_el['solid'])
+                copper_outline = self.ui.outline_object.currentText()
+                if copper_outline != '' and self.ui.out_cb.get_value():
+                    outline_dict['name'] = deepcopy(copper_outline)
+                    outline_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_outline).apertures)
 
-        total_geo_grb_1 = MultiPolygon(total_geo_grb_1)
-        total_geo_grb_1 = total_geo_grb_1.buffer(0)
+                try:
+                    copper_outline_clearance = float(self.ui.clearance_copper2ol_entry.get_value())
+                except Exception as e:
+                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Outline Clearance"),
+                        _("Value is not valid.")))
+                    return
 
-        total_geo_grb_3 = MultiPolygon(total_geo_grb_3)
-        total_geo_grb_3 = total_geo_grb_3.buffer(0)
+                if not top_dict and not bottom_dict or not outline_dict:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Outline Clearance"),
+                        _("One of the Silk Gerber objects or the Outline Gerber object is not valid.")))
+                    return
 
-        if isinstance(total_geo_grb_1, Polygon):
-            len_1 = 1
-            total_geo_grb_1 = [total_geo_grb_1]
-        else:
-            len_1 = len(total_geo_grb_1)
+                objs = []
+                if top_dict:
+                    objs.append(top_dict)
+                if bottom_dict:
+                    objs.append(bottom_dict)
 
-        if isinstance(total_geo_grb_3, Polygon):
-            len_3 = 1
-            total_geo_grb_3 = [total_geo_grb_3]
-        else:
-            len_3 = len(total_geo_grb_3)
+                if outline_dict:
+                    objs.append(outline_dict)
+                else:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Silk to Outline Clearance"),
+                        _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
+                    return
 
-        iterations = len_1 * len_3
-        log.debug("RulesCheck.check_gerber_clearance(). Iterations: %s" % str(iterations))
+                self.results.append(self.pool.apply_async(self.check_gerber_clearance,
+                                                          args=(objs,
+                                                                copper_outline_clearance,
+                                                                _("Silk to Outline Clearance"))))
 
-        min_dict = {}
-        for geo in total_geo_grb_1:
-            for s_geo in total_geo_grb_3:
-                # minimize the number of distances by not taking into considerations those that are too small
-                dist = geo.distance(s_geo)
-                if float(dist) < float(size):
-                    loc_1, loc_2 = nearest_points(geo, s_geo)
+            # RULE: Check Minimum Solder Mask Sliver
+            if self.ui.clearance_silk2silk_cb.get_value():
+                sm_dict = {}
 
-                    dx = loc_1.x - loc_2.x
-                    dy = loc_1.y - loc_2.y
-                    loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
+                try:
+                    sm_sm_clearance = float(self.ui.clearance_sm2sm_entry.get_value())
+                except Exception as e:
+                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Minimum Solder Mask Sliver"),
+                        _("Value is not valid.")))
+                    return
 
-                    if dist in min_dict:
-                        min_dict[dist].append(loc)
-                    else:
-                        min_dict[dist] = [loc]
+                if self.ui.sm_t_cb.get_value():
+                    solder_obj = self.ui.sm_t_object.currentText()
+                    if solder_obj != '':
+                        sm_dict['name'] = deepcopy(solder_obj)
+                        sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures)
 
-        points_list = set()
-        for dist in min_dict.keys():
-            for location in min_dict[dist]:
-                points_list.add(location)
+                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
+                                                                  args=(sm_dict,
+                                                                        sm_sm_clearance,
+                                                                        _("TOP -> Minimum Solder Mask Sliver"))))
+                if self.ui.sm_b_cb.get_value():
+                    solder_obj = self.ui.sm_b_object.currentText()
+                    if solder_obj != '':
+                        sm_dict['name'] = deepcopy(solder_obj)
+                        sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures)
 
-        name_list = []
-        if gerber_1:
-            name_list.append(gerber_1['name'])
-        if gerber_2:
-            name_list.append(gerber_2['name'])
-        if gerber_3:
-            name_list.append(gerber_3['name'])
+                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
+                                                                  args=(sm_dict,
+                                                                        sm_sm_clearance,
+                                                                        _("BOTTOM -> Minimum Solder Mask Sliver"))))
 
-        obj_violations['name'] = name_list
-        obj_violations['points'] = list(points_list)
-        violations.append(deepcopy(obj_violations))
+                if self.ui.sm_t_cb.get_value() is False and self.ui.sm_b_cb.get_value() is False:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Minimum Solder Mask Sliver"),
+                        _("At least one Gerber object has to be selected for this rule but none is selected.")))
+                    return
 
-        return rule_title, violations
+            # RULE: Check Minimum Annular Ring
+            if self.ui.ring_integrity_cb.get_value():
+                top_dict = {}
+                bottom_dict = {}
+                exc_1_dict = {}
+                exc_2_dict = {}
 
-    @staticmethod
-    def check_holes_size(elements, size):
-        log.debug("RulesCheck.check_holes_size()")
+                copper_top = self.ui.copper_t_object.currentText()
+                if copper_top != '' and self.ui.copper_t_cb.get_value():
+                    top_dict['name'] = deepcopy(copper_top)
+                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_top).apertures)
 
-        rule = _("Hole Size")
+                copper_bottom = self.ui.copper_b_object.currentText()
+                if copper_bottom != '' and self.ui.copper_b_cb.get_value():
+                    bottom_dict['name'] = deepcopy(copper_bottom)
+                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_bottom).apertures)
 
-        violations = []
-        obj_violations = {}
-        obj_violations.update({
-            'name': '',
-            'dia': list()
-        })
+                excellon_1 = self.ui.e1_object.currentText()
+                if excellon_1 != '' and self.ui.e1_cb.get_value():
+                    exc_1_dict['name'] = deepcopy(excellon_1)
+                    exc_1_dict['tools'] = deepcopy(
+                        self.app.collection.get_by_name(excellon_1).tools)
 
-        for elem in elements:
-            dia_list = []
+                excellon_2 = self.ui.e2_object.currentText()
+                if excellon_2 != '' and self.ui.e2_cb.get_value():
+                    exc_2_dict['name'] = deepcopy(excellon_2)
+                    exc_2_dict['tools'] = deepcopy(
+                        self.app.collection.get_by_name(excellon_2).tools)
 
-            name = elem['name']
-            for tool in elem['tools']:
-                tool_dia = float('%.*f' % (4, float(elem['tools'][tool]['C'])))
-                if tool_dia < float(size):
-                    dia_list.append(tool_dia)
-            obj_violations['name'] = name
-            obj_violations['dia'] = dia_list
-            violations.append(deepcopy(obj_violations))
+                try:
+                    ring_val = float(self.ui.ring_integrity_entry.get_value())
+                except Exception as e:
+                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Minimum Annular Ring"),
+                        _("Value is not valid.")))
+                    return
 
-        return rule, violations
+                if (not top_dict and not bottom_dict) or (not exc_1_dict and not exc_2_dict):
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Minimum Annular Ring"),
+                        _("One of the Copper Gerber objects or the Excellon objects is not valid.")))
+                    return
 
-    @staticmethod
-    def check_holes_clearance(elements, size):
-        log.debug("RulesCheck.check_holes_clearance()")
-        rule = _("Hole to Hole Clearance")
+                objs = []
+                if top_dict:
+                    objs.append(top_dict)
+                elif bottom_dict:
+                    objs.append(bottom_dict)
 
-        violations = []
-        obj_violations = {}
-        obj_violations.update({
-            'name': '',
-            'points': list()
-        })
+                if exc_1_dict:
+                    objs.append(exc_1_dict)
+                elif exc_2_dict:
+                    objs.append(exc_2_dict)
+                else:
+                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                        _("Minimum Annular Ring"),
+                        _("Excellon object presence is mandatory for this rule but none is selected.")))
+                    return
 
-        total_geo = []
-        for elem in elements:
-            for tool in elem['tools']:
-                if 'solid_geometry' in elem['tools'][tool]:
-                    geometry = elem['tools'][tool]['solid_geometry']
-                    for geo in geometry:
-                        total_geo.append(geo)
+                self.results.append(self.pool.apply_async(self.check_gerber_annular_ring,
+                                                          args=(objs,
+                                                                ring_val,
+                                                                _("Minimum Annular Ring"))))
 
-        min_dict = {}
-        idx = 1
-        for geo in total_geo:
-            for s_geo in total_geo[idx:]:
+            # RULE: Check Hole to Hole Clearance
+            if self.ui.clearance_d2d_cb.get_value():
+                exc_list = []
+                exc_name_1 = self.ui.e1_object.currentText()
+                if exc_name_1 != '' and self.ui.e1_cb.get_value():
+                    elem_dict = {}
+                    elem_dict['name'] = deepcopy(exc_name_1)
+                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
+                    exc_list.append(elem_dict)
 
-                # minimize the number of distances by not taking into considerations those that are too small
-                dist = geo.distance(s_geo)
-                loc_1, loc_2 = nearest_points(geo, s_geo)
+                exc_name_2 = self.ui.e2_object.currentText()
+                if exc_name_2 != '' and self.ui.e2_cb.get_value():
+                    elem_dict = {}
+                    elem_dict['name'] = deepcopy(exc_name_2)
+                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
+                    exc_list.append(elem_dict)
 
-                dx = loc_1.x - loc_2.x
-                dy = loc_1.y - loc_2.y
-                loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
+                hole_clearance = float(self.ui.clearance_d2d_entry.get_value())
+                self.results.append(self.pool.apply_async(self.check_holes_clearance, args=(exc_list, hole_clearance)))
 
-                if dist in min_dict:
-                    min_dict[dist].append(loc)
-                else:
-                    min_dict[dist] = [loc]
-            idx += 1
+            # RULE: Check Holes Size
+            if self.ui.drill_size_cb.get_value():
+                exc_list = []
+                exc_name_1 = self.ui.e1_object.currentText()
+                if exc_name_1 != '' and self.ui.e1_cb.get_value():
+                    elem_dict = {}
+                    elem_dict['name'] = deepcopy(exc_name_1)
+                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
+                    exc_list.append(elem_dict)
 
-        points_list = set()
-        for dist in min_dict.keys():
-            if float(dist) < size:
-                for location in min_dict[dist]:
-                    points_list.add(location)
+                exc_name_2 = self.ui.e2_object.currentText()
+                if exc_name_2 != '' and self.ui.e2_cb.get_value():
+                    elem_dict = {}
+                    elem_dict['name'] = deepcopy(exc_name_2)
+                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
+                    exc_list.append(elem_dict)
 
-        name_list = []
-        for elem in elements:
-            name_list.append(elem['name'])
+                drill_size = float(self.ui.drill_size_entry.get_value())
+                self.results.append(self.pool.apply_async(self.check_holes_size, args=(exc_list, drill_size)))
 
-        obj_violations['name'] = name_list
-        obj_violations['points'] = list(points_list)
-        violations.append(deepcopy(obj_violations))
+            output = []
+            for p in self.results:
+                output.append(p.get())
 
-        return rule, violations
+            self.tool_finished.emit(output)
 
-    @staticmethod
-    def check_traces_size(elements, size):
-        log.debug("RulesCheck.check_traces_size()")
+            log.debug("RuleCheck() finished")
 
-        rule = _("Trace Size")
+        self.app.worker_task.emit({'fcn': worker_job, 'params': [self.app]})
 
-        violations = []
-        obj_violations = {}
-        obj_violations.update({
-            'name': '',
-            'size': list(),
-            'points': list()
-        })
+    def on_tool_finished(self, res):
+        def init(new_obj, app_obj):
+            txt = ''
+            for el in res:
+                txt += '<b>RULE NAME:</b>&nbsp;&nbsp;&nbsp;&nbsp;%s<BR>' % str(el[0]).upper()
+                if isinstance(el[1][0]['name'], list):
+                    for name in el[1][0]['name']:
+                        txt += 'File name: %s<BR>' % str(name)
+                else:
+                    txt += 'File name: %s<BR>' % str(el[1][0]['name'])
 
-        for elem in elements:
-            dia_list = []
-            points_list = []
-            name = elem['name']
+                point_txt = ''
+                try:
+                    if el[1][0]['points']:
+                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
+                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
+                                                                     h_color='red',
+                                                                     color='white',
+                                                                     status=_("FAILED"))
+                        if 'Failed' in el[1][0]['points'][0]:
+                            point_txt = el[1][0]['points'][0]
+                        else:
+                            for pt in el[1][0]['points']:
+                                point_txt += '(%.*f, %.*f)' % (self.decimals, float(pt[0]), self.decimals, float(pt[1]))
+                                point_txt += ', '
+                        txt += 'Violations: %s<BR>' % str(point_txt)
+                    else:
+                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
+                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
+                                                                     h_color='green',
+                                                                     color='white',
+                                                                     status=_("PASSED"))
+                        txt += '%s<BR>' % _("Violations: There are no violations for the current rule.")
+                except KeyError:
+                    pass
 
-            for apid in elem['apertures']:
                 try:
-                    tool_dia = float(elem['apertures'][apid]['size'])
-                    if tool_dia < float(size) and tool_dia != 0.0:
-                        dia_list.append(tool_dia)
-                        for geo_el in elem['apertures'][apid]['geometry']:
-                            if 'solid' in geo_el.keys():
-                                geo = geo_el['solid']
-                                pt = geo.representative_point()
-                                points_list.append((pt.x, pt.y))
-                except Exception:
-                    # An exception  will be raised for the 'size' key in case of apertures of type AM (macro) which does
-                    # not have the size key
+                    if el[1][0]['dia']:
+                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
+                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
+                                                                     h_color='red',
+                                                                     color='white',
+                                                                     status=_("FAILED"))
+                        if 'Failed' in el[1][0]['dia']:
+                            point_txt = el[1][0]['dia']
+                        else:
+                            for pt in el[1][0]['dia']:
+                                point_txt += '%.*f' % (self.decimals, float(pt))
+                                point_txt += ', '
+                        txt += 'Violations: %s<BR>' % str(point_txt)
+                    else:
+                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
+                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
+                                                                     h_color='green',
+                                                                     color='white',
+                                                                     status=_("PASSED"))
+                        txt += '%s<BR>' % _("Violations: There are no violations for the current rule.")
+                except KeyError:
                     pass
 
-            obj_violations['name'] = name
-            obj_violations['size'] = dia_list
-            obj_violations['points'] = points_list
-            violations.append(deepcopy(obj_violations))
-        return rule, violations
+                txt += '<BR><BR>'
+            new_obj.source_file = txt
+            new_obj.read_only = True
 
-    @staticmethod
-    def check_gerber_annular_ring(obj_list, size, rule):
-        rule_title = rule
+        self.app.app_obj.new_object('document', name='Rules_check_results', initialize=init, plot=False)
 
-        violations = []
-        obj_violations = {}
-        obj_violations.update({
-            'name': '',
-            'points': list()
-        })
+    def reset_fields(self):
+        # self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        # self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        pass
 
-        # added it so I won't have errors of using before declaring
-        gerber_obj = {}
-        gerber_extra_obj = {}
-        exc_obj = {}
-        exc_extra_obj = {}
 
-        if len(obj_list) == 2:
-            gerber_obj = obj_list[0]
-            exc_obj = obj_list[1]
-            if 'apertures' in gerber_obj and 'tools' in exc_obj:
-                pass
-            else:
-                return 'Fail. At least one Gerber and one Excellon object is required to check Minimum Annular Ring'
-        elif len(obj_list) == 3:
-            o1 = obj_list[0]
-            o2 = obj_list[1]
-            o3 = obj_list[2]
-            if 'apertures' in o1 and 'apertures' in o2:
-                gerber_obj = o1
-                gerber_extra_obj = o2
-                exc_obj = o3
-            elif 'tools' in o2 and 'tools' in o3:
-                gerber_obj = o1
-                exc_obj = o2
-                exc_extra_obj = o3
-        elif len(obj_list) == 4:
-            gerber_obj = obj_list[0]
-            gerber_extra_obj = obj_list[1]
-            exc_obj = obj_list[2]
-            exc_extra_obj = obj_list[3]
-        else:
-            return 'Fail. Not enough objects to check Minimum Annular Ring'
+class RulesUI:
+    
+    toolName = _("Check Rules")
 
-        total_geo_grb = []
-        for apid in gerber_obj['apertures']:
-            if 'geometry' in gerber_obj['apertures'][apid]:
-                geometry = gerber_obj['apertures'][apid]['geometry']
-                for geo_el in geometry:
-                    if 'solid' in geo_el and geo_el['solid'] is not None:
-                        total_geo_grb.append(geo_el['solid'])
+    def __init__(self, layout, app):
+        self.app = app
+        self.decimals = self.app.decimals
+        self.layout = layout
 
-        if len(obj_list) == 3 and gerber_extra_obj:
-            # add the second Gerber geometry to the first one if it exists
-            for apid in gerber_extra_obj['apertures']:
-                if 'geometry' in gerber_extra_obj['apertures'][apid]:
-                    geometry = gerber_extra_obj['apertures'][apid]['geometry']
-                    for geo_el in geometry:
-                        if 'solid' in geo_el and geo_el['solid'] is not None:
-                            total_geo_grb.append(geo_el['solid'])
+        # ## Title
+        title_label = FCLabel("%s" % self.toolName)
+        title_label.setStyleSheet("""
+                                QLabel
+                                {
+                                    font-size: 16px;
+                                    font-weight: bold;
+                                }
+                                """)
+        self.layout.addWidget(title_label)
 
-        total_geo_grb = MultiPolygon(total_geo_grb)
-        total_geo_grb = total_geo_grb.buffer(0)
+        # Form Layout
+        self.grid_layout = QtWidgets.QGridLayout()
+        self.layout.addLayout(self.grid_layout)
 
-        total_geo_exc = []
-        for tool in exc_obj['tools']:
-            if 'solid_geometry' in exc_obj['tools'][tool]:
-                geometry = exc_obj['tools'][tool]['solid_geometry']
-                for geo in geometry:
-                    total_geo_exc.append(geo)
+        self.grid_layout.setColumnStretch(0, 0)
+        self.grid_layout.setColumnStretch(1, 3)
+        self.grid_layout.setColumnStretch(2, 0)
 
-        if len(obj_list) == 3 and exc_extra_obj:
-            # add the second Excellon geometry to the first one if it exists
-            for tool in exc_extra_obj['tools']:
-                if 'solid_geometry' in exc_extra_obj['tools'][tool]:
-                    geometry = exc_extra_obj['tools'][tool]['solid_geometry']
-                    for geo in geometry:
-                        total_geo_exc.append(geo)
+        self.gerber_title_lbl = FCLabel('<b>%s</b>:' % _("GERBER"))
+        self.gerber_title_lbl.setToolTip(
+            _("Gerber objects for which to check rules.")
+        )
 
-        if isinstance(total_geo_grb, Polygon):
-            len_1 = 1
-            total_geo_grb = [total_geo_grb]
-        else:
-            len_1 = len(total_geo_grb)
+        self.all_obj_cb = FCCheckBox()
+
+        self.grid_layout.addWidget(self.gerber_title_lbl, 0, 0, 1, 2)
+        self.grid_layout.addWidget(self.all_obj_cb, 0, 2)
+
+        # Copper Top object
+        self.copper_t_object = FCComboBox()
+        self.copper_t_object.setModel(self.app.collection)
+        self.copper_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.copper_t_object.is_last = True
+        self.copper_t_object.obj_type = "Gerber"
+
+        self.copper_t_object_lbl = FCLabel('%s:' % _("Top"))
+        self.copper_t_object_lbl.setToolTip(
+            _("The Top Gerber Copper object for which rules are checked.")
+        )
+
+        self.copper_t_cb = FCCheckBox()
+
+        self.grid_layout.addWidget(self.copper_t_object_lbl, 1, 0)
+        self.grid_layout.addWidget(self.copper_t_object, 1, 1)
+        self.grid_layout.addWidget(self.copper_t_cb, 1, 2)
 
-        if isinstance(total_geo_exc, Polygon):
-            len_2 = 1
-            total_geo_exc = [total_geo_exc]
-        else:
-            len_2 = len(total_geo_exc)
+        # Copper Bottom object
+        self.copper_b_object = FCComboBox()
+        self.copper_b_object.setModel(self.app.collection)
+        self.copper_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.copper_b_object.is_last = True
+        self.copper_b_object.obj_type = "Gerber"
 
-        iterations = len_1 * len_2
-        log.debug("RulesCheck.check_gerber_annular_ring(). Iterations: %s" % str(iterations))
+        self.copper_b_object_lbl = FCLabel('%s:' % _("Bottom"))
+        self.copper_b_object_lbl.setToolTip(
+            _("The Bottom Gerber Copper object for which rules are checked.")
+        )
 
-        min_dict = {}
-        dist = None
-        for geo in total_geo_grb:
-            for s_geo in total_geo_exc:
-                try:
-                    # minimize the number of distances by not taking into considerations those that are too small
-                    dist = abs(geo.exterior.distance(s_geo))
-                except Exception as e:
-                    log.debug("RulesCheck.check_gerber_annular_ring() --> %s" % str(e))
+        self.copper_b_cb = FCCheckBox()
 
-                if dist > 0:
-                    if float(dist) < float(size):
-                        loc_1, loc_2 = nearest_points(geo.exterior, s_geo)
+        self.grid_layout.addWidget(self.copper_b_object_lbl, 2, 0)
+        self.grid_layout.addWidget(self.copper_b_object, 2, 1)
+        self.grid_layout.addWidget(self.copper_b_cb, 2, 2)
 
-                        dx = loc_1.x - loc_2.x
-                        dy = loc_1.y - loc_2.y
-                        loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2)
+        # SolderMask Top object
+        self.sm_t_object = FCComboBox()
+        self.sm_t_object.setModel(self.app.collection)
+        self.sm_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.sm_t_object.is_last = True
+        self.sm_t_object.obj_type = "Gerber"
 
-                        if dist in min_dict:
-                            min_dict[dist].append(loc)
-                        else:
-                            min_dict[dist] = [loc]
-                else:
-                    if dist in min_dict:
-                        min_dict[dist].append(s_geo.representative_point())
-                    else:
-                        min_dict[dist] = [s_geo.representative_point()]
+        self.sm_t_object_lbl = FCLabel('%s:' % _("SM Top"))
+        self.sm_t_object_lbl.setToolTip(
+            _("The Top Gerber Solder Mask object for which rules are checked.")
+        )
 
-        points_list = []
-        for dist in min_dict.keys():
-            for location in min_dict[dist]:
-                points_list.append(location)
+        self.sm_t_cb = FCCheckBox()
 
-        name_list = []
-        try:
-            if gerber_obj:
-                name_list.append(gerber_obj['name'])
-        except KeyError:
-            pass
-        try:
-            if gerber_extra_obj:
-                name_list.append(gerber_extra_obj['name'])
-        except KeyError:
-            pass
+        self.grid_layout.addWidget(self.sm_t_object_lbl, 3, 0)
+        self.grid_layout.addWidget(self.sm_t_object, 3, 1)
+        self.grid_layout.addWidget(self.sm_t_cb, 3, 2)
 
-        try:
-            if exc_obj:
-                name_list.append(exc_obj['name'])
-        except KeyError:
-            pass
+        # SolderMask Bottom object
+        self.sm_b_object = FCComboBox()
+        self.sm_b_object.setModel(self.app.collection)
+        self.sm_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.sm_b_object.is_last = True
+        self.sm_b_object.obj_type = "Gerber"
 
-        try:
-            if exc_extra_obj:
-                name_list.append(exc_extra_obj['name'])
-        except KeyError:
-            pass
+        self.sm_b_object_lbl = FCLabel('%s:' % _("SM Bottom"))
+        self.sm_b_object_lbl.setToolTip(
+            _("The Bottom Gerber Solder Mask object for which rules are checked.")
+        )
 
-        obj_violations['name'] = name_list
-        obj_violations['points'] = points_list
-        violations.append(deepcopy(obj_violations))
-        return rule_title, violations
+        self.sm_b_cb = FCCheckBox()
 
-    def execute(self):
-        self.results = []
+        self.grid_layout.addWidget(self.sm_b_object_lbl, 4, 0)
+        self.grid_layout.addWidget(self.sm_b_object, 4, 1)
+        self.grid_layout.addWidget(self.sm_b_cb, 4, 2)
 
-        log.debug("RuleCheck() executing")
+        # SilkScreen Top object
+        self.ss_t_object = FCComboBox()
+        self.ss_t_object.setModel(self.app.collection)
+        self.ss_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.ss_t_object.is_last = True
+        self.ss_t_object.obj_type = "Gerber"
 
-        def worker_job(app_obj):
-            self.app.proc_container.new(_("Working..."))
+        self.ss_t_object_lbl = FCLabel('%s:' % _("Silk Top"))
+        self.ss_t_object_lbl.setToolTip(
+            _("The Top Gerber Silkscreen object for which rules are checked.")
+        )
 
-            # RULE: Check Trace Size
-            if self.trace_size_cb.get_value():
-                copper_list = []
-                copper_name_1 = self.copper_t_object.currentText()
-                if copper_name_1 != '' and self.copper_t_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(copper_name_1)
-                    elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_1).apertures)
-                    copper_list.append(elem_dict)
+        self.ss_t_cb = FCCheckBox()
 
-                copper_name_2 = self.copper_b_object.currentText()
-                if copper_name_2 != '' and self.copper_b_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(copper_name_2)
-                    elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_2).apertures)
-                    copper_list.append(elem_dict)
+        self.grid_layout.addWidget(self.ss_t_object_lbl, 5, 0)
+        self.grid_layout.addWidget(self.ss_t_object, 5, 1)
+        self.grid_layout.addWidget(self.ss_t_cb, 5, 2)
 
-                trace_size = float(self.trace_size_entry.get_value())
-                self.results.append(self.pool.apply_async(self.check_traces_size, args=(copper_list, trace_size)))
+        # SilkScreen Bottom object
+        self.ss_b_object = FCComboBox()
+        self.ss_b_object.setModel(self.app.collection)
+        self.ss_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.ss_b_object.is_last = True
+        self.ss_b_object.obj_type = "Gerber"
 
-            # RULE: Check Copper to Copper Clearance
-            if self.clearance_copper2copper_cb.get_value():
+        self.ss_b_object_lbl = FCLabel('%s:' % _("Silk Bottom"))
+        self.ss_b_object_lbl.setToolTip(
+            _("The Bottom Gerber Silkscreen object for which rules are checked.")
+        )
 
-                try:
-                    copper_copper_clearance = float(self.clearance_copper2copper_entry.get_value())
-                except Exception as e:
-                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Copper to Copper clearance"),
-                        _("Value is not valid.")))
-                    return
+        self.ss_b_cb = FCCheckBox()
 
-                if self.copper_t_cb.get_value():
-                    copper_t_obj = self.copper_t_object.currentText()
-                    copper_t_dict = {}
+        self.grid_layout.addWidget(self.ss_b_object_lbl, 6, 0)
+        self.grid_layout.addWidget(self.ss_b_object, 6, 1)
+        self.grid_layout.addWidget(self.ss_b_cb, 6, 2)
 
-                    if copper_t_obj != '':
-                        copper_t_dict['name'] = deepcopy(copper_t_obj)
-                        copper_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_t_obj).apertures)
+        # Outline object
+        self.outline_object = FCComboBox()
+        self.outline_object.setModel(self.app.collection)
+        self.outline_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.outline_object.is_last = True
+        self.outline_object.obj_type = "Gerber"
 
-                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
-                                                                  args=(copper_t_dict,
-                                                                        copper_copper_clearance,
-                                                                        _("TOP -> Copper to Copper clearance"))))
-                if self.copper_b_cb.get_value():
-                    copper_b_obj = self.copper_b_object.currentText()
-                    copper_b_dict = {}
-                    if copper_b_obj != '':
-                        copper_b_dict['name'] = deepcopy(copper_b_obj)
-                        copper_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_b_obj).apertures)
+        self.outline_object_lbl = FCLabel('%s:' % _("Outline"))
+        self.outline_object_lbl.setToolTip(
+            _("The Gerber Outline (Cutout) object for which rules are checked.")
+        )
 
-                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
-                                                                  args=(copper_b_dict,
-                                                                        copper_copper_clearance,
-                                                                        _("BOTTOM -> Copper to Copper clearance"))))
+        self.out_cb = FCCheckBox()
 
-                if self.copper_t_cb.get_value() is False and self.copper_b_cb.get_value() is False:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Copper to Copper clearance"),
-                        _("At least one Gerber object has to be selected for this rule but none is selected.")))
-                    return
+        self.grid_layout.addWidget(self.outline_object_lbl, 7, 0)
+        self.grid_layout.addWidget(self.outline_object, 7, 1)
+        self.grid_layout.addWidget(self.out_cb, 7, 2)
 
-            # RULE: Check Copper to Outline Clearance
-            if self.clearance_copper2ol_cb.get_value() and self.out_cb.get_value():
-                top_dict = {}
-                bottom_dict = {}
-                outline_dict = {}
+        self.grid_layout.addWidget(FCLabel(""), 8, 0, 1, 3)
 
-                copper_top = self.copper_t_object.currentText()
-                if copper_top != '' and self.copper_t_cb.get_value():
-                    top_dict['name'] = deepcopy(copper_top)
-                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_top).apertures)
+        self.excellon_title_lbl = FCLabel('<b>%s</b>:' % _("EXCELLON"))
+        self.excellon_title_lbl.setToolTip(
+            _("Excellon objects for which to check rules.")
+        )
 
-                copper_bottom = self.copper_b_object.currentText()
-                if copper_bottom != '' and self.copper_b_cb.get_value():
-                    bottom_dict['name'] = deepcopy(copper_bottom)
-                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_bottom).apertures)
+        self.grid_layout.addWidget(self.excellon_title_lbl, 9, 0, 1, 3)
 
-                copper_outline = self.outline_object.currentText()
-                if copper_outline != '' and self.out_cb.get_value():
-                    outline_dict['name'] = deepcopy(copper_outline)
-                    outline_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_outline).apertures)
+        # Excellon 1 object
+        self.e1_object = FCComboBox()
+        self.e1_object.setModel(self.app.collection)
+        self.e1_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
+        self.e1_object.is_last = True
+        self.e1_object.obj_type = "Excellon"
 
-                try:
-                    copper_outline_clearance = float(self.clearance_copper2ol_entry.get_value())
-                except Exception as e:
-                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Copper to Outline clearance"),
-                        _("Value is not valid.")))
-                    return
+        self.e1_object_lbl = FCLabel('%s:' % _("Excellon 1"))
+        self.e1_object_lbl.setToolTip(
+            _("Excellon object for which to check rules.\n"
+              "Holds the plated holes or a general Excellon file content.")
+        )
 
-                if not top_dict and not bottom_dict or not outline_dict:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Copper to Outline clearance"),
-                        _("One of the copper Gerber objects or the Outline Gerber object is not valid.")))
-                    return
-                objs = []
-                if top_dict:
-                    objs.append(top_dict)
-                if bottom_dict:
-                    objs.append(bottom_dict)
+        self.e1_cb = FCCheckBox()
 
-                if outline_dict:
-                    objs.append(outline_dict)
-                else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Copper to Outline clearance"),
-                        _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
-                    return
+        self.grid_layout.addWidget(self.e1_object_lbl, 10, 0)
+        self.grid_layout.addWidget(self.e1_object, 10, 1)
+        self.grid_layout.addWidget(self.e1_cb, 10, 2)
 
-                self.results.append(self.pool.apply_async(self.check_gerber_clearance,
-                                                          args=(objs,
-                                                                copper_outline_clearance,
-                                                                _("Copper to Outline clearance"))))
+        # Excellon 2 object
+        self.e2_object = FCComboBox()
+        self.e2_object.setModel(self.app.collection)
+        self.e2_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
+        self.e2_object.is_last = True
+        self.e2_object.obj_type = "Excellon"
 
-            # RULE: Check Silk to Silk Clearance
-            if self.clearance_silk2silk_cb.get_value():
-                silk_dict = {}
+        self.e2_object_lbl = FCLabel('%s:' % _("Excellon 2"))
+        self.e2_object_lbl.setToolTip(
+            _("Excellon object for which to check rules.\n"
+              "Holds the non-plated holes.")
+        )
+
+        self.e2_cb = FCCheckBox()
 
-                try:
-                    silk_silk_clearance = float(self.clearance_silk2silk_entry.get_value())
-                except Exception as e:
-                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Silk clearance"),
-                        _("Value is not valid.")))
-                    return
+        self.grid_layout.addWidget(self.e2_object_lbl, 11, 0)
+        self.grid_layout.addWidget(self.e2_object, 11, 1)
+        self.grid_layout.addWidget(self.e2_cb, 11, 2)
 
-                if self.ss_t_cb.get_value():
-                    silk_obj = self.ss_t_object.currentText()
-                    if silk_obj != '':
-                        silk_dict['name'] = deepcopy(silk_obj)
-                        silk_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_obj).apertures)
+        self.grid_layout.addWidget(FCLabel(""), 12, 0, 1, 3)
 
-                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
-                                                                  args=(silk_dict,
-                                                                        silk_silk_clearance,
-                                                                        _("TOP -> Silk to Silk clearance"))))
-                if self.ss_b_cb.get_value():
-                    silk_obj = self.ss_b_object.currentText()
-                    if silk_obj != '':
-                        silk_dict['name'] = deepcopy(silk_obj)
-                        silk_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_obj).apertures)
+        # Control All
+        self.all_cb = FCCheckBox('%s' % _("All Rules"))
+        self.all_cb.setToolTip(
+            _("This check/uncheck all the rules below.")
+        )
+        self.all_cb.setStyleSheet(
+            """
+            QCheckBox {font-weight: bold; color: green}
+            """
+        )
+        self.layout.addWidget(self.all_cb)
 
-                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
-                                                                  args=(silk_dict,
-                                                                        silk_silk_clearance,
-                                                                        _("BOTTOM -> Silk to Silk clearance"))))
+        # Form Layout
+        self.form_layout_1 = QtWidgets.QFormLayout()
+        self.layout.addLayout(self.form_layout_1)
 
-                if self.ss_t_cb.get_value() is False and self.ss_b_cb.get_value() is False:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Silk clearance"),
-                        _("At least one Gerber object has to be selected for this rule but none is selected.")))
-                    return
+        self.form_layout_1.addRow(FCLabel(""))
 
-            # RULE: Check Silk to Solder Mask Clearance
-            if self.clearance_silk2sm_cb.get_value():
-                silk_t_dict = {}
-                sm_t_dict = {}
-                silk_b_dict = {}
-                sm_b_dict = {}
+        # Trace size
+        self.trace_size_cb = FCCheckBox('%s:' % _("Trace Size"))
+        self.trace_size_cb.setToolTip(
+            _("This checks if the minimum size for traces is met.")
+        )
+        self.form_layout_1.addRow(self.trace_size_cb)
 
-                top_ss = False
-                bottom_ss = False
-                top_sm = False
-                bottom_sm = False
+        # Trace size value
+        self.trace_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.trace_size_entry.set_range(0.00001, 999.99999)
+        self.trace_size_entry.set_precision(self.decimals)
+        self.trace_size_entry.setSingleStep(0.1)
 
-                silk_top = self.ss_t_object.currentText()
-                if silk_top != '' and self.ss_t_cb.get_value():
-                    silk_t_dict['name'] = deepcopy(silk_top)
-                    silk_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_top).apertures)
-                    top_ss = True
+        self.trace_size_lbl = FCLabel('%s:' % _("Min value"))
+        self.trace_size_lbl.setToolTip(
+            _("Minimum acceptable trace size.")
+        )
+        self.form_layout_1.addRow(self.trace_size_lbl, self.trace_size_entry)
 
-                silk_bottom = self.ss_b_object.currentText()
-                if silk_bottom != '' and self.ss_b_cb.get_value():
-                    silk_b_dict['name'] = deepcopy(silk_bottom)
-                    silk_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_bottom).apertures)
-                    bottom_ss = True
+        self.ts = OptionalInputSection(self.trace_size_cb, [self.trace_size_lbl, self.trace_size_entry])
 
-                sm_top = self.sm_t_object.currentText()
-                if sm_top != '' and self.sm_t_cb.get_value():
-                    sm_t_dict['name'] = deepcopy(sm_top)
-                    sm_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(sm_top).apertures)
-                    top_sm = True
+        # Copper2copper clearance
+        self.clearance_copper2copper_cb = FCCheckBox('%s:' % _("Copper to Copper clearance"))
+        self.clearance_copper2copper_cb.setToolTip(
+            _("This checks if the minimum clearance between copper\n"
+              "features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2copper_cb)
 
-                sm_bottom = self.sm_b_object.currentText()
-                if sm_bottom != '' and self.sm_b_cb.get_value():
-                    sm_b_dict['name'] = deepcopy(sm_bottom)
-                    sm_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(sm_bottom).apertures)
-                    bottom_sm = True
+        # Copper2copper clearance value
+        self.clearance_copper2copper_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.clearance_copper2copper_entry.set_range(0.00001, 999.99999)
+        self.clearance_copper2copper_entry.set_precision(self.decimals)
+        self.clearance_copper2copper_entry.setSingleStep(0.1)
 
-                try:
-                    silk_sm_clearance = float(self.clearance_silk2sm_entry.get_value())
-                except Exception as e:
-                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Solder Mask Clearance"),
-                        _("Value is not valid.")))
-                    return
+        self.clearance_copper2copper_lbl = FCLabel('%s:' % _("Min value"))
+        self.clearance_copper2copper_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry)
 
-                if (not silk_t_dict and not silk_b_dict) or (not sm_t_dict and not sm_b_dict):
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Solder Mask Clearance"),
-                        _("One or more of the Gerber objects is not valid.")))
-                    return
+        self.c2c = OptionalInputSection(
+            self.clearance_copper2copper_cb, [self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry])
 
-                if top_ss is True and top_sm is True:
-                    objs = [silk_t_dict, sm_t_dict]
-                    self.results.append(self.pool.apply_async(self.check_gerber_clearance,
-                                                              args=(objs,
-                                                                    silk_sm_clearance,
-                                                                    _("TOP -> Silk to Solder Mask Clearance"))))
-                elif bottom_ss is True and bottom_sm is True:
-                    objs = [silk_b_dict, sm_b_dict]
-                    self.results.append(self.pool.apply_async(self.check_gerber_clearance,
-                                                              args=(objs,
-                                                                    silk_sm_clearance,
-                                                                    _("BOTTOM -> Silk to Solder Mask Clearance"))))
-                else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Solder Mask Clearance"),
-                        _("Both Silk and Solder Mask Gerber objects has to be either both Top or both Bottom.")))
-                    return
+        # Copper2outline clearance
+        self.clearance_copper2ol_cb = FCCheckBox('%s:' % _("Copper to Outline clearance"))
+        self.clearance_copper2ol_cb.setToolTip(
+            _("This checks if the minimum clearance between copper\n"
+              "features and the outline is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2ol_cb)
 
-            # RULE: Check Silk to Outline Clearance
-            if self.clearance_silk2ol_cb.get_value():
-                top_dict = {}
-                bottom_dict = {}
-                outline_dict = {}
+        # Copper2outline clearance value
+        self.clearance_copper2ol_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.clearance_copper2ol_entry.set_range(0.00001, 999.99999)
+        self.clearance_copper2ol_entry.set_precision(self.decimals)
+        self.clearance_copper2ol_entry.setSingleStep(0.1)
 
-                silk_top = self.ss_t_object.currentText()
-                if silk_top != '' and self.ss_t_cb.get_value():
-                    top_dict['name'] = deepcopy(silk_top)
-                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_top).apertures)
+        self.clearance_copper2ol_lbl = FCLabel('%s:' % _("Min value"))
+        self.clearance_copper2ol_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry)
 
-                silk_bottom = self.ss_b_object.currentText()
-                if silk_bottom != '' and self.ss_b_cb.get_value():
-                    bottom_dict['name'] = deepcopy(silk_bottom)
-                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_bottom).apertures)
+        self.c2ol = OptionalInputSection(
+            self.clearance_copper2ol_cb, [self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry])
 
-                copper_outline = self.outline_object.currentText()
-                if copper_outline != '' and self.out_cb.get_value():
-                    outline_dict['name'] = deepcopy(copper_outline)
-                    outline_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_outline).apertures)
+        # Silkscreen2silkscreen clearance
+        self.clearance_silk2silk_cb = FCCheckBox('%s:' % _("Silk to Silk Clearance"))
+        self.clearance_silk2silk_cb.setToolTip(
+            _("This checks if the minimum clearance between silkscreen\n"
+              "features and silkscreen features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2silk_cb)
 
-                try:
-                    copper_outline_clearance = float(self.clearance_copper2ol_entry.get_value())
-                except Exception as e:
-                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Outline Clearance"),
-                        _("Value is not valid.")))
-                    return
+        # Copper2silkscreen clearance value
+        self.clearance_silk2silk_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.clearance_silk2silk_entry.set_range(0.00001, 999.99999)
+        self.clearance_silk2silk_entry.set_precision(self.decimals)
+        self.clearance_silk2silk_entry.setSingleStep(0.1)
 
-                if not top_dict and not bottom_dict or not outline_dict:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Outline Clearance"),
-                        _("One of the Silk Gerber objects or the Outline Gerber object is not valid.")))
-                    return
+        self.clearance_silk2silk_lbl = FCLabel('%s:' % _("Min value"))
+        self.clearance_silk2silk_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry)
 
-                objs = []
-                if top_dict:
-                    objs.append(top_dict)
-                if bottom_dict:
-                    objs.append(bottom_dict)
+        self.s2s = OptionalInputSection(
+            self.clearance_silk2silk_cb, [self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry])
 
-                if outline_dict:
-                    objs.append(outline_dict)
-                else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Silk to Outline Clearance"),
-                        _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
-                    return
+        # Silkscreen2soldermask clearance
+        self.clearance_silk2sm_cb = FCCheckBox('%s:' % _("Silk to Solder Mask Clearance"))
+        self.clearance_silk2sm_cb.setToolTip(
+            _("This checks if the minimum clearance between silkscreen\n"
+              "features and soldermask features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2sm_cb)
 
-                self.results.append(self.pool.apply_async(self.check_gerber_clearance,
-                                                          args=(objs,
-                                                                copper_outline_clearance,
-                                                                _("Silk to Outline Clearance"))))
+        # Silkscreen2soldermask clearance value
+        self.clearance_silk2sm_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.clearance_silk2sm_entry.set_range(0.00001, 999.99999)
+        self.clearance_silk2sm_entry.set_precision(self.decimals)
+        self.clearance_silk2sm_entry.setSingleStep(0.1)
 
-            # RULE: Check Minimum Solder Mask Sliver
-            if self.clearance_silk2silk_cb.get_value():
-                sm_dict = {}
+        self.clearance_silk2sm_lbl = FCLabel('%s:' % _("Min value"))
+        self.clearance_silk2sm_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry)
 
-                try:
-                    sm_sm_clearance = float(self.clearance_sm2sm_entry.get_value())
-                except Exception as e:
-                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Minimum Solder Mask Sliver"),
-                        _("Value is not valid.")))
-                    return
+        self.s2sm = OptionalInputSection(
+            self.clearance_silk2sm_cb, [self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry])
 
-                if self.sm_t_cb.get_value():
-                    solder_obj = self.sm_t_object.currentText()
-                    if solder_obj != '':
-                        sm_dict['name'] = deepcopy(solder_obj)
-                        sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures)
+        # Silk2outline clearance
+        self.clearance_silk2ol_cb = FCCheckBox('%s:' % _("Silk to Outline Clearance"))
+        self.clearance_silk2ol_cb.setToolTip(
+            _("This checks if the minimum clearance between silk\n"
+              "features and the outline is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2ol_cb)
 
-                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
-                                                                  args=(sm_dict,
-                                                                        sm_sm_clearance,
-                                                                        _("TOP -> Minimum Solder Mask Sliver"))))
-                if self.sm_b_cb.get_value():
-                    solder_obj = self.sm_b_object.currentText()
-                    if solder_obj != '':
-                        sm_dict['name'] = deepcopy(solder_obj)
-                        sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures)
+        # Silk2outline clearance value
+        self.clearance_silk2ol_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.clearance_silk2ol_entry.set_range(0.00001, 999.99999)
+        self.clearance_silk2ol_entry.set_precision(self.decimals)
+        self.clearance_silk2ol_entry.setSingleStep(0.1)
 
-                        self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
-                                                                  args=(sm_dict,
-                                                                        sm_sm_clearance,
-                                                                        _("BOTTOM -> Minimum Solder Mask Sliver"))))
+        self.clearance_silk2ol_lbl = FCLabel('%s:' % _("Min value"))
+        self.clearance_silk2ol_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2ol_lbl, self.clearance_silk2ol_entry)
 
-                if self.sm_t_cb.get_value() is False and self.sm_b_cb.get_value() is False:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Minimum Solder Mask Sliver"),
-                        _("At least one Gerber object has to be selected for this rule but none is selected.")))
-                    return
+        self.s2ol = OptionalInputSection(
+            self.clearance_silk2ol_cb, [self.clearance_silk2ol_lbl, self.clearance_silk2ol_entry])
 
-            # RULE: Check Minimum Annular Ring
-            if self.ring_integrity_cb.get_value():
-                top_dict = {}
-                bottom_dict = {}
-                exc_1_dict = {}
-                exc_2_dict = {}
+        # Soldermask2soldermask clearance
+        self.clearance_sm2sm_cb = FCCheckBox('%s:' % _("Minimum Solder Mask Sliver"))
+        self.clearance_sm2sm_cb.setToolTip(
+            _("This checks if the minimum clearance between soldermask\n"
+              "features and soldermask features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_sm2sm_cb)
 
-                copper_top = self.copper_t_object.currentText()
-                if copper_top != '' and self.copper_t_cb.get_value():
-                    top_dict['name'] = deepcopy(copper_top)
-                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_top).apertures)
+        # Soldermask2soldermask clearance value
+        self.clearance_sm2sm_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.clearance_sm2sm_entry.set_range(0.00001, 999.99999)
+        self.clearance_sm2sm_entry.set_precision(self.decimals)
+        self.clearance_sm2sm_entry.setSingleStep(0.1)
 
-                copper_bottom = self.copper_b_object.currentText()
-                if copper_bottom != '' and self.copper_b_cb.get_value():
-                    bottom_dict['name'] = deepcopy(copper_bottom)
-                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_bottom).apertures)
+        self.clearance_sm2sm_lbl = FCLabel('%s:' % _("Min value"))
+        self.clearance_sm2sm_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry)
 
-                excellon_1 = self.e1_object.currentText()
-                if excellon_1 != '' and self.e1_cb.get_value():
-                    exc_1_dict['name'] = deepcopy(excellon_1)
-                    exc_1_dict['tools'] = deepcopy(
-                        self.app.collection.get_by_name(excellon_1).tools)
+        self.sm2sm = OptionalInputSection(
+            self.clearance_sm2sm_cb, [self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry])
 
-                excellon_2 = self.e2_object.currentText()
-                if excellon_2 != '' and self.e2_cb.get_value():
-                    exc_2_dict['name'] = deepcopy(excellon_2)
-                    exc_2_dict['tools'] = deepcopy(
-                        self.app.collection.get_by_name(excellon_2).tools)
+        # Ring integrity check
+        self.ring_integrity_cb = FCCheckBox('%s:' % _("Minimum Annular Ring"))
+        self.ring_integrity_cb.setToolTip(
+            _("This checks if the minimum copper ring left by drilling\n"
+              "a hole into a pad is met.")
+        )
+        self.form_layout_1.addRow(self.ring_integrity_cb)
 
-                try:
-                    ring_val = float(self.ring_integrity_entry.get_value())
-                except Exception as e:
-                    log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Minimum Annular Ring"),
-                        _("Value is not valid.")))
-                    return
+        # Ring integrity value
+        self.ring_integrity_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.ring_integrity_entry.set_range(0.00001, 999.99999)
+        self.ring_integrity_entry.set_precision(self.decimals)
+        self.ring_integrity_entry.setSingleStep(0.1)
 
-                if (not top_dict and not bottom_dict) or (not exc_1_dict and not exc_2_dict):
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Minimum Annular Ring"),
-                        _("One of the Copper Gerber objects or the Excellon objects is not valid.")))
-                    return
+        self.ring_integrity_lbl = FCLabel('%s:' % _("Min value"))
+        self.ring_integrity_lbl.setToolTip(
+            _("Minimum acceptable ring value.")
+        )
+        self.form_layout_1.addRow(self.ring_integrity_lbl, self.ring_integrity_entry)
 
-                objs = []
-                if top_dict:
-                    objs.append(top_dict)
-                elif bottom_dict:
-                    objs.append(bottom_dict)
+        self.anr = OptionalInputSection(
+            self.ring_integrity_cb, [self.ring_integrity_lbl, self.ring_integrity_entry])
 
-                if exc_1_dict:
-                    objs.append(exc_1_dict)
-                elif exc_2_dict:
-                    objs.append(exc_2_dict)
-                else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
-                        _("Minimum Annular Ring"),
-                        _("Excellon object presence is mandatory for this rule but none is selected.")))
-                    return
+        self.form_layout_1.addRow(FCLabel(""))
 
-                self.results.append(self.pool.apply_async(self.check_gerber_annular_ring,
-                                                          args=(objs,
-                                                                ring_val,
-                                                                _("Minimum Annular Ring"))))
+        # Hole2Hole clearance
+        self.clearance_d2d_cb = FCCheckBox('%s:' % _("Hole to Hole Clearance"))
+        self.clearance_d2d_cb.setToolTip(
+            _("This checks if the minimum clearance between a drill hole\n"
+              "and another drill hole is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_d2d_cb)
 
-            # RULE: Check Hole to Hole Clearance
-            if self.clearance_d2d_cb.get_value():
-                exc_list = []
-                exc_name_1 = self.e1_object.currentText()
-                if exc_name_1 != '' and self.e1_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_1)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
-                    exc_list.append(elem_dict)
+        # Hole2Hole clearance value
+        self.clearance_d2d_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.clearance_d2d_entry.set_range(0.00001, 999.99999)
+        self.clearance_d2d_entry.set_precision(self.decimals)
+        self.clearance_d2d_entry.setSingleStep(0.1)
 
-                exc_name_2 = self.e2_object.currentText()
-                if exc_name_2 != '' and self.e2_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_2)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
-                    exc_list.append(elem_dict)
+        self.clearance_d2d_lbl = FCLabel('%s:' % _("Min value"))
+        self.clearance_d2d_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_d2d_lbl, self.clearance_d2d_entry)
 
-                hole_clearance = float(self.clearance_d2d_entry.get_value())
-                self.results.append(self.pool.apply_async(self.check_holes_clearance, args=(exc_list, hole_clearance)))
+        self.d2d = OptionalInputSection(
+            self.clearance_d2d_cb, [self.clearance_d2d_lbl, self.clearance_d2d_entry])
 
-            # RULE: Check Holes Size
-            if self.drill_size_cb.get_value():
-                exc_list = []
-                exc_name_1 = self.e1_object.currentText()
-                if exc_name_1 != '' and self.e1_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_1)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
-                    exc_list.append(elem_dict)
+        # Drill holes size check
+        self.drill_size_cb = FCCheckBox('%s:' % _("Hole Size"))
+        self.drill_size_cb.setToolTip(
+            _("This checks if the drill holes\n"
+              "sizes are above the threshold.")
+        )
+        self.form_layout_1.addRow(self.drill_size_cb)
 
-                exc_name_2 = self.e2_object.currentText()
-                if exc_name_2 != '' and self.e2_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_2)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
-                    exc_list.append(elem_dict)
+        # Drile holes value
+        self.drill_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.drill_size_entry.set_range(0.00001, 999.99999)
+        self.drill_size_entry.set_precision(self.decimals)
+        self.drill_size_entry.setSingleStep(0.1)
 
-                drill_size = float(self.drill_size_entry.get_value())
-                self.results.append(self.pool.apply_async(self.check_holes_size, args=(exc_list, drill_size)))
+        self.drill_size_lbl = FCLabel('%s:' % _("Min value"))
+        self.drill_size_lbl.setToolTip(
+            _("Minimum acceptable drill size.")
+        )
+        self.form_layout_1.addRow(self.drill_size_lbl, self.drill_size_entry)
 
-            output = []
-            for p in self.results:
-                output.append(p.get())
+        self.ds = OptionalInputSection(
+            self.drill_size_cb, [self.drill_size_lbl, self.drill_size_entry])
 
-            self.tool_finished.emit(output)
+        # Buttons
+        hlay_2 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay_2)
 
-            log.debug("RuleCheck() finished")
+        # hlay_2.addStretch()
+        self.run_button = FCButton(_("Run Rules Check"))
+        self.run_button.setToolTip(
+            _("Panelize the specified object around the specified box.\n"
+              "In other words it creates multiple copies of the source object,\n"
+              "arranged in a 2D array of rows and columns.")
+        )
+        self.run_button.setStyleSheet("""
+                                QPushButton
+                                {
+                                    font-weight: bold;
+                                }
+                                """)
+        hlay_2.addWidget(self.run_button)
 
-        self.app.worker_task.emit({'fcn': worker_job, 'params': [self.app]})
+        self.layout.addStretch()
 
-    def on_tool_finished(self, res):
-        def init(new_obj, app_obj):
-            txt = ''
-            for el in res:
-                txt += '<b>RULE NAME:</b>&nbsp;&nbsp;&nbsp;&nbsp;%s<BR>' % str(el[0]).upper()
-                if isinstance(el[1][0]['name'], list):
-                    for name in el[1][0]['name']:
-                        txt += 'File name: %s<BR>' % str(name)
-                else:
-                    txt += 'File name: %s<BR>' % str(el[1][0]['name'])
+        # ## Reset Tool
+        self.reset_button = FCButton(_("Reset Tool"))
+        self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
+        self.reset_button.setToolTip(
+            _("Will reset the tool parameters.")
+        )
+        self.reset_button.setStyleSheet("""
+                                QPushButton
+                                {
+                                    font-weight: bold;
+                                }
+                                """)
+        self.layout.addWidget(self.reset_button)
 
-                point_txt = ''
-                try:
-                    if el[1][0]['points']:
-                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
-                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
-                                                                     h_color='red',
-                                                                     color='white',
-                                                                     status=_("FAILED"))
-                        if 'Failed' in el[1][0]['points'][0]:
-                            point_txt = el[1][0]['points'][0]
-                        else:
-                            for pt in el[1][0]['points']:
-                                point_txt += '(%.*f, %.*f)' % (self.decimals, float(pt[0]), self.decimals, float(pt[1]))
-                                point_txt += ', '
-                        txt += 'Violations: %s<BR>' % str(point_txt)
-                    else:
-                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
-                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
-                                                                     h_color='green',
-                                                                     color='white',
-                                                                     status=_("PASSED"))
-                        txt += '%s<BR>' % _("Violations: There are no violations for the current rule.")
-                except KeyError:
-                    pass
+        # #################################### FINSIHED GUI ###########################
+        # #############################################################################
+    def on_all_cb_changed(self, state):
+        cb_items = [self.form_layout_1.itemAt(i).widget() for i in range(self.form_layout_1.count())
+                    if isinstance(self.form_layout_1.itemAt(i).widget(), FCCheckBox)]
 
-                try:
-                    if el[1][0]['dia']:
-                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
-                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
-                                                                     h_color='red',
-                                                                     color='white',
-                                                                     status=_("FAILED"))
-                        if 'Failed' in el[1][0]['dia']:
-                            point_txt = el[1][0]['dia']
-                        else:
-                            for pt in el[1][0]['dia']:
-                                point_txt += '%.*f' % (self.decimals, float(pt))
-                                point_txt += ', '
-                        txt += 'Violations: %s<BR>' % str(point_txt)
-                    else:
-                        txt += '{title}: <span style="color:{color};background-color:{h_color}"' \
-                               '>&nbsp;{status} </span>.<BR>'.format(title=_("STATUS"),
-                                                                     h_color='green',
-                                                                     color='white',
-                                                                     status=_("PASSED"))
-                        txt += '%s<BR>' % _("Violations: There are no violations for the current rule.")
-                except KeyError:
-                    pass
+        for cb in cb_items:
+            if state:
+                cb.setChecked(True)
+            else:
+                cb.setChecked(False)
 
-                txt += '<BR><BR>'
-            new_obj.source_file = txt
-            new_obj.read_only = True
+    def on_all_objects_cb_changed(self, state):
+        cb_items = [self.grid_layout.itemAt(i).widget() for i in range(self.grid_layout.count())
+                    if isinstance(self.grid_layout.itemAt(i).widget(), FCCheckBox)]
 
-        self.app.app_obj.new_object('document', name='Rules_check_results', initialize=init, plot=False)
+        for cb in cb_items:
+            if state:
+                cb.setChecked(True)
+            else:
+                cb.setChecked(False)
 
-    def reset_fields(self):
-        # self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        # self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        pass
+    def confirmation_message(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
+                                                                                  self.decimals,
+                                                                                  minval,
+                                                                                  self.decimals,
+                                                                                  maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+
+    def confirmation_message_int(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                            (_("Edited value is out of range"), minval, maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

+ 211 - 186
appTools/ToolSub.py

@@ -37,159 +37,17 @@ class ToolSub(AppTool):
     # meaning geometry that was deformed
     aperture_processing_finished = QtCore.pyqtSignal(str, list)
 
-    toolName = _("Subtract Tool")
-
     def __init__(self, app):
         self.app = app
         self.decimals = self.app.decimals
 
         AppTool.__init__(self, app)
 
-        self.tools_frame = QtWidgets.QFrame()
-        self.tools_frame.setContentsMargins(0, 0, 0, 0)
-        self.layout.addWidget(self.tools_frame)
-        self.tools_box = QtWidgets.QVBoxLayout()
-        self.tools_box.setContentsMargins(0, 0, 0, 0)
-        self.tools_frame.setLayout(self.tools_box)
-
-        # Title
-        title_label = QtWidgets.QLabel("%s" % self.toolName)
-        title_label.setStyleSheet("""
-                        QLabel
-                        {
-                            font-size: 16px;
-                            font-weight: bold;
-                        }
-                        """)
-        self.tools_box.addWidget(title_label)
-
-        # Form Layout
-        form_layout = QtWidgets.QFormLayout()
-        self.tools_box.addLayout(form_layout)
-
-        self.gerber_title = QtWidgets.QLabel("<b>%s</b>" % _("GERBER"))
-        form_layout.addRow(self.gerber_title)
-
-        # Target Gerber Object
-        self.target_gerber_combo = FCComboBox()
-        self.target_gerber_combo.setModel(self.app.collection)
-        self.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        # self.target_gerber_combo.setCurrentIndex(1)
-        self.target_gerber_combo.is_last = True
-        self.target_gerber_combo.obj_type = "Gerber"
-
-        self.target_gerber_label = QtWidgets.QLabel('%s:' % _("Target"))
-        self.target_gerber_label.setToolTip(
-            _("Gerber object from which to subtract\n"
-              "the subtractor Gerber object.")
-        )
-
-        form_layout.addRow(self.target_gerber_label, self.target_gerber_combo)
-
-        # Substractor Gerber Object
-        self.sub_gerber_combo = FCComboBox()
-        self.sub_gerber_combo.setModel(self.app.collection)
-        self.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.sub_gerber_combo.is_last = True
-        self.sub_gerber_combo.obj_type = "Gerber"
-
-        self.sub_gerber_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
-        self.sub_gerber_label.setToolTip(
-            _("Gerber object that will be subtracted\n"
-              "from the target Gerber object.")
-        )
-        e_lab_1 = QtWidgets.QLabel('')
-
-        form_layout.addRow(self.sub_gerber_label, self.sub_gerber_combo)
-
-        self.intersect_btn = FCButton(_('Subtract Gerber'))
-        self.intersect_btn.setToolTip(
-            _("Will remove the area occupied by the subtractor\n"
-              "Gerber from the Target Gerber.\n"
-              "Can be used to remove the overlapping silkscreen\n"
-              "over the soldermask.")
-        )
-        self.intersect_btn.setStyleSheet("""
-                        QPushButton
-                        {
-                            font-weight: bold;
-                        }
-                        """)
-        self.tools_box.addWidget(self.intersect_btn)
-        self.tools_box.addWidget(e_lab_1)
-
-        # Form Layout
-        form_geo_layout = QtWidgets.QFormLayout()
-        self.tools_box.addLayout(form_geo_layout)
-
-        self.geo_title = QtWidgets.QLabel("<b>%s</b>" % _("GEOMETRY"))
-        form_geo_layout.addRow(self.geo_title)
-
-        # Target Geometry Object
-        self.target_geo_combo = FCComboBox()
-        self.target_geo_combo.setModel(self.app.collection)
-        self.target_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
-        # self.target_geo_combo.setCurrentIndex(1)
-        self.target_geo_combo.is_last = True
-        self.target_geo_combo.obj_type = "Geometry"
-
-        self.target_geo_label = QtWidgets.QLabel('%s:' % _("Target"))
-        self.target_geo_label.setToolTip(
-            _("Geometry object from which to subtract\n"
-              "the subtractor Geometry object.")
-        )
-
-        form_geo_layout.addRow(self.target_geo_label, self.target_geo_combo)
-
-        # Substractor Geometry Object
-        self.sub_geo_combo = FCComboBox()
-        self.sub_geo_combo.setModel(self.app.collection)
-        self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
-        self.sub_geo_combo.is_last = True
-        self.sub_geo_combo.obj_type = "Geometry"
-
-        self.sub_geo_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
-        self.sub_geo_label.setToolTip(
-            _("Geometry object that will be subtracted\n"
-              "from the target Geometry object.")
-        )
-        e_lab_1 = QtWidgets.QLabel('')
-
-        form_geo_layout.addRow(self.sub_geo_label, self.sub_geo_combo)
-
-        self.close_paths_cb = FCCheckBox(_("Close paths"))
-        self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry subtractor object."))
-        self.tools_box.addWidget(self.close_paths_cb)
-
-        self.intersect_geo_btn = FCButton(_('Subtract Geometry'))
-        self.intersect_geo_btn.setToolTip(
-            _("Will remove the area occupied by the subtractor\n"
-              "Geometry from the Target Geometry.")
-        )
-        self.intersect_geo_btn.setStyleSheet("""
-                        QPushButton
-                        {
-                            font-weight: bold;
-                        }
-                        """)
-        self.tools_box.addWidget(self.intersect_geo_btn)
-        self.tools_box.addWidget(e_lab_1)
-
-        self.tools_box.addStretch()
-
-        # ## Reset Tool
-        self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
-        self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
-        self.reset_button.setToolTip(
-            _("Will reset the tool parameters.")
-        )
-        self.reset_button.setStyleSheet("""
-                        QPushButton
-                        {
-                            font-weight: bold;
-                        }
-                        """)
-        self.tools_box.addWidget(self.reset_button)
+        # #############################################################################
+        # ######################### Tool GUI ##########################################
+        # #############################################################################
+        self.ui = SubUI(layout=self.layout, app=self.app)
+        self.toolName = self.ui.toolName
 
         # QTimer for periodic check
         self.check_thread = QtCore.QTimer()
@@ -228,11 +86,14 @@ class ToolSub(AppTool):
         self.pool = self.app.pool
         self.results = []
 
-        self.intersect_btn.clicked.connect(self.on_grb_intersection_click)
-        self.intersect_geo_btn.clicked.connect(self.on_geo_intersection_click)
+        # Signals
+        self.ui.intersect_btn.clicked.connect(self.on_grb_intersection_click)
+        self.ui.intersect_geo_btn.clicked.connect(self.on_geo_intersection_click)
+        self.ui.reset_button.clicked.connect(self.set_tool_ui)
+
+        # Custom Signals
         self.job_finished.connect(self.on_job_finished)
         self.aperture_processing_finished.connect(self.new_gerber_object)
-        self.reset_button.clicked.connect(self.set_tool_ui)
 
     def install(self, icon=None, separator=None, **kwargs):
         AppTool.install(self, icon, separator, shortcut='Alt+W', **kwargs)
@@ -270,8 +131,8 @@ class ToolSub(AppTool):
         self.new_solid_geometry = []
         self.target_options.clear()
 
-        self.tools_frame.show()
-        self.close_paths_cb.setChecked(self.app.defaults["tools_sub_close_paths"])
+        self.ui.tools_frame.show()
+        self.ui.close_paths_cb.setChecked(self.app.defaults["tools_sub_close_paths"])
 
     def on_grb_intersection_click(self):
         # reset previous values
@@ -281,7 +142,7 @@ class ToolSub(AppTool):
 
         self.sub_type = "gerber"
 
-        self.target_grb_obj_name = self.target_gerber_combo.currentText()
+        self.target_grb_obj_name = self.ui.target_gerber_combo.currentText()
         if self.target_grb_obj_name == '':
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("No Target object loaded."))
             return
@@ -296,7 +157,7 @@ class ToolSub(AppTool):
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.obj_name))
             return "Could not retrieve object: %s" % self.target_grb_obj_name
 
-        self.sub_grb_obj_name = self.sub_gerber_combo.currentText()
+        self.sub_grb_obj_name = self.ui.sub_gerber_combo.currentText()
         if self.sub_grb_obj_name == '':
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("No Subtractor object loaded."))
             return
@@ -485,10 +346,9 @@ class ToolSub(AppTool):
 
         self.sub_type = "geo"
 
-        self.target_geo_obj_name = self.target_geo_combo.currentText()
+        self.target_geo_obj_name = self.ui.target_geo_combo.currentText()
         if self.target_geo_obj_name == '':
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("No Target object loaded."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("No Target object loaded."))
             return
 
         # Get target object.
@@ -496,14 +356,12 @@ class ToolSub(AppTool):
             self.target_geo_obj = self.app.collection.get_by_name(self.target_geo_obj_name)
         except Exception as e:
             log.debug("ToolSub.on_geo_intersection_click() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                 (_("Could not retrieve object"), self.target_geo_obj_name))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.target_geo_obj_name))
             return "Could not retrieve object: %s" % self.target_grb_obj_name
 
-        self.sub_geo_obj_name = self.sub_geo_combo.currentText()
+        self.sub_geo_obj_name = self.ui.sub_geo_combo.currentText()
         if self.sub_geo_obj_name == '':
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("No Subtractor object loaded."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("No Subtractor object loaded."))
             return
 
         # Get substractor object.
@@ -511,8 +369,7 @@ class ToolSub(AppTool):
             self.sub_geo_obj = self.app.collection.get_by_name(self.sub_geo_obj_name)
         except Exception as e:
             log.debug("ToolSub.on_geo_intersection_click() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                 (_("Could not retrieve object"), self.sub_geo_obj_name))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.sub_geo_obj_name))
             return "Could not retrieve object: %s" % self.sub_geo_obj_name
 
         if self.sub_geo_obj.multigeo:
@@ -529,11 +386,8 @@ class ToolSub(AppTool):
         # crate the new_tools dict structure
         for tool in self.target_geo_obj.tools:
             self.new_tools[tool] = {}
-            for key in self.target_geo_obj.tools[tool]:
-                if key == 'solid_geometry':
-                    self.new_tools[tool][key] = []
-                else:
-                    self.new_tools[tool][key] = deepcopy(self.target_geo_obj.tools[tool][key])
+            for key, v in self.target_geo_obj.tools[tool]:
+                self.new_tools[tool][key] = [] if key == 'solid_geometry' else deepcopy(v)
 
         # add the promises
         if self.target_geo_obj.multigeo:
@@ -550,12 +404,10 @@ class ToolSub(AppTool):
         if self.target_geo_obj.multigeo:
             for tool in self.target_geo_obj.tools:
                 geo = self.target_geo_obj.tools[tool]['solid_geometry']
-                self.app.worker_task.emit({'fcn': self.toolgeo_intersection,
-                                           'params': [tool, geo]})
+                self.app.worker_task.emit({'fcn': self.toolgeo_intersection, 'params': [tool, geo]})
         else:
             geo = self.target_geo_obj.solid_geometry
-            self.app.worker_task.emit({'fcn': self.toolgeo_intersection,
-                                       'params': ["single", geo]})
+            self.app.worker_task.emit({'fcn': self.toolgeo_intersection, 'params': ["single", geo]})
 
     def toolgeo_intersection(self, tool, geo):
         new_geometry = []
@@ -568,7 +420,7 @@ class ToolSub(AppTool):
 
         with self.app.proc_container.new(text):
             # resulting paths are closed resulting into Polygons
-            if self.close_paths_cb.isChecked():
+            if self.ui.close_paths_cb.isChecked():
                 new_geo = (cascaded_union(geo)).difference(self.sub_union)
                 if new_geo:
                     if not new_geo.is_empty:
@@ -663,14 +515,12 @@ class ToolSub(AppTool):
         with self.app.proc_container.new(_("Generating new object ...")):
             ret = self.app.app_obj.new_object('geometry', outname, obj_init, autoselected=False)
             if ret == 'fail':
-                self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                     _('Generating new object failed.'))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _('Generating new object failed.'))
                 return
             # Register recent file
             self.app.file_opened.emit('geometry', outname)
             # GUI feedback
-            self.app.inform.emit('[success] %s: %s' %
-                                 (_("Created"), outname))
+            self.app.inform.emit('[success] %s: %s' % (_("Created"), outname))
 
             # cleanup
             self.new_tools.clear()
@@ -732,13 +582,12 @@ class ToolSub(AppTool):
         """
         if succcess is True:
             if self.sub_type == "gerber":
-                outname = self.target_gerber_combo.currentText() + '_sub'
+                outname = self.ui.target_gerber_combo.currentText() + '_sub'
 
                 # intersection jobs finished, start the creation of solid_geometry
-                self.app.worker_task.emit({'fcn': self.new_gerber_object,
-                                           'params': [outname]})
+                self.app.worker_task.emit({'fcn': self.new_gerber_object, 'params': [outname]})
             else:
-                outname = self.target_geo_combo.currentText() + '_sub'
+                outname = self.ui.target_geo_combo.currentText() + '_sub'
 
                 # intersection jobs finished, start the creation of solid_geometry
                 self.app.worker_task.emit({'fcn': self.new_geo_object, 'params': [outname]})
@@ -746,13 +595,189 @@ class ToolSub(AppTool):
             self.app.inform.emit('[ERROR_NOTCL] %s' % _('Generating new object failed.'))
 
     def reset_fields(self):
+        self.ui.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.ui.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+
+        self.ui.target_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
+        self.ui.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
+
+    @staticmethod
+    def poly2rings(poly):
+        return [poly.exterior] + [interior for interior in poly.interiors]
+
+
+class SubUI:
+
+    toolName = _("Subtract Tool")
+
+    def __init__(self, layout, app):
+        self.app = app
+        self.decimals = self.app.decimals
+        self.layout = layout
+
+        # ## Title
+        title_label = QtWidgets.QLabel("%s" % self.toolName)
+        title_label.setStyleSheet("""
+                                QLabel
+                                {
+                                    font-size: 16px;
+                                    font-weight: bold;
+                                }
+                                """)
+        self.layout.addWidget(title_label)
+        self.layout.addWidget(QtWidgets.QLabel(""))
+
+        self.tools_frame = QtWidgets.QFrame()
+        self.tools_frame.setContentsMargins(0, 0, 0, 0)
+        self.layout.addWidget(self.tools_frame)
+        self.tools_box = QtWidgets.QVBoxLayout()
+        self.tools_box.setContentsMargins(0, 0, 0, 0)
+        self.tools_frame.setLayout(self.tools_box)
+
+        # Form Layout
+        form_layout = QtWidgets.QFormLayout()
+        self.tools_box.addLayout(form_layout)
+
+        self.gerber_title = QtWidgets.QLabel("<b>%s</b>" % _("GERBER"))
+        form_layout.addRow(self.gerber_title)
+
+        # Target Gerber Object
+        self.target_gerber_combo = FCComboBox()
+        self.target_gerber_combo.setModel(self.app.collection)
         self.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        # self.target_gerber_combo.setCurrentIndex(1)
+        self.target_gerber_combo.is_last = True
+        self.target_gerber_combo.obj_type = "Gerber"
+
+        self.target_gerber_label = QtWidgets.QLabel('%s:' % _("Target"))
+        self.target_gerber_label.setToolTip(
+            _("Gerber object from which to subtract\n"
+              "the subtractor Gerber object.")
+        )
+
+        form_layout.addRow(self.target_gerber_label, self.target_gerber_combo)
+
+        # Substractor Gerber Object
+        self.sub_gerber_combo = FCComboBox()
+        self.sub_gerber_combo.setModel(self.app.collection)
         self.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.sub_gerber_combo.is_last = True
+        self.sub_gerber_combo.obj_type = "Gerber"
+
+        self.sub_gerber_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
+        self.sub_gerber_label.setToolTip(
+            _("Gerber object that will be subtracted\n"
+              "from the target Gerber object.")
+        )
+        e_lab_1 = QtWidgets.QLabel('')
+
+        form_layout.addRow(self.sub_gerber_label, self.sub_gerber_combo)
+
+        self.intersect_btn = FCButton(_('Subtract Gerber'))
+        self.intersect_btn.setToolTip(
+            _("Will remove the area occupied by the subtractor\n"
+              "Gerber from the Target Gerber.\n"
+              "Can be used to remove the overlapping silkscreen\n"
+              "over the soldermask.")
+        )
+        self.intersect_btn.setStyleSheet("""
+                                QPushButton
+                                {
+                                    font-weight: bold;
+                                }
+                                """)
+        self.tools_box.addWidget(self.intersect_btn)
+        self.tools_box.addWidget(e_lab_1)
 
+        # Form Layout
+        form_geo_layout = QtWidgets.QFormLayout()
+        self.tools_box.addLayout(form_geo_layout)
+
+        self.geo_title = QtWidgets.QLabel("<b>%s</b>" % _("GEOMETRY"))
+        form_geo_layout.addRow(self.geo_title)
+
+        # Target Geometry Object
+        self.target_geo_combo = FCComboBox()
+        self.target_geo_combo.setModel(self.app.collection)
         self.target_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
+        # self.target_geo_combo.setCurrentIndex(1)
+        self.target_geo_combo.is_last = True
+        self.target_geo_combo.obj_type = "Geometry"
+
+        self.target_geo_label = QtWidgets.QLabel('%s:' % _("Target"))
+        self.target_geo_label.setToolTip(
+            _("Geometry object from which to subtract\n"
+              "the subtractor Geometry object.")
+        )
+
+        form_geo_layout.addRow(self.target_geo_label, self.target_geo_combo)
+
+        # Substractor Geometry Object
+        self.sub_geo_combo = FCComboBox()
+        self.sub_geo_combo.setModel(self.app.collection)
         self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
+        self.sub_geo_combo.is_last = True
+        self.sub_geo_combo.obj_type = "Geometry"
 
-    @staticmethod
-    def poly2rings(poly):
-        return [poly.exterior] + [interior for interior in poly.interiors]
-# end of file
+        self.sub_geo_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
+        self.sub_geo_label.setToolTip(
+            _("Geometry object that will be subtracted\n"
+              "from the target Geometry object.")
+        )
+        e_lab_1 = QtWidgets.QLabel('')
+
+        form_geo_layout.addRow(self.sub_geo_label, self.sub_geo_combo)
+
+        self.close_paths_cb = FCCheckBox(_("Close paths"))
+        self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry subtractor object."))
+        self.tools_box.addWidget(self.close_paths_cb)
+
+        self.intersect_geo_btn = FCButton(_('Subtract Geometry'))
+        self.intersect_geo_btn.setToolTip(
+            _("Will remove the area occupied by the subtractor\n"
+              "Geometry from the Target Geometry.")
+        )
+        self.intersect_geo_btn.setStyleSheet("""
+                                QPushButton
+                                {
+                                    font-weight: bold;
+                                }
+                                """)
+        self.tools_box.addWidget(self.intersect_geo_btn)
+        self.tools_box.addWidget(e_lab_1)
+
+        self.tools_box.addStretch()
+
+        # ## Reset Tool
+        self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
+        self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
+        self.reset_button.setToolTip(
+            _("Will reset the tool parameters.")
+        )
+        self.reset_button.setStyleSheet("""
+                                QPushButton
+                                {
+                                    font-weight: bold;
+                                }
+                                """)
+        self.tools_box.addWidget(self.reset_button)
+
+        # #################################### FINSIHED GUI ###########################
+        # #############################################################################
+
+    def confirmation_message(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
+                                                                                  self.decimals,
+                                                                                  minval,
+                                                                                  self.decimals,
+                                                                                  maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+
+    def confirmation_message_int(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                            (_("Edited value is out of range"), minval, maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

+ 793 - 763
appTools/ToolTransform.py

@@ -7,8 +7,8 @@
 
 from PyQt5 import QtWidgets, QtGui, QtCore
 from appTool import AppTool
-from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, FCEntry, FCComboBox, \
-    NumericalEvalTupleEntry
+from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, FCComboBox, \
+    NumericalEvalTupleEntry, FCLabel
 
 import numpy as np
 
@@ -23,925 +23,955 @@ if '_' not in builtins.__dict__:
 
 class ToolTransform(AppTool):
 
-    toolName = _("Object Transform")
-    rotateName = _("Rotate")
-    skewName = _("Skew/Shear")
-    scaleName = _("Scale")
-    flipName = _("Mirror (Flip)")
-    offsetName = _("Offset")
-    bufferName = _("Buffer")
-
     def __init__(self, app):
         AppTool.__init__(self, app)
         self.decimals = self.app.decimals
 
-        # ## Title
-        title_label = QtWidgets.QLabel("%s" % self.toolName)
-        title_label.setStyleSheet("""
-                        QLabel
-                        {
-                            font-size: 16px;
-                            font-weight: bold;
-                        }
-                        """)
-        self.layout.addWidget(title_label)
-        self.layout.addWidget(QtWidgets.QLabel(''))
+        # #############################################################################
+        # ######################### Tool GUI ##########################################
+        # #############################################################################
+        self.ui = TransformUI(layout=self.layout, app=self.app)
+        self.toolName = self.ui.toolName
+        
+        # ## Signals
+        self.ui.ref_combo.currentIndexChanged.connect(self.ui.on_reference_changed)
+        self.ui.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
+        self.ui.point_button.clicked.connect(self.on_add_coords)
 
-        # ## Layout
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-        grid0.setColumnStretch(2, 0)
+        self.ui.rotate_button.clicked.connect(self.on_rotate)
 
-        grid0.addWidget(QtWidgets.QLabel(''))
+        self.ui.skewx_button.clicked.connect(self.on_skewx)
+        self.ui.skewy_button.clicked.connect(self.on_skewy)
 
-        # Reference
-        ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
-        ref_label.setToolTip(
-            _("The reference point for Rotate, Skew, Scale, Mirror.\n"
-              "Can be:\n"
-              "- Origin -> it is the 0, 0 point\n"
-              "- Selection -> the center of the bounding box of the selected objects\n"
-              "- Point -> a custom point defined by X,Y coordinates\n"
-              "- Object -> the center of the bounding box of a specific object")
-        )
-        self.ref_combo = FCComboBox()
-        self.ref_items = [_("Origin"), _("Selection"), _("Point"), _("Object")]
-        self.ref_combo.addItems(self.ref_items)
+        self.ui.scalex_button.clicked.connect(self.on_scalex)
+        self.ui.scaley_button.clicked.connect(self.on_scaley)
 
-        grid0.addWidget(ref_label, 0, 0)
-        grid0.addWidget(self.ref_combo, 0, 1, 1, 2)
+        self.ui.offx_button.clicked.connect(self.on_offx)
+        self.ui.offy_button.clicked.connect(self.on_offy)
 
-        self.point_label = QtWidgets.QLabel('%s:' % _("Value"))
-        self.point_label.setToolTip(
-            _("A point of reference in format X,Y.")
-        )
-        self.point_entry = NumericalEvalTupleEntry()
+        self.ui.flipx_button.clicked.connect(self.on_flipx)
+        self.ui.flipy_button.clicked.connect(self.on_flipy)
 
-        grid0.addWidget(self.point_label, 1, 0)
-        grid0.addWidget(self.point_entry, 1, 1, 1, 2)
+        self.ui.buffer_button.clicked.connect(self.on_buffer_by_distance)
+        self.ui.buffer_factor_button.clicked.connect(self.on_buffer_by_factor)
 
-        self.point_button = FCButton(_("Add"))
-        self.point_button.setToolTip(
-            _("Add point coordinates from clipboard.")
-        )
-        grid0.addWidget(self.point_button, 2, 0, 1, 3)
+        self.ui.reset_button.clicked.connect(self.set_tool_ui)
 
-        # Type of object to be used as reference
-        self.type_object_label = QtWidgets.QLabel('%s:' % _("Type"))
-        self.type_object_label.setToolTip(
-            _("The type of object used as reference.")
-        )
+    def run(self, toggle=True):
+        self.app.defaults.report_usage("ToolTransform()")
 
-        self.type_obj_combo = FCComboBox()
-        self.type_obj_combo.addItem(_("Gerber"))
-        self.type_obj_combo.addItem(_("Excellon"))
-        self.type_obj_combo.addItem(_("Geometry"))
+        if toggle:
+            # if the splitter is hidden, display it, else hide it but only if the current widget is the same
+            if self.app.ui.splitter.sizes()[0] == 0:
+                self.app.ui.splitter.setSizes([1, 1])
+            else:
+                try:
+                    if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
+                        # if tab is populated with the tool but it does not have the focus, focus on it
+                        if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
+                            # focus on Tool Tab
+                            self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
+                        else:
+                            self.app.ui.splitter.setSizes([0, 1])
+                except AttributeError:
+                    pass
+        else:
+            if self.app.ui.splitter.sizes()[0] == 0:
+                self.app.ui.splitter.setSizes([1, 1])
 
-        self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
-        self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
-        self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
+        AppTool.run(self)
+        self.set_tool_ui()
 
-        grid0.addWidget(self.type_object_label, 3, 0)
-        grid0.addWidget(self.type_obj_combo, 3, 1, 1, 2)
+        self.app.ui.notebook.setTabText(2, _("Transform Tool"))
 
-        # Object to be used as reference
-        self.object_combo = FCComboBox()
-        self.object_combo.setModel(self.app.collection)
-        self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
-        self.object_combo.is_last = True
+    def install(self, icon=None, separator=None, **kwargs):
+        AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
 
-        self.object_combo.setToolTip(
-            _("The object used as reference.\n"
-              "The used point is the center of it's bounding box.")
-        )
-        grid0.addWidget(self.object_combo, 4, 0, 1, 3)
+    def set_tool_ui(self):
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 5, 0, 1, 3)
+        # ## Initialize form
+        self.ui.ref_combo.set_value(self.app.defaults["tools_transform_reference"])
+        self.ui.type_obj_combo.set_value(self.app.defaults["tools_transform_ref_object"])
+        self.ui.point_entry.set_value(self.app.defaults["tools_transform_ref_point"])
+        self.ui.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])
 
-        # ## Rotate Title
-        rotate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.rotateName)
-        grid0.addWidget(rotate_title_label, 6, 0, 1, 3)
+        self.ui.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"])
+        self.ui.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"])
+        self.ui.skew_link_cb.set_value(self.app.defaults["tools_transform_skew_link"])
 
-        self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
-        self.rotate_label.setToolTip(
-            _("Angle for Rotation action, in degrees.\n"
-              "Float number between -360 and 359.\n"
-              "Positive numbers for CW motion.\n"
-              "Negative numbers for CCW motion.")
-        )
+        self.ui.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"])
+        self.ui.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"])
+        self.ui.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"])
 
-        self.rotate_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.rotate_entry.set_precision(self.decimals)
-        self.rotate_entry.setSingleStep(45)
-        self.rotate_entry.setWrapping(True)
-        self.rotate_entry.set_range(-360, 360)
+        self.ui.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"])
+        self.ui.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"])
 
-        # self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.ui.buffer_entry.set_value(self.app.defaults["tools_transform_buffer_dis"])
+        self.ui.buffer_factor_entry.set_value(self.app.defaults["tools_transform_buffer_factor"])
+        self.ui.buffer_rounded_cb.set_value(self.app.defaults["tools_transform_buffer_corner"])
 
-        self.rotate_button = FCButton(_("Rotate"))
-        self.rotate_button.setToolTip(
-            _("Rotate the selected object(s).\n"
-              "The point of reference is the middle of\n"
-              "the bounding box for all selected objects.")
-        )
-        self.rotate_button.setMinimumWidth(90)
+        # initial state is hidden
+        self.ui.point_label.hide()
+        self.ui.point_entry.hide()
+        self.ui.point_button.hide()
 
-        grid0.addWidget(self.rotate_label, 7, 0)
-        grid0.addWidget(self.rotate_entry, 7, 1)
-        grid0.addWidget(self.rotate_button, 7, 2)
+        self.ui.type_object_label.hide()
+        self.ui.type_obj_combo.hide()
+        self.ui.object_combo.hide()
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 8, 0, 1, 3)
+    def on_type_obj_index_changed(self, index):
+        self.ui.object_combo.setRootModelIndex(self.app.collection.index(index, 0, QtCore.QModelIndex()))
+        self.ui.object_combo.setCurrentIndex(0)
+        self.ui.object_combo.obj_type = {
+            _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
+        }[self.ui.type_obj_combo.get_value()]
 
-        # ## Skew Title
-        skew_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.skewName)
-        grid0.addWidget(skew_title_label, 9, 0, 1, 2)
+    def on_calculate_reference(self):
+        ref_val = self.ui.ref_combo.currentIndex()
 
-        self.skew_link_cb = FCCheckBox()
-        self.skew_link_cb.setText(_("Link"))
-        self.skew_link_cb.setToolTip(
-            _("Link the Y entry to X entry and copy its content.")
-        )
+        if ref_val == 0:    # "Origin" reference
+            return 0, 0
+        elif ref_val == 1:  # "Selection" reference
+            sel_list = self.app.collection.get_selected()
+            if sel_list:
+                xmin, ymin, xmax, ymax = self.alt_bounds(obj_list=sel_list)
+                px = (xmax + xmin) * 0.5
+                py = (ymax + ymin) * 0.5
+                return px, py
+            else:
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
+                return "fail"
+        elif ref_val == 2:  # "Point" reference
+            point_val = self.uipoint_entry.get_value()
+            try:
+                px, py = eval('{}'.format(point_val))
+                return px, py
+            except Exception:
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("Incorrect format for Point value. Needs format X,Y"))
+                return "fail"
+        else:               # "Object" reference
+            obj_name = self.ui.object_combo.get_value()
+            ref_obj = self.app.collection.get_by_name(obj_name)
+            xmin, ymin, xmax, ymax = ref_obj.bounds()
+            px = (xmax + xmin) * 0.5
+            py = (ymax + ymin) * 0.5
+            return px, py
 
-        grid0.addWidget(self.skew_link_cb, 9, 2)
+    def on_add_coords(self):
+        val = self.app.clipboard.text()
+        self.ui.point_entry.set_value(val)
 
-        self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
-        self.skewx_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
-              "Float number between -360 and 360.")
-        )
-        self.skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.skewx_entry.set_precision(self.decimals)
-        self.skewx_entry.set_range(-360, 360)
+    def on_rotate(self):
+        value = float(self.ui.rotate_entry.get_value())
+        if value == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Rotate transformation can not be done for a value of 0."))
+            return
+        point = self.on_calculate_reference()
+        if point == 'fail':
+            return
+        self.app.worker_task.emit({'fcn': self.on_rotate_action, 'params': [value, point]})
 
-        self.skewx_button = FCButton(_("Skew X"))
-        self.skewx_button.setToolTip(
-            _("Skew/shear the selected object(s).\n"
-              "The point of reference is the middle of\n"
-              "the bounding box for all selected objects."))
-        self.skewx_button.setMinimumWidth(90)
+    def on_flipx(self):
+        axis = 'Y'
+        point = self.on_calculate_reference()
+        if point == 'fail':
+            return
+        self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis, point]})
 
-        grid0.addWidget(self.skewx_label, 10, 0)
-        grid0.addWidget(self.skewx_entry, 10, 1)
-        grid0.addWidget(self.skewx_button, 10, 2)
+    def on_flipy(self):
+        axis = 'X'
+        point = self.on_calculate_reference()
+        if point == 'fail':
+            return
+        self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis, point]})
 
-        self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
-        self.skewy_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
-              "Float number between -360 and 360.")
-        )
-        self.skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.skewy_entry.set_precision(self.decimals)
-        self.skewy_entry.set_range(-360, 360)
+    def on_skewx(self):
+        xvalue = float(self.ui.skewx_entry.get_value())
 
-        self.skewy_button = FCButton(_("Skew Y"))
-        self.skewy_button.setToolTip(
-            _("Skew/shear the selected object(s).\n"
-              "The point of reference is the middle of\n"
-              "the bounding box for all selected objects."))
-        self.skewy_button.setMinimumWidth(90)
+        if xvalue == 0:
+            return
 
-        grid0.addWidget(self.skewy_label, 12, 0)
-        grid0.addWidget(self.skewy_entry, 12, 1)
-        grid0.addWidget(self.skewy_button, 12, 2)
+        if self.ui.skew_link_cb.get_value():
+            yvalue = xvalue
+        else:
+            yvalue = 0
 
-        self.ois_sk = OptionalInputSection(self.skew_link_cb, [self.skewy_label, self.skewy_entry, self.skewy_button],
-                                           logic=False)
+        axis = 'X'
+        point = self.on_calculate_reference()
+        if point == 'fail':
+            return
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 14, 0, 1, 3)
+        self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, xvalue, yvalue, point]})
 
-        # ## Scale Title
-        scale_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.scaleName)
-        grid0.addWidget(scale_title_label, 15, 0, 1, 2)
+    def on_skewy(self):
+        xvalue = 0
+        yvalue = float(self.ui.skewy_entry.get_value())
 
-        self.scale_link_cb = FCCheckBox()
-        self.scale_link_cb.setText(_("Link"))
-        self.scale_link_cb.setToolTip(
-            _("Link the Y entry to X entry and copy its content.")
-        )
+        if yvalue == 0:
+            return
 
-        grid0.addWidget(self.scale_link_cb, 15, 2)
+        axis = 'Y'
+        point = self.on_calculate_reference()
+        if point == 'fail':
+            return
 
-        self.scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
-        self.scalex_label.setToolTip(
-            _("Factor for scaling on X axis.")
-        )
-        self.scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.scalex_entry.set_precision(self.decimals)
-        self.scalex_entry.setMinimum(-1e6)
+        self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, xvalue, yvalue, point]})
 
-        self.scalex_button = FCButton(_("Scale X"))
-        self.scalex_button.setToolTip(
-            _("Scale the selected object(s).\n"
-              "The point of reference depends on \n"
-              "the Scale reference checkbox state."))
-        self.scalex_button.setMinimumWidth(90)
+    def on_scalex(self):
+        xvalue = float(self.ui.scalex_entry.get_value())
 
-        grid0.addWidget(self.scalex_label, 17, 0)
-        grid0.addWidget(self.scalex_entry, 17, 1)
-        grid0.addWidget(self.scalex_button, 17, 2)
+        if xvalue == 0 or xvalue == 1:
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Scale transformation can not be done for a factor of 0 or 1."))
+            return
 
-        self.scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
-        self.scaley_label.setToolTip(
-            _("Factor for scaling on Y axis.")
-        )
-        self.scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.scaley_entry.set_precision(self.decimals)
-        self.scaley_entry.setMinimum(-1e6)
+        if self.ui.scale_link_cb.get_value():
+            yvalue = xvalue
+        else:
+            yvalue = 1
 
-        self.scaley_button = FCButton(_("Scale Y"))
-        self.scaley_button.setToolTip(
-            _("Scale the selected object(s).\n"
-              "The point of reference depends on \n"
-              "the Scale reference checkbox state."))
-        self.scaley_button.setMinimumWidth(90)
+        axis = 'X'
+        point = self.on_calculate_reference()
+        if point == 'fail':
+            return
 
-        grid0.addWidget(self.scaley_label, 19, 0)
-        grid0.addWidget(self.scaley_entry, 19, 1)
-        grid0.addWidget(self.scaley_button, 19, 2)
+        self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
 
-        self.ois_s = OptionalInputSection(self.scale_link_cb,
-                                          [
-                                              self.scaley_label,
-                                              self.scaley_entry,
-                                              self.scaley_button
-                                          ], logic=False)
+    def on_scaley(self):
+        xvalue = 1
+        yvalue = float(self.ui.scaley_entry.get_value())
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 21, 0, 1, 3)
+        if yvalue == 0 or yvalue == 1:
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Scale transformation can not be done for a factor of 0 or 1."))
+            return
 
-        # ## Flip Title
-        flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
-        grid0.addWidget(flip_title_label, 23, 0, 1, 3)
+        axis = 'Y'
+        point = self.on_calculate_reference()
+        if point == 'fail':
+            return
 
-        self.flipx_button = FCButton(_("Flip on X"))
-        self.flipx_button.setToolTip(
-            _("Flip the selected object(s) over the X axis.")
-        )
+        self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
 
-        self.flipy_button = FCButton(_("Flip on Y"))
-        self.flipy_button.setToolTip(
-            _("Flip the selected object(s) over the X axis.")
-        )
+    def on_offx(self):
+        value = float(self.ui.offx_entry.get_value())
+        if value == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
+            return
+        axis = 'X'
 
-        hlay0 = QtWidgets.QHBoxLayout()
-        grid0.addLayout(hlay0, 25, 0, 1, 3)
+        self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
 
-        hlay0.addWidget(self.flipx_button)
-        hlay0.addWidget(self.flipy_button)
+    def on_offy(self):
+        value = float(self.ui.offy_entry.get_value())
+        if value == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
+            return
+        axis = 'Y'
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 27, 0, 1, 3)
+        self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
 
-        # ## Offset Title
-        offset_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.offsetName)
-        grid0.addWidget(offset_title_label, 29, 0, 1, 3)
+    def on_buffer_by_distance(self):
+        value = self.ui.buffer_entry.get_value()
+        join = 1 if self.ui.buffer_rounded_cb.get_value() else 2
 
-        self.offx_label = QtWidgets.QLabel('%s:' % _("X val"))
-        self.offx_label.setToolTip(
-            _("Distance to offset on X axis. In current units.")
-        )
-        self.offx_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.offx_entry.set_precision(self.decimals)
-        self.offx_entry.setMinimum(-1e6)
+        self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join]})
 
-        self.offx_button = FCButton(_("Offset X"))
-        self.offx_button.setToolTip(
-            _("Offset the selected object(s).\n"
-              "The point of reference is the middle of\n"
-              "the bounding box for all selected objects.\n"))
-        self.offx_button.setMinimumWidth(90)
+    def on_buffer_by_factor(self):
+        value = 1 + self.ui.buffer_factor_entry.get_value() / 100.0
+        join = 1 if self.ui.buffer_rounded_cb.get_value() else 2
 
-        grid0.addWidget(self.offx_label, 31, 0)
-        grid0.addWidget(self.offx_entry, 31, 1)
-        grid0.addWidget(self.offx_button, 31, 2)
+        # tell the buffer method to use the factor
+        factor = True
 
-        self.offy_label = QtWidgets.QLabel('%s:' % _("Y val"))
-        self.offy_label.setToolTip(
-            _("Distance to offset on Y axis. In current units.")
-        )
-        self.offy_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.offy_entry.set_precision(self.decimals)
-        self.offy_entry.setMinimum(-1e6)
+        self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join, factor]})
 
-        self.offy_button = FCButton(_("Offset Y"))
-        self.offy_button.setToolTip(
-            _("Offset the selected object(s).\n"
-              "The point of reference is the middle of\n"
-              "the bounding box for all selected objects.\n"))
-        self.offy_button.setMinimumWidth(90)
+    def on_rotate_action(self, num, point):
+        obj_list = self.app.collection.get_selected()
 
-        grid0.addWidget(self.offy_label, 32, 0)
-        grid0.addWidget(self.offy_entry, 32, 1)
-        grid0.addWidget(self.offy_button, 32, 2)
+        if not obj_list:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to rotate!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Appying Rotate")):
+                try:
+                    px, py = point
+                    for sel_obj in obj_list:
+                        if sel_obj.kind == 'cncjob':
+                            self.app.inform.emit(_("CNCJob objects can't be rotated."))
+                        else:
+                            sel_obj.rotate(-num, point=(px, py))
+                            self.app.app_obj.object_changed.emit(sel_obj)
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 34, 0, 1, 3)
+                        # add information to the object that it was changed and how much
+                        sel_obj.options['rotate'] = num
+                        sel_obj.plot()
+                    self.app.inform.emit('[success] %s...' % _('Rotate done'))
+                except Exception as e:
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
+                    return
 
-        # ## Buffer Title
-        buffer_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.bufferName)
-        grid0.addWidget(buffer_title_label, 35, 0, 1, 2)
+    def on_flip(self, axis, point):
+        obj_list = self.app.collection.get_selected()
 
-        self.buffer_rounded_cb = FCCheckBox('%s' % _("Rounded"))
-        self.buffer_rounded_cb.setToolTip(
-            _("If checked then the buffer will surround the buffered shape,\n"
-              "every corner will be rounded.\n"
-              "If not checked then the buffer will follow the exact geometry\n"
-              "of the buffered shape.")
-        )
+        if not obj_list:
+            self.app.inform.emit('[WARNING_NOTCL] %s!' % _("No object selected. Please Select an object to flip"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Flip")):
+                try:
+                    px, py = point
 
-        grid0.addWidget(self.buffer_rounded_cb, 35, 2)
+                    # execute mirroring
+                    for sel_obj in obj_list:
+                        if sel_obj.kind == 'cncjob':
+                            self.app.inform.emit(_("CNCJob objects can't be mirrored/flipped."))
+                        else:
+                            if axis == 'X':
+                                sel_obj.mirror('X', (px, py))
+                                # add information to the object that it was changed and how much
+                                # the axis is reversed because of the reference
+                                if 'mirror_y' in sel_obj.options:
+                                    sel_obj.options['mirror_y'] = not sel_obj.options['mirror_y']
+                                else:
+                                    sel_obj.options['mirror_y'] = True
+                                self.app.inform.emit('[success] %s...' % _('Flip on the Y axis done'))
+                            elif axis == 'Y':
+                                sel_obj.mirror('Y', (px, py))
+                                # add information to the object that it was changed and how much
+                                # the axis is reversed because of the reference
+                                if 'mirror_x' in sel_obj.options:
+                                    sel_obj.options['mirror_x'] = not sel_obj.options['mirror_x']
+                                else:
+                                    sel_obj.options['mirror_x'] = True
+                                self.app.inform.emit('[success] %s...' % _('Flip on the X axis done'))
+                            self.app.app_obj.object_changed.emit(sel_obj)
+                        sel_obj.plot()
+                except Exception as e:
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
+                    return
 
-        self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
-        self.buffer_label.setToolTip(
-            _("A positive value will create the effect of dilation,\n"
-              "while a negative value will create the effect of erosion.\n"
-              "Each geometry element of the object will be increased\n"
-              "or decreased with the 'distance'.")
-        )
+    def on_skew(self, axis, xvalue, yvalue, point):
+        obj_list = self.app.collection.get_selected()
 
-        self.buffer_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.buffer_entry.set_precision(self.decimals)
-        self.buffer_entry.setSingleStep(0.1)
-        self.buffer_entry.setWrapping(True)
-        self.buffer_entry.set_range(-9999.9999, 9999.9999)
+        if xvalue in [90, 180] or yvalue in [90, 180] or xvalue == yvalue == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Skew transformation can not be done for 0, 90 and 180 degrees."))
+            return
 
-        self.buffer_button = FCButton(_("Buffer D"))
-        self.buffer_button.setToolTip(
-            _("Create the buffer effect on each geometry,\n"
-              "element from the selected object, using the distance.")
-        )
-        self.buffer_button.setMinimumWidth(90)
+        if not obj_list:
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No object selected. Please Select an object to shear/skew!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Skew")):
+                try:
+                    px, py = point
 
-        grid0.addWidget(self.buffer_label, 37, 0)
-        grid0.addWidget(self.buffer_entry, 37, 1)
-        grid0.addWidget(self.buffer_button, 37, 2)
+                    for sel_obj in obj_list:
+                        if sel_obj.kind == 'cncjob':
+                            self.app.inform.emit(_("CNCJob objects can't be skewed."))
+                        else:
+                            sel_obj.skew(xvalue, yvalue, point=(px, py))
+                            # add information to the object that it was changed and how much
+                            sel_obj.options['skew_x'] = xvalue
+                            sel_obj.options['skew_y'] = yvalue
+                            self.app.app_obj.object_changed.emit(sel_obj)
+                        sel_obj.plot()
+                    self.app.inform.emit('[success] %s %s %s...' % (_('Skew on the'),  str(axis), _("axis done")))
+                except Exception as e:
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
+                    return
 
-        self.buffer_factor_label = QtWidgets.QLabel('%s:' % _("Value"))
-        self.buffer_factor_label.setToolTip(
-            _("A positive value will create the effect of dilation,\n"
-              "while a negative value will create the effect of erosion.\n"
-              "Each geometry element of the object will be increased\n"
-              "or decreased to fit the 'Value'. Value is a percentage\n"
-              "of the initial dimension.")
-        )
+    def on_scale(self, axis, xfactor, yfactor, point=None):
+        obj_list = self.app.collection.get_selected()
 
-        self.buffer_factor_entry = FCDoubleSpinner(callback=self.confirmation_message, suffix='%')
-        self.buffer_factor_entry.set_range(-100.0000, 1000.0000)
-        self.buffer_factor_entry.set_precision(self.decimals)
-        self.buffer_factor_entry.setWrapping(True)
-        self.buffer_factor_entry.setSingleStep(1)
+        if not obj_list:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to scale!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Scale")):
+                try:
+                    px, py = point
 
-        self.buffer_factor_button = FCButton(_("Buffer F"))
-        self.buffer_factor_button.setToolTip(
-            _("Create the buffer effect on each geometry,\n"
-              "element from the selected object, using the factor.")
-        )
-        self.buffer_factor_button.setMinimumWidth(90)
+                    for sel_obj in obj_list:
+                        if sel_obj.kind == 'cncjob':
+                            self.app.inform.emit(_("CNCJob objects can't be scaled."))
+                        else:
+                            sel_obj.scale(xfactor, yfactor, point=(px, py))
+                            # add information to the object that it was changed and how much
+                            sel_obj.options['scale_x'] = xfactor
+                            sel_obj.options['scale_y'] = yfactor
+                            self.app.app_obj.object_changed.emit(sel_obj)
+                        sel_obj.plot()
 
-        grid0.addWidget(self.buffer_factor_label, 38, 0)
-        grid0.addWidget(self.buffer_factor_entry, 38, 1)
-        grid0.addWidget(self.buffer_factor_button, 38, 2)
+                    self.app.inform.emit('[success] %s %s %s...' % (_('Scale on the'), str(axis), _('axis done')))
+                except Exception as e:
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
+                    return
 
-        grid0.addWidget(QtWidgets.QLabel(''), 42, 0, 1, 3)
+    def on_offset(self, axis, num):
+        obj_list = self.app.collection.get_selected()
 
-        self.layout.addStretch()
+        if not obj_list:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to offset!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Offset")):
+                try:
+                    for sel_obj in obj_list:
+                        if sel_obj.kind == 'cncjob':
+                            self.app.inform.emit(_("CNCJob objects can't be offset."))
+                        else:
+                            if axis == 'X':
+                                sel_obj.offset((num, 0))
+                                # add information to the object that it was changed and how much
+                                sel_obj.options['offset_x'] = num
+                            elif axis == 'Y':
+                                sel_obj.offset((0, num))
+                                # add information to the object that it was changed and how much
+                                sel_obj.options['offset_y'] = num
+                            self.app.app_obj.object_changed.emit(sel_obj)
+                        sel_obj.plot()
 
-        # ## Reset Tool
-        self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
-        self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
-        self.reset_button.setToolTip(
-            _("Will reset the tool parameters.")
-        )
-        self.reset_button.setStyleSheet("""
-                        QPushButton
-                        {
-                            font-weight: bold;
-                        }
-                        """)
-        self.layout.addWidget(self.reset_button)
+                    self.app.inform.emit('[success] %s %s %s...' % (_('Offset on the'), str(axis), _('axis done')))
+                except Exception as e:
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed, due of"), str(e)))
+                    return
 
-        # ## Signals
-        self.ref_combo.currentIndexChanged.connect(self.on_reference_changed)
-        self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
-        self.point_button.clicked.connect(self.on_add_coords)
+    def on_buffer_action(self, value, join, factor=None):
+        obj_list = self.app.collection.get_selected()
 
-        self.rotate_button.clicked.connect(self.on_rotate)
+        if not obj_list:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to buffer!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Buffer")):
+                try:
+                    for sel_obj in obj_list:
+                        if sel_obj.kind == 'cncjob':
+                            self.app.inform.emit(_("CNCJob objects can't be buffered."))
+                        elif sel_obj.kind.lower() == 'gerber':
+                            sel_obj.buffer(value, join, factor)
+                            sel_obj.source_file = self.app.export_gerber(obj_name=sel_obj.options['name'],
+                                                                         filename=None, local_use=sel_obj,
+                                                                         use_thread=False)
+                        elif sel_obj.kind.lower() == 'excellon':
+                            sel_obj.buffer(value, join, factor)
+                            sel_obj.source_file = self.app.export_excellon(obj_name=sel_obj.options['name'],
+                                                                           filename=None, local_use=sel_obj,
+                                                                           use_thread=False)
+                        elif sel_obj.kind.lower() == 'geometry':
+                            sel_obj.buffer(value, join, factor)
 
-        self.skewx_button.clicked.connect(self.on_skewx)
-        self.skewy_button.clicked.connect(self.on_skewy)
+                        self.app.app_obj.object_changed.emit(sel_obj)
+                        sel_obj.plot()
 
-        self.scalex_button.clicked.connect(self.on_scalex)
-        self.scaley_button.clicked.connect(self.on_scaley)
+                    self.app.inform.emit('[success] %s...' % _('Buffer done'))
 
-        self.offx_button.clicked.connect(self.on_offx)
-        self.offy_button.clicked.connect(self.on_offy)
+                except Exception as e:
+                    self.app.log.debug("ToolTransform.on_buffer_action() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed, due of"), str(e)))
+                    return
 
-        self.flipx_button.clicked.connect(self.on_flipx)
-        self.flipy_button.clicked.connect(self.on_flipy)
+    @staticmethod
+    def alt_bounds(obj_list):
+        """
+        Returns coordinates of rectangular bounds
+        of an object with geometry: (xmin, ymin, xmax, ymax).
+        """
 
-        self.buffer_button.clicked.connect(self.on_buffer_by_distance)
-        self.buffer_factor_button.clicked.connect(self.on_buffer_by_factor)
+        def bounds_rec(lst):
+            minx = np.Inf
+            miny = np.Inf
+            maxx = -np.Inf
+            maxy = -np.Inf
 
-        self.reset_button.clicked.connect(self.set_tool_ui)
+            try:
+                for obj in lst:
+                    if obj.kind != 'cncjob':
+                        minx_, miny_, maxx_, maxy_ = bounds_rec(obj)
+                        minx = min(minx, minx_)
+                        miny = min(miny, miny_)
+                        maxx = max(maxx, maxx_)
+                        maxy = max(maxy, maxy_)
+                return minx, miny, maxx, maxy
+            except TypeError:
+                # it's an object, return it's bounds
+                return lst.bounds()
 
-    def run(self, toggle=True):
-        self.app.defaults.report_usage("ToolTransform()")
+        return bounds_rec(obj_list)
 
-        if toggle:
-            # if the splitter is hidden, display it, else hide it but only if the current widget is the same
-            if self.app.ui.splitter.sizes()[0] == 0:
-                self.app.ui.splitter.setSizes([1, 1])
-            else:
-                try:
-                    if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
-                        # if tab is populated with the tool but it does not have the focus, focus on it
-                        if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
-                            # focus on Tool Tab
-                            self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
-                        else:
-                            self.app.ui.splitter.setSizes([0, 1])
-                except AttributeError:
-                    pass
-        else:
-            if self.app.ui.splitter.sizes()[0] == 0:
-                self.app.ui.splitter.setSizes([1, 1])
 
-        AppTool.run(self)
-        self.set_tool_ui()
+class TransformUI:
+    
+    toolName = _("Object Transform")
+    rotateName = _("Rotate")
+    skewName = _("Skew/Shear")
+    scaleName = _("Scale")
+    flipName = _("Mirror (Flip)")
+    offsetName = _("Offset")
+    bufferName = _("Buffer")
 
-        self.app.ui.notebook.setTabText(2, _("Transform Tool"))
+    def __init__(self, layout, app):
+        self.app = app
+        self.decimals = self.app.decimals
+        self.layout = layout
 
-    def install(self, icon=None, separator=None, **kwargs):
-        AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
+        # ## Title
+        title_label = FCLabel("%s" % self.toolName)
+        title_label.setStyleSheet("""
+                                QLabel
+                                {
+                                    font-size: 16px;
+                                    font-weight: bold;
+                                }
+                                """)
+        self.layout.addWidget(title_label)
+        self.layout.addWidget(FCLabel(""))
 
-    def set_tool_ui(self):
+        # ## Layout
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+        grid0.setColumnStretch(2, 0)
 
-        # ## Initialize form
-        self.ref_combo.set_value(self.app.defaults["tools_transform_reference"])
-        self.type_obj_combo.set_value(self.app.defaults["tools_transform_ref_object"])
-        self.point_entry.set_value(self.app.defaults["tools_transform_ref_point"])
-        self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])
+        grid0.addWidget(FCLabel(''))
 
-        self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"])
-        self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"])
-        self.skew_link_cb.set_value(self.app.defaults["tools_transform_skew_link"])
+        # Reference
+        ref_label = FCLabel('%s:' % _("Reference"))
+        ref_label.setToolTip(
+            _("The reference point for Rotate, Skew, Scale, Mirror.\n"
+              "Can be:\n"
+              "- Origin -> it is the 0, 0 point\n"
+              "- Selection -> the center of the bounding box of the selected objects\n"
+              "- Point -> a custom point defined by X,Y coordinates\n"
+              "- Object -> the center of the bounding box of a specific object")
+        )
+        self.ref_combo = FCComboBox()
+        self.ref_items = [_("Origin"), _("Selection"), _("Point"), _("Object")]
+        self.ref_combo.addItems(self.ref_items)
 
-        self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"])
-        self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"])
-        self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"])
+        grid0.addWidget(ref_label, 0, 0)
+        grid0.addWidget(self.ref_combo, 0, 1, 1, 2)
 
-        self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"])
-        self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"])
+        self.point_label = FCLabel('%s:' % _("Value"))
+        self.point_label.setToolTip(
+            _("A point of reference in format X,Y.")
+        )
+        self.point_entry = NumericalEvalTupleEntry()
 
-        self.buffer_entry.set_value(self.app.defaults["tools_transform_buffer_dis"])
-        self.buffer_factor_entry.set_value(self.app.defaults["tools_transform_buffer_factor"])
-        self.buffer_rounded_cb.set_value(self.app.defaults["tools_transform_buffer_corner"])
+        grid0.addWidget(self.point_label, 1, 0)
+        grid0.addWidget(self.point_entry, 1, 1, 1, 2)
 
-        # initial state is hidden
-        self.point_label.hide()
-        self.point_entry.hide()
-        self.point_button.hide()
+        self.point_button = FCButton(_("Add"))
+        self.point_button.setToolTip(
+            _("Add point coordinates from clipboard.")
+        )
+        grid0.addWidget(self.point_button, 2, 0, 1, 3)
 
-        self.type_object_label.hide()
-        self.type_obj_combo.hide()
-        self.object_combo.hide()
+        # Type of object to be used as reference
+        self.type_object_label = FCLabel('%s:' % _("Type"))
+        self.type_object_label.setToolTip(
+            _("The type of object used as reference.")
+        )
 
-    def on_type_obj_index_changed(self, index):
-        self.object_combo.setRootModelIndex(self.app.collection.index(index, 0, QtCore.QModelIndex()))
-        self.object_combo.setCurrentIndex(0)
-        self.object_combo.obj_type = {
-            _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
-        }[self.type_obj_combo.get_value()]
+        self.type_obj_combo = FCComboBox()
+        self.type_obj_combo.addItem(_("Gerber"))
+        self.type_obj_combo.addItem(_("Excellon"))
+        self.type_obj_combo.addItem(_("Geometry"))
 
-    def on_reference_changed(self, index):
-        if index == 0 or index == 1:  # "Origin" or "Selection" reference
-            self.point_label.hide()
-            self.point_entry.hide()
-            self.point_button.hide()
+        self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
+        self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
+        self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
 
-            self.type_object_label.hide()
-            self.type_obj_combo.hide()
-            self.object_combo.hide()
-        elif index == 2:    # "Point" reference
-            self.point_label.show()
-            self.point_entry.show()
-            self.point_button.show()
+        grid0.addWidget(self.type_object_label, 3, 0)
+        grid0.addWidget(self.type_obj_combo, 3, 1, 1, 2)
 
-            self.type_object_label.hide()
-            self.type_obj_combo.hide()
-            self.object_combo.hide()
-        else:   # "Object" reference
-            self.point_label.hide()
-            self.point_entry.hide()
-            self.point_button.hide()
+        # Object to be used as reference
+        self.object_combo = FCComboBox()
+        self.object_combo.setModel(self.app.collection)
+        self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.object_combo.is_last = True
 
-            self.type_object_label.show()
-            self.type_obj_combo.show()
-            self.object_combo.show()
+        self.object_combo.setToolTip(
+            _("The object used as reference.\n"
+              "The used point is the center of it's bounding box.")
+        )
+        grid0.addWidget(self.object_combo, 4, 0, 1, 3)
 
-    def on_calculate_reference(self):
-        ref_val = self.ref_combo.currentIndex()
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 5, 0, 1, 3)
 
-        if ref_val == 0:    # "Origin" reference
-            return 0, 0
-        elif ref_val == 1:  # "Selection" reference
-            sel_list = self.app.collection.get_selected()
-            if sel_list:
-                xmin, ymin, xmax, ymax = self.alt_bounds(obj_list=sel_list)
-                px = (xmax + xmin) * 0.5
-                py = (ymax + ymin) * 0.5
-                return px, py
-            else:
-                self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
-                return "fail"
-        elif ref_val == 2:  # "Point" reference
-            point_val = self.point_entry.get_value()
-            try:
-                px, py = eval('{}'.format(point_val))
-                return px, py
-            except Exception:
-                self.app.inform.emit('[WARNING_NOTCL] %s' % _("Incorrect format for Point value. Needs format X,Y"))
-                return "fail"
-        else:               # "Object" reference
-            obj_name = self.object_combo.get_value()
-            ref_obj = self.app.collection.get_by_name(obj_name)
-            xmin, ymin, xmax, ymax = ref_obj.bounds()
-            px = (xmax + xmin) * 0.5
-            py = (ymax + ymin) * 0.5
-            return px, py
+        # ## Rotate Title
+        rotate_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.rotateName)
+        grid0.addWidget(rotate_title_label, 6, 0, 1, 3)
 
-    def on_add_coords(self):
-        val = self.app.clipboard.text()
-        self.point_entry.set_value(val)
+        self.rotate_label = FCLabel('%s:' % _("Angle"))
+        self.rotate_label.setToolTip(
+            _("Angle for Rotation action, in degrees.\n"
+              "Float number between -360 and 359.\n"
+              "Positive numbers for CW motion.\n"
+              "Negative numbers for CCW motion.")
+        )
 
-    def on_rotate(self):
-        value = float(self.rotate_entry.get_value())
-        if value == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Rotate transformation can not be done for a value of 0."))
-            return
-        point = self.on_calculate_reference()
-        if point == 'fail':
-            return
-        self.app.worker_task.emit({'fcn': self.on_rotate_action, 'params': [value, point]})
+        self.rotate_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.rotate_entry.set_precision(self.decimals)
+        self.rotate_entry.setSingleStep(45)
+        self.rotate_entry.setWrapping(True)
+        self.rotate_entry.set_range(-360, 360)
 
-    def on_flipx(self):
-        axis = 'Y'
-        point = self.on_calculate_reference()
-        if point == 'fail':
-            return
-        self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis, point]})
+        # self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
 
-    def on_flipy(self):
-        axis = 'X'
-        point = self.on_calculate_reference()
-        if point == 'fail':
-            return
-        self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis, point]})
+        self.rotate_button = FCButton(_("Rotate"))
+        self.rotate_button.setToolTip(
+            _("Rotate the selected object(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected objects.")
+        )
+        self.rotate_button.setMinimumWidth(90)
 
-    def on_skewx(self):
-        xvalue = float(self.skewx_entry.get_value())
+        grid0.addWidget(self.rotate_label, 7, 0)
+        grid0.addWidget(self.rotate_entry, 7, 1)
+        grid0.addWidget(self.rotate_button, 7, 2)
 
-        if xvalue == 0:
-            return
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 8, 0, 1, 3)
 
-        if self.skew_link_cb.get_value():
-            yvalue = xvalue
-        else:
-            yvalue = 0
+        # ## Skew Title
+        skew_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.skewName)
+        grid0.addWidget(skew_title_label, 9, 0, 1, 2)
 
-        axis = 'X'
-        point = self.on_calculate_reference()
-        if point == 'fail':
-            return
+        self.skew_link_cb = FCCheckBox()
+        self.skew_link_cb.setText(_("Link"))
+        self.skew_link_cb.setToolTip(
+            _("Link the Y entry to X entry and copy its content.")
+        )
 
-        self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, xvalue, yvalue, point]})
+        grid0.addWidget(self.skew_link_cb, 9, 2)
 
-    def on_skewy(self):
-        xvalue = 0
-        yvalue = float(self.skewy_entry.get_value())
+        self.skewx_label = FCLabel('%s:' % _("X angle"))
+        self.skewx_label.setToolTip(
+            _("Angle for Skew action, in degrees.\n"
+              "Float number between -360 and 360.")
+        )
+        self.skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        # self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.skewx_entry.set_precision(self.decimals)
+        self.skewx_entry.set_range(-360, 360)
 
-        if yvalue == 0:
-            return
+        self.skewx_button = FCButton(_("Skew X"))
+        self.skewx_button.setToolTip(
+            _("Skew/shear the selected object(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected objects."))
+        self.skewx_button.setMinimumWidth(90)
 
-        axis = 'Y'
-        point = self.on_calculate_reference()
-        if point == 'fail':
-            return
+        grid0.addWidget(self.skewx_label, 10, 0)
+        grid0.addWidget(self.skewx_entry, 10, 1)
+        grid0.addWidget(self.skewx_button, 10, 2)
 
-        self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, xvalue, yvalue, point]})
+        self.skewy_label = FCLabel('%s:' % _("Y angle"))
+        self.skewy_label.setToolTip(
+            _("Angle for Skew action, in degrees.\n"
+              "Float number between -360 and 360.")
+        )
+        self.skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        # self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.skewy_entry.set_precision(self.decimals)
+        self.skewy_entry.set_range(-360, 360)
 
-    def on_scalex(self):
-        xvalue = float(self.scalex_entry.get_value())
+        self.skewy_button = FCButton(_("Skew Y"))
+        self.skewy_button.setToolTip(
+            _("Skew/shear the selected object(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected objects."))
+        self.skewy_button.setMinimumWidth(90)
 
-        if xvalue == 0 or xvalue == 1:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Scale transformation can not be done for a factor of 0 or 1."))
-            return
+        grid0.addWidget(self.skewy_label, 12, 0)
+        grid0.addWidget(self.skewy_entry, 12, 1)
+        grid0.addWidget(self.skewy_button, 12, 2)
 
-        if self.scale_link_cb.get_value():
-            yvalue = xvalue
-        else:
-            yvalue = 1
+        self.ois_sk = OptionalInputSection(self.skew_link_cb, [self.skewy_label, self.skewy_entry, self.skewy_button],
+                                           logic=False)
 
-        axis = 'X'
-        point = self.on_calculate_reference()
-        if point == 'fail':
-            return
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 14, 0, 1, 3)
 
-        self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
+        # ## Scale Title
+        scale_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.scaleName)
+        grid0.addWidget(scale_title_label, 15, 0, 1, 2)
 
-    def on_scaley(self):
-        xvalue = 1
-        yvalue = float(self.scaley_entry.get_value())
+        self.scale_link_cb = FCCheckBox()
+        self.scale_link_cb.setText(_("Link"))
+        self.scale_link_cb.setToolTip(
+            _("Link the Y entry to X entry and copy its content.")
+        )
 
-        if yvalue == 0 or yvalue == 1:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Scale transformation can not be done for a factor of 0 or 1."))
-            return
+        grid0.addWidget(self.scale_link_cb, 15, 2)
 
-        axis = 'Y'
-        point = self.on_calculate_reference()
-        if point == 'fail':
-            return
+        self.scalex_label = FCLabel('%s:' % _("X factor"))
+        self.scalex_label.setToolTip(
+            _("Factor for scaling on X axis.")
+        )
+        self.scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        # self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.scalex_entry.set_precision(self.decimals)
+        self.scalex_entry.setMinimum(-1e6)
 
-        self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
+        self.scalex_button = FCButton(_("Scale X"))
+        self.scalex_button.setToolTip(
+            _("Scale the selected object(s).\n"
+              "The point of reference depends on \n"
+              "the Scale reference checkbox state."))
+        self.scalex_button.setMinimumWidth(90)
 
-    def on_offx(self):
-        value = float(self.offx_entry.get_value())
-        if value == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
-            return
-        axis = 'X'
+        grid0.addWidget(self.scalex_label, 17, 0)
+        grid0.addWidget(self.scalex_entry, 17, 1)
+        grid0.addWidget(self.scalex_button, 17, 2)
 
-        self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
+        self.scaley_label = FCLabel('%s:' % _("Y factor"))
+        self.scaley_label.setToolTip(
+            _("Factor for scaling on Y axis.")
+        )
+        self.scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        # self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.scaley_entry.set_precision(self.decimals)
+        self.scaley_entry.setMinimum(-1e6)
 
-    def on_offy(self):
-        value = float(self.offy_entry.get_value())
-        if value == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
-            return
-        axis = 'Y'
+        self.scaley_button = FCButton(_("Scale Y"))
+        self.scaley_button.setToolTip(
+            _("Scale the selected object(s).\n"
+              "The point of reference depends on \n"
+              "the Scale reference checkbox state."))
+        self.scaley_button.setMinimumWidth(90)
 
-        self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
+        grid0.addWidget(self.scaley_label, 19, 0)
+        grid0.addWidget(self.scaley_entry, 19, 1)
+        grid0.addWidget(self.scaley_button, 19, 2)
 
-    def on_buffer_by_distance(self):
-        value = self.buffer_entry.get_value()
-        join = 1 if self.buffer_rounded_cb.get_value() else 2
+        self.ois_s = OptionalInputSection(self.scale_link_cb,
+                                          [
+                                              self.scaley_label,
+                                              self.scaley_entry,
+                                              self.scaley_button
+                                          ], logic=False)
 
-        self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join]})
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 21, 0, 1, 3)
 
-    def on_buffer_by_factor(self):
-        value = 1 + self.buffer_factor_entry.get_value() / 100.0
-        join = 1 if self.buffer_rounded_cb.get_value() else 2
+        # ## Flip Title
+        flip_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.flipName)
+        grid0.addWidget(flip_title_label, 23, 0, 1, 3)
 
-        # tell the buffer method to use the factor
-        factor = True
+        self.flipx_button = FCButton(_("Flip on X"))
+        self.flipx_button.setToolTip(
+            _("Flip the selected object(s) over the X axis.")
+        )
 
-        self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join, factor]})
+        self.flipy_button = FCButton(_("Flip on Y"))
+        self.flipy_button.setToolTip(
+            _("Flip the selected object(s) over the X axis.")
+        )
 
-    def on_rotate_action(self, num, point):
-        obj_list = self.app.collection.get_selected()
+        hlay0 = QtWidgets.QHBoxLayout()
+        grid0.addLayout(hlay0, 25, 0, 1, 3)
 
-        if not obj_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to rotate!"))
-            return
-        else:
-            with self.app.proc_container.new(_("Appying Rotate")):
-                try:
-                    px, py = point
-                    for sel_obj in obj_list:
-                        if sel_obj.kind == 'cncjob':
-                            self.app.inform.emit(_("CNCJob objects can't be rotated."))
-                        else:
-                            sel_obj.rotate(-num, point=(px, py))
-                            self.app.app_obj.object_changed.emit(sel_obj)
+        hlay0.addWidget(self.flipx_button)
+        hlay0.addWidget(self.flipy_button)
 
-                        # add information to the object that it was changed and how much
-                        sel_obj.options['rotate'] = num
-                        sel_obj.plot()
-                    self.app.inform.emit('[success] %s...' % _('Rotate done'))
-                except Exception as e:
-                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
-                                         (_("Due of"), str(e), _("action was not executed.")))
-                    return
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 27, 0, 1, 3)
 
-    def on_flip(self, axis, point):
-        obj_list = self.app.collection.get_selected()
+        # ## Offset Title
+        offset_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.offsetName)
+        grid0.addWidget(offset_title_label, 29, 0, 1, 3)
 
-        if not obj_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s!' % _("No object selected. Please Select an object to flip"))
-            return
-        else:
-            with self.app.proc_container.new(_("Applying Flip")):
-                try:
-                    px, py = point
+        self.offx_label = FCLabel('%s:' % _("X val"))
+        self.offx_label.setToolTip(
+            _("Distance to offset on X axis. In current units.")
+        )
+        self.offx_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        # self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.offx_entry.set_precision(self.decimals)
+        self.offx_entry.setMinimum(-1e6)
 
-                    # execute mirroring
-                    for sel_obj in obj_list:
-                        if sel_obj.kind == 'cncjob':
-                            self.app.inform.emit(_("CNCJob objects can't be mirrored/flipped."))
-                        else:
-                            if axis == 'X':
-                                sel_obj.mirror('X', (px, py))
-                                # add information to the object that it was changed and how much
-                                # the axis is reversed because of the reference
-                                if 'mirror_y' in sel_obj.options:
-                                    sel_obj.options['mirror_y'] = not sel_obj.options['mirror_y']
-                                else:
-                                    sel_obj.options['mirror_y'] = True
-                                self.app.inform.emit('[success] %s...' % _('Flip on the Y axis done'))
-                            elif axis == 'Y':
-                                sel_obj.mirror('Y', (px, py))
-                                # add information to the object that it was changed and how much
-                                # the axis is reversed because of the reference
-                                if 'mirror_x' in sel_obj.options:
-                                    sel_obj.options['mirror_x'] = not sel_obj.options['mirror_x']
-                                else:
-                                    sel_obj.options['mirror_x'] = True
-                                self.app.inform.emit('[success] %s...' % _('Flip on the X axis done'))
-                            self.app.app_obj.object_changed.emit(sel_obj)
-                        sel_obj.plot()
-                except Exception as e:
-                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
-                                         (_("Due of"), str(e), _("action was not executed.")))
-                    return
+        self.offx_button = FCButton(_("Offset X"))
+        self.offx_button.setToolTip(
+            _("Offset the selected object(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected objects.\n"))
+        self.offx_button.setMinimumWidth(90)
 
-    def on_skew(self, axis, xvalue, yvalue, point):
-        obj_list = self.app.collection.get_selected()
+        grid0.addWidget(self.offx_label, 31, 0)
+        grid0.addWidget(self.offx_entry, 31, 1)
+        grid0.addWidget(self.offx_button, 31, 2)
 
-        if xvalue in [90, 180] or yvalue in [90, 180] or xvalue == yvalue == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Skew transformation can not be done for 0, 90 and 180 degrees."))
-            return
+        self.offy_label = FCLabel('%s:' % _("Y val"))
+        self.offy_label.setToolTip(
+            _("Distance to offset on Y axis. In current units.")
+        )
+        self.offy_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        # self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.offy_entry.set_precision(self.decimals)
+        self.offy_entry.setMinimum(-1e6)
 
-        if not obj_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("No object selected. Please Select an object to shear/skew!"))
-            return
-        else:
-            with self.app.proc_container.new(_("Applying Skew")):
-                try:
-                    px, py = point
+        self.offy_button = FCButton(_("Offset Y"))
+        self.offy_button.setToolTip(
+            _("Offset the selected object(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected objects.\n"))
+        self.offy_button.setMinimumWidth(90)
 
-                    for sel_obj in obj_list:
-                        if sel_obj.kind == 'cncjob':
-                            self.app.inform.emit(_("CNCJob objects can't be skewed."))
-                        else:
-                            sel_obj.skew(xvalue, yvalue, point=(px, py))
-                            # add information to the object that it was changed and how much
-                            sel_obj.options['skew_x'] = xvalue
-                            sel_obj.options['skew_y'] = yvalue
-                            self.app.app_obj.object_changed.emit(sel_obj)
-                        sel_obj.plot()
-                    self.app.inform.emit('[success] %s %s %s...' % (_('Skew on the'),  str(axis), _("axis done")))
-                except Exception as e:
-                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
-                                         (_("Due of"), str(e), _("action was not executed.")))
-                    return
+        grid0.addWidget(self.offy_label, 32, 0)
+        grid0.addWidget(self.offy_entry, 32, 1)
+        grid0.addWidget(self.offy_button, 32, 2)
 
-    def on_scale(self, axis, xfactor, yfactor, point=None):
-        obj_list = self.app.collection.get_selected()
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 34, 0, 1, 3)
 
-        if not obj_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to scale!"))
-            return
-        else:
-            with self.app.proc_container.new(_("Applying Scale")):
-                try:
-                    px, py = point
+        # ## Buffer Title
+        buffer_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.bufferName)
+        grid0.addWidget(buffer_title_label, 35, 0, 1, 2)
 
-                    for sel_obj in obj_list:
-                        if sel_obj.kind == 'cncjob':
-                            self.app.inform.emit(_("CNCJob objects can't be scaled."))
-                        else:
-                            sel_obj.scale(xfactor, yfactor, point=(px, py))
-                            # add information to the object that it was changed and how much
-                            sel_obj.options['scale_x'] = xfactor
-                            sel_obj.options['scale_y'] = yfactor
-                            self.app.app_obj.object_changed.emit(sel_obj)
-                        sel_obj.plot()
+        self.buffer_rounded_cb = FCCheckBox('%s' % _("Rounded"))
+        self.buffer_rounded_cb.setToolTip(
+            _("If checked then the buffer will surround the buffered shape,\n"
+              "every corner will be rounded.\n"
+              "If not checked then the buffer will follow the exact geometry\n"
+              "of the buffered shape.")
+        )
 
-                    self.app.inform.emit('[success] %s %s %s...' % (_('Scale on the'), str(axis), _('axis done')))
-                except Exception as e:
-                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
-                                         (_("Due of"), str(e), _("action was not executed.")))
-                    return
+        grid0.addWidget(self.buffer_rounded_cb, 35, 2)
 
-    def on_offset(self, axis, num):
-        obj_list = self.app.collection.get_selected()
+        self.buffer_label = FCLabel('%s:' % _("Distance"))
+        self.buffer_label.setToolTip(
+            _("A positive value will create the effect of dilation,\n"
+              "while a negative value will create the effect of erosion.\n"
+              "Each geometry element of the object will be increased\n"
+              "or decreased with the 'distance'.")
+        )
 
-        if not obj_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to offset!"))
-            return
-        else:
-            with self.app.proc_container.new(_("Applying Offset")):
-                try:
-                    for sel_obj in obj_list:
-                        if sel_obj.kind == 'cncjob':
-                            self.app.inform.emit(_("CNCJob objects can't be offset."))
-                        else:
-                            if axis == 'X':
-                                sel_obj.offset((num, 0))
-                                # add information to the object that it was changed and how much
-                                sel_obj.options['offset_x'] = num
-                            elif axis == 'Y':
-                                sel_obj.offset((0, num))
-                                # add information to the object that it was changed and how much
-                                sel_obj.options['offset_y'] = num
-                            self.app.app_obj.object_changed.emit(sel_obj)
-                        sel_obj.plot()
+        self.buffer_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.buffer_entry.set_precision(self.decimals)
+        self.buffer_entry.setSingleStep(0.1)
+        self.buffer_entry.setWrapping(True)
+        self.buffer_entry.set_range(-9999.9999, 9999.9999)
 
-                    self.app.inform.emit('[success] %s %s %s...' % (_('Offset on the'), str(axis), _('axis done')))
-                except Exception as e:
-                    self.app.inform.emit('[ERROR_NOTCL] %s: %s.' %
-                                     (_("Action was not executed, due of"), str(e)))
-                    return
+        self.buffer_button = FCButton(_("Buffer D"))
+        self.buffer_button.setToolTip(
+            _("Create the buffer effect on each geometry,\n"
+              "element from the selected object, using the distance.")
+        )
+        self.buffer_button.setMinimumWidth(90)
 
-    def on_buffer_action(self, value, join, factor=None):
-        obj_list = self.app.collection.get_selected()
+        grid0.addWidget(self.buffer_label, 37, 0)
+        grid0.addWidget(self.buffer_entry, 37, 1)
+        grid0.addWidget(self.buffer_button, 37, 2)
 
-        if not obj_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to buffer!"))
-            return
-        else:
-            with self.app.proc_container.new(_("Applying Buffer")):
-                try:
-                    for sel_obj in obj_list:
-                        if sel_obj.kind == 'cncjob':
-                            self.app.inform.emit(_("CNCJob objects can't be buffered."))
-                        elif sel_obj.kind.lower() == 'gerber':
-                            sel_obj.buffer(value, join, factor)
-                            sel_obj.source_file = self.app.export_gerber(obj_name=sel_obj.options['name'],
-                                                                         filename=None, local_use=sel_obj,
-                                                                         use_thread=False)
-                        elif sel_obj.kind.lower() == 'excellon':
-                            sel_obj.buffer(value, join, factor)
-                            sel_obj.source_file = self.app.export_excellon(obj_name=sel_obj.options['name'],
-                                                                           filename=None, local_use=sel_obj,
-                                                                           use_thread=False)
-                        elif sel_obj.kind.lower() == 'geometry':
-                            sel_obj.buffer(value, join, factor)
+        self.buffer_factor_label = FCLabel('%s:' % _("Value"))
+        self.buffer_factor_label.setToolTip(
+            _("A positive value will create the effect of dilation,\n"
+              "while a negative value will create the effect of erosion.\n"
+              "Each geometry element of the object will be increased\n"
+              "or decreased to fit the 'Value'. Value is a percentage\n"
+              "of the initial dimension.")
+        )
 
-                        self.app.app_obj.object_changed.emit(sel_obj)
-                        sel_obj.plot()
+        self.buffer_factor_entry = FCDoubleSpinner(callback=self.confirmation_message, suffix='%')
+        self.buffer_factor_entry.set_range(-100.0000, 1000.0000)
+        self.buffer_factor_entry.set_precision(self.decimals)
+        self.buffer_factor_entry.setWrapping(True)
+        self.buffer_factor_entry.setSingleStep(1)
 
-                    self.app.inform.emit('[success] %s...' % _('Buffer done'))
+        self.buffer_factor_button = FCButton(_("Buffer F"))
+        self.buffer_factor_button.setToolTip(
+            _("Create the buffer effect on each geometry,\n"
+              "element from the selected object, using the factor.")
+        )
+        self.buffer_factor_button.setMinimumWidth(90)
 
-                except Exception as e:
-                    self.app.log.debug("ToolTransform.on_buffer_action() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s: %s.' %
-                                     (_("Action was not executed, due of"), str(e)))
-                    return
+        grid0.addWidget(self.buffer_factor_label, 38, 0)
+        grid0.addWidget(self.buffer_factor_entry, 38, 1)
+        grid0.addWidget(self.buffer_factor_button, 38, 2)
 
-    @staticmethod
-    def alt_bounds(obj_list):
-        """
-        Returns coordinates of rectangular bounds
-        of an object with geometry: (xmin, ymin, xmax, ymax).
-        """
+        grid0.addWidget(FCLabel(''), 42, 0, 1, 3)
 
-        def bounds_rec(lst):
-            minx = np.Inf
-            miny = np.Inf
-            maxx = -np.Inf
-            maxy = -np.Inf
+        self.layout.addStretch()
 
-            try:
-                for obj in lst:
-                    if obj.kind != 'cncjob':
-                        minx_, miny_, maxx_, maxy_ = bounds_rec(obj)
-                        minx = min(minx, minx_)
-                        miny = min(miny, miny_)
-                        maxx = max(maxx, maxx_)
-                        maxy = max(maxy, maxy_)
-                return minx, miny, maxx, maxy
-            except TypeError:
-                # it's an object, return it's bounds
-                return lst.bounds()
+        # ## Reset Tool
+        self.reset_button = FCButton(_("Reset Tool"))
+        self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
+        self.reset_button.setToolTip(
+            _("Will reset the tool parameters.")
+        )
+        self.reset_button.setStyleSheet("""
+                                QPushButton
+                                {
+                                    font-weight: bold;
+                                }
+                                """)
+        self.layout.addWidget(self.reset_button)
 
-        return bounds_rec(obj_list)
+        # #################################### FINSIHED GUI ###########################
+        # #############################################################################
+    
+    def on_reference_changed(self, index):
+        if index == 0 or index == 1:  # "Origin" or "Selection" reference
+            self.point_label.hide()
+            self.point_entry.hide()
+            self.point_button.hide()
+
+            self.type_object_label.hide()
+            self.type_obj_combo.hide()
+            self.object_combo.hide()
+        elif index == 2:    # "Point" reference
+            self.point_label.show()
+            self.point_entry.show()
+            self.point_button.show()
 
-# end of file
+            self.type_object_label.hide()
+            self.type_obj_combo.hide()
+            self.object_combo.hide()
+        else:   # "Object" reference
+            self.point_label.hide()
+            self.point_entry.hide()
+            self.point_button.hide()
+
+            self.type_object_label.show()
+            self.type_obj_combo.show()
+            self.object_combo.show()
+            
+    def confirmation_message(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
+                                                                                  self.decimals,
+                                                                                  minval,
+                                                                                  self.decimals,
+                                                                                  maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+
+    def confirmation_message_int(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                            (_("Edited value is out of range"), minval, maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)