<reponame>haisenzhao/CarpentryCompiler<filename>FeatureHole/TaskHole.py
#/******************************************************************************
# * Copyright (c)2012 <NAME> <<EMAIL>> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This library is free software; you can redistribute it and/or *
# * modify it under the terms of the GNU Library General Public *
# * License as published by the Free Software Foundation; either *
# * version 2 of the License, or (at your option) any later version. *
# * *
# * This library is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this library; see the file COPYING.LIB. If not, *
# * write to the Free Software Foundation, Inc., 59 Temple Place, *
# * Suite 330, Boston, MA 02111-1307, USA *
# * *
# ******************************************************************************/

import FreeCAD, FreeCADGui
import Part,  PartDesignGui
from PyQt4 import QtCore, QtGui
import Standards
import os

class TaskHole:
    "Hole hole feature"
    types = ["Linear", "Coaxial"]
    typestr = ["Linear to two lines/planes", "Coaxial to a circle/cylinder"]

    def __init__(self, feature):
        self.form = None
        self.extraStandards = []
        self.feature = feature
        p=os.path.realpath(__file__)
        p=os.path.dirname(p)
        self.ui = os.path.join(p, "TaskHole.ui")

    def accept(self):
        self.feature.touch()
        FreeCAD.ActiveDocument.recompute()
        FreeCADGui.ActiveDocument.resetEdit()
        return True

    def reject(self):
        if (self.feature != None):
            self.hideFeature() # Show the support again
            document = self.feature.Document
            body = FreeCADGui.activeView().getActiveObject("pdbody");
            groove = self.feature.HoleGroove
            sketch = groove.Sketch
            plane = sketch.Support[0]
            axis = plane.References[0][0]     
            body.removeObject(self.feature)
            document.removeObject(self.feature.Name)                      
            body.removeObject(groove)
            document.removeObject(groove.Name)
            body.removeObject(sketch)
            try:
                document.removeObject(sketch.Name)      
            except:
                pass # This always throws an exception: "Sketch support has been deleted" from SketchObject::execute()
            body.removeObject(plane)
            document.removeObject(plane.Name)
            body.removeObject(axis)
            document.removeObject(axis.Name)            
        FreeCADGui.ActiveDocument.resetEdit()
        FreeCADGui.Control.closeDialog(self)
        return True
        
    def isAllowedAlterDocument(self):
        return False
        
    def isAllowedAlterView(self):
        return False
        
    def isAllowedAlterSelection(self):
        return True

    def getMainWindow(self):
        "returns the main window"
        # using QtGui.QApplication.activeWindow() isn't very reliable because if another
        # widget than the mainwindow is active (e.g. a dialog) the wrong widget is
        # returned
        toplevel = QtGui.QApplication.topLevelWidgets()
        for i in toplevel:
            if i.metaObject().className() == "Gui::MainWindow":
                return i
        raise Exception("No main window found")

    def setupUi(self):
        mw = self.getMainWindow()
        form = mw.findChild(QtGui.QWidget, "TaskHole")
        if form == None:
            return
        form.tabWidget = form.findChild(QtGui.QTabWidget, "tabWidget")
        # Type
        form.tabType = form.tabWidget.findChild(QtGui.QWidget, "tab_type")
        form.buttonThru = form.tabType.findChild(QtGui.QRadioButton, "buttonThru")
        form.buttonDepth = form.tabType.findChild(QtGui.QRadioButton, "buttonDepth")        
        form.checkThreaded = form.tabType.findChild(QtGui.QCheckBox, "checkThreaded")        
        form.checkCounterbore = form.tabType.findChild(QtGui.QCheckBox, "checkCounterbore")        
        form.checkCountersink = form.tabType.findChild(QtGui.QCheckBox, "checkCountersink")
        # Norm
        form.tabNorm = form.tabWidget.findChild(QtGui.QWidget, "tab_norm")
        form.checkCustom = form.tabNorm.findChild(QtGui.QCheckBox, "checkCustom")
        form.comboNorm = form.tabNorm.findChild(QtGui.QComboBox, "comboNorm")
        for std in Standards.getStandards("through"):
            form.comboNorm.addItem(std)
        form.comboTolerance = form.tabNorm.findChild(QtGui.QComboBox, "comboTolerance")
        for tol in Standards.standards_tolerance:
            form.comboTolerance.addItem(tol)
        form.comboNormDia = form.tabNorm.findChild(QtGui.QComboBox, "comboNormDia")
        form.comboNormBoltWasher = form.tabNorm.findChild(QtGui.QComboBox,  "comboNormBoltWasher")        
        # Thread
        form.tabThread = form.tabWidget.findChild(QtGui.QWidget,  "tab_thread")
        form.comboThreadNorm = form.tabThread.findChild(QtGui.QComboBox,  "comboThreadNorm")
        for std in Standards.getStandards("thread"):
            form.comboThreadNorm.addItem(std)
        form.comboThreadDia = form.tabThread.findChild(QtGui.QComboBox,  "comboThreadDia")
        form.checkCustomThreadLength = form.tabThread.findChild(QtGui.QCheckBox,  "checkCustomThreadLength")
        form.comboFinishNorm = form.tabThread.findChild(QtGui.QComboBox,  "comboFinishNorm")
        for std in Standards.getStandards("threaded"):
            form.comboFinishNorm.addItem(std)
        # Data
        form.tabData = form.tabWidget.findChild(QtGui.QWidget, "tab_data")
        form.spinDiameter = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinDiameter")        
        form.spinDepth = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinDepth")        
        form.spinCounterboreDiameter = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinCounterboreDiameter")
        form.spinCounterboreDepth = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinCounterboreDepth")
        form.spinCountersinkAngle = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinCountersinkAngle")
        form.spinThreadLength = form.tabData.findChild(QtGui.QDoubleSpinBox,  "spinThreadLength")
        # Position
        form.tabPosition = form.tabWidget.findChild(QtGui.QWidget, "tab_position")
        form.comboType = form.tabPosition.findChild(QtGui.QComboBox, "comboType")
        for i in self.typestr:
            form.comboType.addItem(i)
        form.buttonSupport = form.tabPosition.findChild(QtGui.QPushButton, "buttonSupport")
        form.lineSupport = form.tabPosition.findChild(QtGui.QLineEdit, "lineSupport")
        form.buttonRef1 = form.tabPosition.findChild(QtGui.QPushButton, "buttonRef1")
        form.lineRef1 = form.tabPosition.findChild(QtGui.QLineEdit, "lineRef1")
        form.labelRef1 = form.tabPosition.findChild(QtGui.QLabel, "labelRef1")
        form.spinRef1 = form.tabPosition.findChild(QtGui.QDoubleSpinBox, "spinRef1")
        form.buttonRef2 = form.tabPosition.findChild(QtGui.QPushButton, "buttonRef2")
        form.lineRef2 = form.tabPosition.findChild(QtGui.QLineEdit, "lineRef2")
        form.labelRef2 = form.tabPosition.findChild(QtGui.QLabel, "labelRef2")
        form.spinRef2 = form.tabPosition.findChild(QtGui.QDoubleSpinBox, "spinRef2")
        self.form = form

        # Connect Signals and Slots
        # Type
        self.form.buttonThru.toggled.connect(self.buttonThru)
        self.form.buttonDepth.toggled.connect(self.buttonDepth)
        self.form.checkThreaded.toggled.connect(self.checkThreaded)
        self.form.checkCounterbore.toggled.connect(self.checkCounterbore)
        self.form.checkCountersink.toggled.connect(self.checkCountersink)
        # Norm
        self.form.checkCustom.toggled.connect(self.checkCustom)
        self.form.comboNorm.currentIndexChanged.connect(self.comboNorm)
        self.form.comboTolerance.currentIndexChanged.connect(self.comboTolerance)
        self.form.comboNormDia.currentIndexChanged.connect(self.comboNormDia)
        self.form.comboNormBoltWasher.currentIndexChanged.connect(self.comboNormBoltWasher)
        # Thread
        self.form.comboThreadNorm.currentIndexChanged.connect(self.comboThreadNorm)
        self.form.comboThreadDia.currentIndexChanged.connect(self.comboThreadDia)
        self.form.checkCustomThreadLength.toggled.connect(self.checkCustomThreadLength)
        self.form.comboFinishNorm.currentIndexChanged.connect(self.comboFinishNorm)
        # Data
        self.form.spinDiameter.valueChanged.connect(self.spinDiameter)
        self.form.spinDepth.valueChanged.connect(self.spinDepth)
        self.form.spinCounterboreDiameter.valueChanged.connect(self.spinCounterboreDiameter)
        self.form.spinCounterboreDepth.valueChanged.connect(self.spinCounterboreDepth)
        self.form.spinCountersinkAngle.valueChanged.connect(self.spinCountersinkAngle)
        self.form.spinThreadLength.valueChanged.connect(self.spinThreadLength)
        # Position
        self.form.comboType.currentIndexChanged.connect(self.comboType)
        self.form.buttonSupport.clicked.connect(self.buttonSupport)
        self.form.buttonRef1.clicked.connect(self.buttonRef1)
        self.form.spinRef1.valueChanged.connect(self.spinRef1)
        self.form.buttonRef2.clicked.connect(self.buttonRef2)
        self.form.spinRef2.valueChanged.connect(self.spinRef2)

        # Update the UI
        self.updateUI()
        return True
        
    def getRefText(self,  ref):
        (obj,  element) = ref
        if isinstance(element,  basestring):
            return obj.Name + ":" + element
        elif isinstance(element,  list):
            return obj.Name + ":" + element[0]
        else:
            return obj.Name

    def updateUI(self):
        # Type
        self.form.buttonThru.setChecked(self.feature.HoleType == "Thru")
        self.form.buttonDepth.setChecked(self.feature.HoleType == "Depth")
        self.form.checkThreaded.setChecked(self.feature.Threaded == True)
        self.form.checkCounterbore.setChecked(self.feature.Counterbore == True)
        self.form.checkCountersink.setChecked(self.feature.Countersink == True)
        # Norm
        if self.feature.Norm == "Custom":
            self.form.checkCustom.setChecked(True)
            self.form.comboNorm.setEnabled(False)
            self.form.comboTolerance.setEnabled(False)
            self.form.comboNormDia.setEnabled(False)
            self.form.comboNormBoltWasher.setEnabled(False)
        else:
            if self.feature.Counterbore == True:
                holetype = "counterbore"
            elif self.feature.Countersink == True:
                 holetype = "countersink"
            elif self.feature.Threaded == True:
                holetype = "threaded"
            else:
                holetype = "through"
            self.form.comboNorm.setEnabled(True)
            self.form.comboTolerance.setEnabled(True)
            self.form.comboNormDia.setEnabled(True)
            if holetype == "counterbore":
                self.form.comboNormBoltWasher.setEnabled(True)
            else:
                self.form.comboNormBoltWasher.setEnabled(False)
            # comboNorm            
            standards = Standards.getStandards(holetype)
            self.form.comboNorm.blockSignals(True)
            self.form.comboNorm.clear()
            for std in standards:
                self.form.comboNorm.addItem(std)
            if not self.feature.Norm in standards:
                self.feature.Norm = standards[0]
            else:
                self.form.comboNorm.setCurrentIndex(standards.index(self.feature.Norm))
            self.form.comboNorm.blockSignals(False)
            # comboTolerance
            self.form.comboTolerance.blockSignals(True)
            self.form.comboTolerance.setCurrentIndex(Standards.standards_tolerance.index(self.feature.NormTolerance))
            self.form.comboTolerance.blockSignals(False)
            # comboNormDia
            diameters = sorted(Standards.getBaseDiameters(self.feature.Norm))
            self.form.comboNormDia.blockSignals(True)
            self.form.comboNormDia.clear()
            for dia in diameters:
                self.form.comboNormDia.addItem("M%g" % dia)
            if self.feature.NormDiameter in diameters:
                self.form.comboNormDia.setCurrentIndex(diameters.index(self.feature.NormDiameter))
            self.form.comboNormDia.blockSignals(False)
            # comboNormBoltWasher
            if holetype == "counterbore":
                rowStandards = sorted(Standards.getRowStandards(self.feature.Norm))
                self.form.comboNormBoltWasher.blockSignals(True)
                self.form.comboNormBoltWasher.clear()
                for std in rowStandards:
                    self.form.comboNormBoltWasher.addItem(std)
                if self.feature.ExtraNorm in rowStandards:
                    self.form.comboNormBoltWasher.setCurrentIndex(rowStandards.index(self.feature.ExtraNorm))
                self.form.comboNormBoltWasher.blockSignals(False)
            # Dependent values
            if holetype == "through":
                self.feature.Diameter = Standards.getThroughHoleDia(self.feature.Norm, self.feature.NormDiameter, self.feature.NormTolerance)
            elif holetype == "counterbore":
                throughStandard = Standards.getThroughHoleStandard(self.feature.Norm)
                self.feature.Diameter = Standards.getThroughHoleDia(throughStandard,  self.feature.NormDiameter,  self.feature.NormTolerance)
                self.feature.CounterboreDiameter = Standards.getCounterboreDia(self.feature.Norm,  self.feature.NormDiameter,  self.feature.ExtraNorm)                
                # TODO: Calculate counter bore depth from standard for bolt and washer(s)
                # Requires accessing all the norms for bolts
                # self.feature.CounterboreDepth = calcCounterboreDepth(...)
            elif holetype == "countersink":
                throughStandard = Standards.getThroughHoleStandard(self.feature.Norm)
                self.feature.Diameter = Standards.getThroughHoleDia(throughStandard,  self.feature.NormDiameter,  self.feature.NormTolerance)
                self.feature.CounterboreDiameter = Standards.getCountersinkDia(self.feature.Norm,  self.feature.NormDiameter)
                self.feature.CountersinkAngle = Standards.getCountersinkAngle(self.feature.Norm,  self.feature.NormDiameter) / 2.0
        # Thread
        if self.feature.Threaded == True:
            if not self.feature.Counterbore and not self.feature.Countersink:
                self.form.comboTolerance.setEnabled(False)
            else:
                self.form.tabNorm.setEnabled(True)
            self.form.comboTolerance.setEnabled(False)
            self.form.tabThread.setEnabled(True) 
            self.form.comboThreadNorm.blockSignals(True)
            standards = Standards.getStandards("thread")
            if not self.feature.NormThread in standards:
                self.feature.NormThread = standards[0]
            else:
                self.form.comboThreadNorm.setCurrentIndex(standards.index(self.feature.NormThread))
            self.form.comboThreadNorm.blockSignals(False)
            threadDiameters = sorted(Standards.getBaseDiameters(self.feature.NormThread))
            self.form.comboThreadDia.blockSignals(True)
            self.form.comboThreadDia.clear()
            for dia in threadDiameters:
                self.form.comboThreadDia.addItem("M%g" % dia)
            if self.feature.NormDiameter in threadDiameters:
                self.form.comboThreadDia.setCurrentIndex(threadDiameters.index(self.feature.NormDiameter))
            self.form.comboThreadDia.blockSignals(False)
            if self.feature.NormThreadFinish == "Custom":
                self.form.checkCustomThreadLength.setChecked(True)
                self.form.comboFinishNorm.setEnabled(False)
            else:
                self.form.checkCustomThreadLength.setChecked(False)
                self.form.comboFinishNorm.setEnabled(True)
                self.form.comboFinishNorm.blockSignals(True)
                standards = Standards.getStandards("threaded")
                if not self.feature.NormThreadFinish in standards:
                    self.feature.NormThreadFinish = standards[0]
                else:
                    self.form.comboFinishNorm.setCurrentIndex(standards.index(self.feature.NormThreadFinish))
                self.form.comboFinishNorm.blockSignals(False)
                flength = Standards.getThreadFinishLength(self.feature.NormThreadFinish,  self.feature.NormDiameter)
                tlength = self.feature.Depth - flength
                if tlength > 0:
                    self.feature.ThreadLength = tlength # TODO: Warning message
            # Dependents
            self.feature.Diameter = Standards.getThreadCoreDiameter(self.feature.NormThread,  self.feature.NormDiameter)
        else:
            self.form.tabThread.setEnabled(False)
            # Dependents
            self.form.spinDiameter.setEnabled(True)
        # Data
        self.form.spinDiameter.setValue(self.feature.Diameter)
        self.form.spinDepth.setValue(self.feature.Depth)
        if self.feature.HoleType == "Thru":
            self.form.spinDepth.setEnabled(False)
        else:
            self.form.spinDepth.setEnabled(True)
        if self.feature.Threaded == True:
            self.form.spinThreadLength.setEnabled(True)
        else:
            self.form.spinThreadLength.setEnabled(False)
        if self.feature.Counterbore == True:
            self.form.spinCounterboreDiameter.setEnabled(True)
            self.form.spinCounterboreDiameter.setValue(self.feature.CounterboreDiameter)
            self.form.spinCounterboreDepth.setEnabled(True)
            self.form.spinCounterboreDepth.setValue(self.feature.CounterboreDepth)
            self.form.spinCountersinkAngle.setEnabled(False)
        elif self.feature.Countersink == True:
            self.form.spinCounterboreDiameter.setEnabled(True)
            self.form.spinCounterboreDiameter.setValue(self.feature.CounterboreDiameter)
            self.form.spinCounterboreDepth.setEnabled(False)       
            self.form.spinCountersinkAngle.setEnabled(True)
            self.form.spinCountersinkAngle.setValue(self.feature.CountersinkAngle)
        else:
            self.form.spinCounterboreDiameter.setEnabled(False)
            self.form.spinCounterboreDepth.setEnabled(False)
            self.form.spinCountersinkAngle.setEnabled(False)
        if self.feature.Norm == "Custom":
            self.form.spinDiameter.setEnabled(True)
        else:
            self.form.spinDiameter.setEnabled(False)
            if holetype == "counterbore":
                # Diameter is taken from Norm
                self.form.spinCounterboreDiameter.setEnabled(False)
            elif holetype == "countersink":
                # Values are taken from Norm
                self.form.spinCounterboreDiameter.setEnabled(False)
                self.form.spinCounterboreDepth.setEnabled(False)
                self.form.spinCountersinkAngle.setEnabled(False)
        if self.feature.Threaded == True:
            self.form.spinDiameter.setEnabled(False)
        if self.feature.NormThreadFinish != "Custom":            
            self.form.spinThreadLength.setEnabled(False)
        self.form.spinThreadLength.setValue(self.feature.ThreadLength)
        # Position
        self.form.buttonSupport.setText("Face")
        if self.feature.Support == None:
            # First-time initialization
            selection = FreeCADGui.Selection.getSelectionEx()
            self.feature.Support = (selection[0].Object, selection[0].SubElementNames)
        self.form.lineSupport.setText(self.getRefText(self.feature.Support))
        if self.feature.PositionType == self.types[0]:
            # Linear            
            self.form.buttonRef1.setText("Line/Plane")
            self.form.buttonRef1.setEnabled(True)
            self.form.buttonRef2.setText("Line/Plane")
            self.form.buttonRef2.setEnabled(True)
            self.form.lineRef1.setEnabled(True)
            self.form.lineRef2.setEnabled(True)
            self.form.labelRef1.setEnabled(True)
            self.form.labelRef1.setText("Distance")
            axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
            if len(axis.References) > 0 and axis.References[0] != None:
                if (len(axis.References) == 3):
                    self.form.lineRef1.setText(self.getRefText(axis.References[1]))
                else:
                    self.form.lineRef1.setText(self.getRefText(axis.References[0]))
            self.form.spinRef1.setEnabled(True)
            self.form.spinRef1.setValue(axis.Offset)
            self.form.labelRef2.setEnabled(True)
            self.form.labelRef2.setText("Distance")
            if len(axis.References) > 1 and axis.References[1] != None:
                if (len(axis.References) == 3):
                    self.form.lineRef2.setText(self.getRefText(axis.References[2]))
                else:
                    self.form.lineRef2.setText(self.getRefText(axis.References[1]))
            self.form.spinRef2.setEnabled(True)
            self.form.spinRef2.setValue(axis.Offset2)
        elif self.feature.PositionType == self.types[1]:
            # Coaxial
            self.form.buttonRef1.setText("Circle/Cylinder")
            self.form.buttonRef1.setEnabled(True)
            self.form.buttonRef2.setEnabled(False)
            self.form.lineRef1.setEnabled(True)
            axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
            if len(axis.References) > 0 and axis.References[0] != None:
                self.form.lineRef1.setText(self.getRefText(axis.References[0]))
            self.form.lineRef2.setEnabled(False)
            self.form.labelRef1.setEnabled(False)
            self.form.spinRef1.setEnabled(False)
            self.form.labelRef2.setEnabled(False)
            self.form.spinRef2.setEnabled(False)
        else:
            # Nothing else defined yet
            pass

    def getStandardButtons(self):
        return int(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)

    def accept(self):
        return True

    def buttonThru(self, toggle):
        if toggle == True:
            self.feature.HoleType = "Thru"

    def buttonDepth(self, toggle):
        if toggle == True:
            self.feature.HoleType = "Depth"

    def checkThreaded(self, checked):
        self.feature.Threaded = checked
        self.updateUI()

    def checkCounterbore(self, checked):
        if checked == True:
            self.feature.Countersink = False
        self.feature.Counterbore = checked
        self.updateUI()

    def checkCountersink(self, checked):
        if checked == True:
            self.feature.Counterbore = False
        self.feature.Countersink = checked
        self.updateUI()

    def checkCustom(self, checked):
        if checked == True:
            self.feature.Norm = "Custom"
        else:
            self.feature.Norm = str(self.form.comboNorm.currentText())
        self.updateUI()

    def comboNorm(self, index):
        self.feature.Norm = str(self.form.comboNorm.itemText(index))
        self.updateUI()

    def comboTolerance(self, index):
        self.feature.NormTolerance = str(self.form.comboTolerance.itemText(index))
        self.updateUI()

    def comboNormDia(self, index):
        diameter = str(self.form.comboNormDia.itemText(index))
        self.feature.NormDiameter = float(diameter[1:])
        self.updateUI()

    def comboNormBoltWasher(self,  index):
        self.feature.ExtraNorm = str(self.form.comboNormBoltWasher.itemText(index))
        self.updateUI()
        
    def comboThreadNorm(self,  index):
        self.feature.NormThread = str(self.form.comboThreadNorm.itemText(index))        
        self.updateUI()
        
    def comboThreadDia(self,  index):
        diameter = str(self.form.comboThreadDia.itemText(index))
        self.feature.NormDiameter = float(diameter[1:])
        self.updateUI()
    
    def checkCustomThreadLength(self, checked):
        if checked == True:
            self.feature.NormThreadFinish = "Custom"
        else:
            self.feature.NormThreadFinish = str(self.form.comboFinishNorm.currentText())
        self.updateUI()
        
    def comboFinishNorm(self,  index):
        self.feature.NormThreadFinish = str(self.form.comboFinishNorm.itemText(index))
        self.updateUI()

    def spinDiameter(self, val):
        if (val > 0.0):
            self.feature.Diameter = val

    def spinDepth(self, val):
        if (val > 0.0):
            self.feature.Depth = val
        self.updateUI() # required to update the thread length

    def spinCounterboreDiameter(self, val):
        if (val > self.feature.Diameter):
            self.feature.CounterboreDiameter = val

    def spinCounterboreDepth(self, val):
        if (val > 0.0):
            self.feature.CounterboreDepth = val

    def spinCountersinkAngle(self, val):
        if (val > 0.0):
            self.feature.CountersinkAngle = val

    def spinThreadLength(self,  val):
        if (val > 0.0):
            self.feature.ThreadLength = val
            
    def comboType(self, index):
        self.feature.PositionType = self.types[index]
        self.updateUI()

    def addSelection(self, document, obj, element, position):
        #FreeCAD.Console.PrintMessage("AddSelection() for " + document + "." + obj + "." + element + "\n")
        # TODO: What is the position parameter?
        if document == self.feature.Document.Name:
            axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
            refs = axis.References
            feature = eval("FreeCAD.getDocument('" + document + "')." + obj)
            shape = eval("feature.Shape." + element)
            if self.selectionMode == "Plane":
                if shape.Surface.__class__ != Part.Plane:
                    FreeCAD.Console.PrintMessage("Selected face must be planar\n")
                    return
                if self.feature.PositionType == self.types[0]:     
                    # The Hole support is also the first reference of the sketch axis in Linear mode with edges selected
                    if len(refs) == 3:
                        refs[0] = (feature, element)
                        axis.References = refs
                self.feature.Support = (feature, [element])       
            elif self.selectionMode == "LinearReference":
                if shape.ShapeType == "Edge":
                    if shape.Curve.__class__ != Part.LineSegment:
                        FreeCAD.Console.PrintMessage("Selected edge must be linear\n")
                        return
                    if len(refs) > 1:
                        refs[1] = (feature, element)
                    else:
                        refs.append((feature, element))
                elif shape.ShapeType == "Face":
                    if shape.Surface.__class__ != Part.Plane:
                        FreeCAD.Console.PrintMessage("Selected face must be planar\n")
                        return
                    if len(refs) > 0:
                        if len(refs) > 2:
                            refs = [(feature, element)]
                        else:
                            refs[0] = (feature, element)
                    else:
                        refs = [(feature, element)]
                else:
                    FreeCAD.Console.PrintMessage("Wrong shape type selected\n")
                    return                
                axis.References = refs
                axis.Document.recompute()
            elif self.selectionMode == "LinearReference2":
                if shape.ShapeType == "Edge":
                    if shape.Curve.__class__ != Part.LineSegment:
                        FreeCAD.Console.PrintMessage("Selected edge must be linear\n")
                        return
                    if len(refs) > 2:
                        refs[2] = (feature, element)
                    else:
                        refs.append((feature, element))
                elif shape.ShapeType == "Face":
                    if shape.Surface.__class__ != Part.Plane:
                        FreeCAD.Console.PrintMessage("Selected face must be planar\n")
                        return
                    if len(refs) > 1:
                        if len(refs) > 2:
                            del refs[2]
                        refs[1] = (feature, element)
                    else:
                        refs.append((feature, element))
                else:
                    FreeCAD.Console.PrintMessage("Wrong shape type selected\n")
                    return                
                axis.References = refs
                axis.Document.recompute()
            elif self.selectionMode == "CircularReference":
                if shape.ShapeType == "Edge":
                    if shape.Curve.__class__ != Part.Circle:
                        FreeCAD.Console.PrintMessage("Selected edge must be arc or circle\n")
                        return
                elif shape.ShapeType == "Face":
                    if shape.Surface.__class__ != Part.Cylinder:
                        FreeCAD.Console.PrintMessage("Selected face must be cylindrical\n")
                        return
                else:
                    FreeCAD.Console.PrintMessage("Wrong shape type selected\n")
                    return                
                refs = [(feature, element)]
                axis.References = refs
                axis.Document.recompute()
            else:
                FreeCAD.Console.PrintMessage("Unknown selection mode: " + self.selectionMode + "\n")
                self.selectionMode = ""
                return

            FreeCADGui.Selection.removeObserver(self)
            FreeCADGui.Selection.clearSelection()
            FreeCADGui.Selection.removeSelectionGate()
            self.selectionMode = ""
            self.updateUI()
            self.showFeature()

    def hideFeature(self):
        # Make sure selection takes place on support, not on hole feature
        if self.feature.Support != None:
            FreeCADGui.ActiveDocument.hide(self.feature.Name)
            (support, elements) = self.feature.Support
            FreeCADGui.ActiveDocument.show(support.Name)

    def showFeature(self):
        if self.feature.Support != None:
            FreeCADGui.ActiveDocument.show(self.feature.Name)
            (support, elements) = self.feature.Support
            FreeCADGui.ActiveDocument.hide(support.Name)

    def buttonSupport(self):
        FreeCADGui.Selection.addSelectionGate("SELECT Part::Feature SUBELEMENT Face COUNT 1")
        FreeCADGui.Selection.addObserver(self)
        # Currently support must be a planar face (but could also be a point or a construction plane in the future)
        self.selectionMode = "Plane"
        self.hideFeature()

    def buttonRef1(self):
        FreeCADGui.Selection.addSelectionGate("SELECT Part::Feature SUBELEMENT Edge COUNT 1 SELECT Part::Feature SUBELEMENT Face COUNT 1")
        FreeCADGui.Selection.addObserver(self)
        if self.feature.PositionType == self.types[0]:
            self.selectionMode = "LinearReference"
        elif self.feature.PositionType == self.types[1]:
            self.selectionMode = "CircularReference"
        self.hideFeature()

    def buttonRef2(self):
        FreeCADGui.Selection.addSelectionGate("SELECT Part::Feature SUBELEMENT Edge COUNT 1 SELECT Part::Feature SUBELEMENT Face COUNT 1")
        FreeCADGui.Selection.addObserver(self)
        self.selectionMode = "LinearReference2"
        self.hideFeature()

    def spinRef1(self, val):
        axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
        axis.Offset = val
        axis.Document.recompute()

    def spinRef2(self, val):
        axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
        axis.Offset2 = val
        axis.Document.recompute()

#/******************************************************************************
# * Copyright (c)2012 <NAME> <<EMAIL>> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This library is free software; you can redistribute it and/or *
# * modify it under the terms of the GNU Library General Public *
# * License as published by the Free Software Foundation; either *
# * version 2 of the License, or (at your option) any later version. *
# * *
# * This library is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this library; see the file COPYING.LIB. If not, *
# * write to the Free Software Foundation, Inc., 59 Temple Place, *
# * Suite 330, Boston, MA 02111-1307, USA *
# * *
# ******************************************************************************/

import FreeCAD, FreeCADGui
import Part,  PartDesignGui
from PyQt4 import QtCore, QtGui
import Standards
import os

class TaskHole:
    "Hole hole feature"
    types = ["Linear", "Coaxial"]
    typestr = ["Linear to two lines/planes", "Coaxial to a circle/cylinder"]

    def __init__(self, feature):
        self.form = None
        self.extraStandards = []
        self.feature = feature
        p=os.path.realpath(__file__)
        p=os.path.dirname(p)
        self.ui = os.path.join(p, "TaskHole.ui")

    def accept(self):
        self.feature.touch()
        FreeCAD.ActiveDocument.recompute()
        FreeCADGui.ActiveDocument.resetEdit()
        return True

    def reject(self):
        if (self.feature != None):
            self.hideFeature() # Show the support again
            document = self.feature.Document
            body = FreeCADGui.activeView().getActiveObject("pdbody");
            groove = self.feature.HoleGroove
            sketch = groove.Sketch
            plane = sketch.Support[0]
            axis = plane.References[0][0]     
            body.removeObject(self.feature)
            document.removeObject(self.feature.Name)                      
            body.removeObject(groove)
            document.removeObject(groove.Name)
            body.removeObject(sketch)
            try:
                document.removeObject(sketch.Name)      
            except:
                pass # This always throws an exception: "Sketch support has been deleted" from SketchObject::execute()
            body.removeObject(plane)
            document.removeObject(plane.Name)
            body.removeObject(axis)
            document.removeObject(axis.Name)            
        FreeCADGui.ActiveDocument.resetEdit()
        FreeCADGui.Control.closeDialog(self)
        return True
        
    def isAllowedAlterDocument(self):
        return False
        
    def isAllowedAlterView(self):
        return False
        
    def isAllowedAlterSelection(self):
        return True

    def getMainWindow(self):
        "returns the main window"
        # using QtGui.QApplication.activeWindow() isn't very reliable because if another
        # widget than the mainwindow is active (e.g. a dialog) the wrong widget is
        # returned
        toplevel = QtGui.QApplication.topLevelWidgets()
        for i in toplevel:
            if i.metaObject().className() == "Gui::MainWindow":
                return i
        raise Exception("No main window found")

    def setupUi(self):
        mw = self.getMainWindow()
        form = mw.findChild(QtGui.QWidget, "TaskHole")
        if form == None:
            return
        form.tabWidget = form.findChild(QtGui.QTabWidget, "tabWidget")
        # Type
        form.tabType = form.tabWidget.findChild(QtGui.QWidget, "tab_type")
        form.buttonThru = form.tabType.findChild(QtGui.QRadioButton, "buttonThru")
        form.buttonDepth = form.tabType.findChild(QtGui.QRadioButton, "buttonDepth")        
        form.checkThreaded = form.tabType.findChild(QtGui.QCheckBox, "checkThreaded")        
        form.checkCounterbore = form.tabType.findChild(QtGui.QCheckBox, "checkCounterbore")        
        form.checkCountersink = form.tabType.findChild(QtGui.QCheckBox, "checkCountersink")
        # Norm
        form.tabNorm = form.tabWidget.findChild(QtGui.QWidget, "tab_norm")
        form.checkCustom = form.tabNorm.findChild(QtGui.QCheckBox, "checkCustom")
        form.comboNorm = form.tabNorm.findChild(QtGui.QComboBox, "comboNorm")
        for std in Standards.getStandards("through"):
            form.comboNorm.addItem(std)
        form.comboTolerance = form.tabNorm.findChild(QtGui.QComboBox, "comboTolerance")
        for tol in Standards.standards_tolerance:
            form.comboTolerance.addItem(tol)
        form.comboNormDia = form.tabNorm.findChild(QtGui.QComboBox, "comboNormDia")
        form.comboNormBoltWasher = form.tabNorm.findChild(QtGui.QComboBox,  "comboNormBoltWasher")        
        # Thread
        form.tabThread = form.tabWidget.findChild(QtGui.QWidget,  "tab_thread")
        form.comboThreadNorm = form.tabThread.findChild(QtGui.QComboBox,  "comboThreadNorm")
        for std in Standards.getStandards("thread"):
            form.comboThreadNorm.addItem(std)
        form.comboThreadDia = form.tabThread.findChild(QtGui.QComboBox,  "comboThreadDia")
        form.checkCustomThreadLength = form.tabThread.findChild(QtGui.QCheckBox,  "checkCustomThreadLength")
        form.comboFinishNorm = form.tabThread.findChild(QtGui.QComboBox,  "comboFinishNorm")
        for std in Standards.getStandards("threaded"):
            form.comboFinishNorm.addItem(std)
        # Data
        form.tabData = form.tabWidget.findChild(QtGui.QWidget, "tab_data")
        form.spinDiameter = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinDiameter")        
        form.spinDepth = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinDepth")        
        form.spinCounterboreDiameter = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinCounterboreDiameter")
        form.spinCounterboreDepth = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinCounterboreDepth")
        form.spinCountersinkAngle = form.tabData.findChild(QtGui.QDoubleSpinBox, "spinCountersinkAngle")
        form.spinThreadLength = form.tabData.findChild(QtGui.QDoubleSpinBox,  "spinThreadLength")
        # Position
        form.tabPosition = form.tabWidget.findChild(QtGui.QWidget, "tab_position")
        form.comboType = form.tabPosition.findChild(QtGui.QComboBox, "comboType")
        for i in self.typestr:
            form.comboType.addItem(i)
        form.buttonSupport = form.tabPosition.findChild(QtGui.QPushButton, "buttonSupport")
        form.lineSupport = form.tabPosition.findChild(QtGui.QLineEdit, "lineSupport")
        form.buttonRef1 = form.tabPosition.findChild(QtGui.QPushButton, "buttonRef1")
        form.lineRef1 = form.tabPosition.findChild(QtGui.QLineEdit, "lineRef1")
        form.labelRef1 = form.tabPosition.findChild(QtGui.QLabel, "labelRef1")
        form.spinRef1 = form.tabPosition.findChild(QtGui.QDoubleSpinBox, "spinRef1")
        form.buttonRef2 = form.tabPosition.findChild(QtGui.QPushButton, "buttonRef2")
        form.lineRef2 = form.tabPosition.findChild(QtGui.QLineEdit, "lineRef2")
        form.labelRef2 = form.tabPosition.findChild(QtGui.QLabel, "labelRef2")
        form.spinRef2 = form.tabPosition.findChild(QtGui.QDoubleSpinBox, "spinRef2")
        self.form = form

        # Connect Signals and Slots
        # Type
        self.form.buttonThru.toggled.connect(self.buttonThru)
        self.form.buttonDepth.toggled.connect(self.buttonDepth)
        self.form.checkThreaded.toggled.connect(self.checkThreaded)
        self.form.checkCounterbore.toggled.connect(self.checkCounterbore)
        self.form.checkCountersink.toggled.connect(self.checkCountersink)
        # Norm
        self.form.checkCustom.toggled.connect(self.checkCustom)
        self.form.comboNorm.currentIndexChanged.connect(self.comboNorm)
        self.form.comboTolerance.currentIndexChanged.connect(self.comboTolerance)
        self.form.comboNormDia.currentIndexChanged.connect(self.comboNormDia)
        self.form.comboNormBoltWasher.currentIndexChanged.connect(self.comboNormBoltWasher)
        # Thread
        self.form.comboThreadNorm.currentIndexChanged.connect(self.comboThreadNorm)
        self.form.comboThreadDia.currentIndexChanged.connect(self.comboThreadDia)
        self.form.checkCustomThreadLength.toggled.connect(self.checkCustomThreadLength)
        self.form.comboFinishNorm.currentIndexChanged.connect(self.comboFinishNorm)
        # Data
        self.form.spinDiameter.valueChanged.connect(self.spinDiameter)
        self.form.spinDepth.valueChanged.connect(self.spinDepth)
        self.form.spinCounterboreDiameter.valueChanged.connect(self.spinCounterboreDiameter)
        self.form.spinCounterboreDepth.valueChanged.connect(self.spinCounterboreDepth)
        self.form.spinCountersinkAngle.valueChanged.connect(self.spinCountersinkAngle)
        self.form.spinThreadLength.valueChanged.connect(self.spinThreadLength)
        # Position
        self.form.comboType.currentIndexChanged.connect(self.comboType)
        self.form.buttonSupport.clicked.connect(self.buttonSupport)
        self.form.buttonRef1.clicked.connect(self.buttonRef1)
        self.form.spinRef1.valueChanged.connect(self.spinRef1)
        self.form.buttonRef2.clicked.connect(self.buttonRef2)
        self.form.spinRef2.valueChanged.connect(self.spinRef2)

        # Update the UI
        self.updateUI()
        return True
        
    def getRefText(self,  ref):
        (obj,  element) = ref
        if isinstance(element,  basestring):
            return obj.Name + ":" + element
        elif isinstance(element,  list):
            return obj.Name + ":" + element[0]
        else:
            return obj.Name

    def updateUI(self):
        # Type
        self.form.buttonThru.setChecked(self.feature.HoleType == "Thru")
        self.form.buttonDepth.setChecked(self.feature.HoleType == "Depth")
        self.form.checkThreaded.setChecked(self.feature.Threaded == True)
        self.form.checkCounterbore.setChecked(self.feature.Counterbore == True)
        self.form.checkCountersink.setChecked(self.feature.Countersink == True)
        # Norm
        if self.feature.Norm == "Custom":
            self.form.checkCustom.setChecked(True)
            self.form.comboNorm.setEnabled(False)
            self.form.comboTolerance.setEnabled(False)
            self.form.comboNormDia.setEnabled(False)
            self.form.comboNormBoltWasher.setEnabled(False)
        else:
            if self.feature.Counterbore == True:
                holetype = "counterbore"
            elif self.feature.Countersink == True:
                 holetype = "countersink"
            elif self.feature.Threaded == True:
                holetype = "threaded"
            else:
                holetype = "through"
            self.form.comboNorm.setEnabled(True)
            self.form.comboTolerance.setEnabled(True)
            self.form.comboNormDia.setEnabled(True)
            if holetype == "counterbore":
                self.form.comboNormBoltWasher.setEnabled(True)
            else:
                self.form.comboNormBoltWasher.setEnabled(False)
            # comboNorm            
            standards = Standards.getStandards(holetype)
            self.form.comboNorm.blockSignals(True)
            self.form.comboNorm.clear()
            for std in standards:
                self.form.comboNorm.addItem(std)
            if not self.feature.Norm in standards:
                self.feature.Norm = standards[0]
            else:
                self.form.comboNorm.setCurrentIndex(standards.index(self.feature.Norm))
            self.form.comboNorm.blockSignals(False)
            # comboTolerance
            self.form.comboTolerance.blockSignals(True)
            self.form.comboTolerance.setCurrentIndex(Standards.standards_tolerance.index(self.feature.NormTolerance))
            self.form.comboTolerance.blockSignals(False)
            # comboNormDia
            diameters = sorted(Standards.getBaseDiameters(self.feature.Norm))
            self.form.comboNormDia.blockSignals(True)
            self.form.comboNormDia.clear()
            for dia in diameters:
                self.form.comboNormDia.addItem("M%g" % dia)
            if self.feature.NormDiameter in diameters:
                self.form.comboNormDia.setCurrentIndex(diameters.index(self.feature.NormDiameter))
            self.form.comboNormDia.blockSignals(False)
            # comboNormBoltWasher
            if holetype == "counterbore":
                rowStandards = sorted(Standards.getRowStandards(self.feature.Norm))
                self.form.comboNormBoltWasher.blockSignals(True)
                self.form.comboNormBoltWasher.clear()
                for std in rowStandards:
                    self.form.comboNormBoltWasher.addItem(std)
                if self.feature.ExtraNorm in rowStandards:
                    self.form.comboNormBoltWasher.setCurrentIndex(rowStandards.index(self.feature.ExtraNorm))
                self.form.comboNormBoltWasher.blockSignals(False)
            # Dependent values
            if holetype == "through":
                self.feature.Diameter = Standards.getThroughHoleDia(self.feature.Norm, self.feature.NormDiameter, self.feature.NormTolerance)
            elif holetype == "counterbore":
                throughStandard = Standards.getThroughHoleStandard(self.feature.Norm)
                self.feature.Diameter = Standards.getThroughHoleDia(throughStandard,  self.feature.NormDiameter,  self.feature.NormTolerance)
                self.feature.CounterboreDiameter = Standards.getCounterboreDia(self.feature.Norm,  self.feature.NormDiameter,  self.feature.ExtraNorm)                
                # TODO: Calculate counter bore depth from standard for bolt and washer(s)
                # Requires accessing all the norms for bolts
                # self.feature.CounterboreDepth = calcCounterboreDepth(...)
            elif holetype == "countersink":
                throughStandard = Standards.getThroughHoleStandard(self.feature.Norm)
                self.feature.Diameter = Standards.getThroughHoleDia(throughStandard,  self.feature.NormDiameter,  self.feature.NormTolerance)
                self.feature.CounterboreDiameter = Standards.getCountersinkDia(self.feature.Norm,  self.feature.NormDiameter)
                self.feature.CountersinkAngle = Standards.getCountersinkAngle(self.feature.Norm,  self.feature.NormDiameter) / 2.0
        # Thread
        if self.feature.Threaded == True:
            if not self.feature.Counterbore and not self.feature.Countersink:
                self.form.comboTolerance.setEnabled(False)
            else:
                self.form.tabNorm.setEnabled(True)
            self.form.comboTolerance.setEnabled(False)
            self.form.tabThread.setEnabled(True) 
            self.form.comboThreadNorm.blockSignals(True)
            standards = Standards.getStandards("thread")
            if not self.feature.NormThread in standards:
                self.feature.NormThread = standards[0]
            else:
                self.form.comboThreadNorm.setCurrentIndex(standards.index(self.feature.NormThread))
            self.form.comboThreadNorm.blockSignals(False)
            threadDiameters = sorted(Standards.getBaseDiameters(self.feature.NormThread))
            self.form.comboThreadDia.blockSignals(True)
            self.form.comboThreadDia.clear()
            for dia in threadDiameters:
                self.form.comboThreadDia.addItem("M%g" % dia)
            if self.feature.NormDiameter in threadDiameters:
                self.form.comboThreadDia.setCurrentIndex(threadDiameters.index(self.feature.NormDiameter))
            self.form.comboThreadDia.blockSignals(False)
            if self.feature.NormThreadFinish == "Custom":
                self.form.checkCustomThreadLength.setChecked(True)
                self.form.comboFinishNorm.setEnabled(False)
            else:
                self.form.checkCustomThreadLength.setChecked(False)
                self.form.comboFinishNorm.setEnabled(True)
                self.form.comboFinishNorm.blockSignals(True)
                standards = Standards.getStandards("threaded")
                if not self.feature.NormThreadFinish in standards:
                    self.feature.NormThreadFinish = standards[0]
                else:
                    self.form.comboFinishNorm.setCurrentIndex(standards.index(self.feature.NormThreadFinish))
                self.form.comboFinishNorm.blockSignals(False)
                flength = Standards.getThreadFinishLength(self.feature.NormThreadFinish,  self.feature.NormDiameter)
                tlength = self.feature.Depth - flength
                if tlength > 0:
                    self.feature.ThreadLength = tlength # TODO: Warning message
            # Dependents
            self.feature.Diameter = Standards.getThreadCoreDiameter(self.feature.NormThread,  self.feature.NormDiameter)
        else:
            self.form.tabThread.setEnabled(False)
            # Dependents
            self.form.spinDiameter.setEnabled(True)
        # Data
        self.form.spinDiameter.setValue(self.feature.Diameter)
        self.form.spinDepth.setValue(self.feature.Depth)
        if self.feature.HoleType == "Thru":
            self.form.spinDepth.setEnabled(False)
        else:
            self.form.spinDepth.setEnabled(True)
        if self.feature.Threaded == True:
            self.form.spinThreadLength.setEnabled(True)
        else:
            self.form.spinThreadLength.setEnabled(False)
        if self.feature.Counterbore == True:
            self.form.spinCounterboreDiameter.setEnabled(True)
            self.form.spinCounterboreDiameter.setValue(self.feature.CounterboreDiameter)
            self.form.spinCounterboreDepth.setEnabled(True)
            self.form.spinCounterboreDepth.setValue(self.feature.CounterboreDepth)
            self.form.spinCountersinkAngle.setEnabled(False)
        elif self.feature.Countersink == True:
            self.form.spinCounterboreDiameter.setEnabled(True)
            self.form.spinCounterboreDiameter.setValue(self.feature.CounterboreDiameter)
            self.form.spinCounterboreDepth.setEnabled(False)       
            self.form.spinCountersinkAngle.setEnabled(True)
            self.form.spinCountersinkAngle.setValue(self.feature.CountersinkAngle)
        else:
            self.form.spinCounterboreDiameter.setEnabled(False)
            self.form.spinCounterboreDepth.setEnabled(False)
            self.form.spinCountersinkAngle.setEnabled(False)
        if self.feature.Norm == "Custom":
            self.form.spinDiameter.setEnabled(True)
        else:
            self.form.spinDiameter.setEnabled(False)
            if holetype == "counterbore":
                # Diameter is taken from Norm
                self.form.spinCounterboreDiameter.setEnabled(False)
            elif holetype == "countersink":
                # Values are taken from Norm
                self.form.spinCounterboreDiameter.setEnabled(False)
                self.form.spinCounterboreDepth.setEnabled(False)
                self.form.spinCountersinkAngle.setEnabled(False)
        if self.feature.Threaded == True:
            self.form.spinDiameter.setEnabled(False)
        if self.feature.NormThreadFinish != "Custom":            
            self.form.spinThreadLength.setEnabled(False)
        self.form.spinThreadLength.setValue(self.feature.ThreadLength)
        # Position
        self.form.buttonSupport.setText("Face")
        if self.feature.Support == None:
            # First-time initialization
            selection = FreeCADGui.Selection.getSelectionEx()
            self.feature.Support = (selection[0].Object, selection[0].SubElementNames)
        self.form.lineSupport.setText(self.getRefText(self.feature.Support))
        if self.feature.PositionType == self.types[0]:
            # Linear            
            self.form.buttonRef1.setText("Line/Plane")
            self.form.buttonRef1.setEnabled(True)
            self.form.buttonRef2.setText("Line/Plane")
            self.form.buttonRef2.setEnabled(True)
            self.form.lineRef1.setEnabled(True)
            self.form.lineRef2.setEnabled(True)
            self.form.labelRef1.setEnabled(True)
            self.form.labelRef1.setText("Distance")
            axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
            if len(axis.References) > 0 and axis.References[0] != None:
                if (len(axis.References) == 3):
                    self.form.lineRef1.setText(self.getRefText(axis.References[1]))
                else:
                    self.form.lineRef1.setText(self.getRefText(axis.References[0]))
            self.form.spinRef1.setEnabled(True)
            self.form.spinRef1.setValue(axis.Offset)
            self.form.labelRef2.setEnabled(True)
            self.form.labelRef2.setText("Distance")
            if len(axis.References) > 1 and axis.References[1] != None:
                if (len(axis.References) == 3):
                    self.form.lineRef2.setText(self.getRefText(axis.References[2]))
                else:
                    self.form.lineRef2.setText(self.getRefText(axis.References[1]))
            self.form.spinRef2.setEnabled(True)
            self.form.spinRef2.setValue(axis.Offset2)
        elif self.feature.PositionType == self.types[1]:
            # Coaxial
            self.form.buttonRef1.setText("Circle/Cylinder")
            self.form.buttonRef1.setEnabled(True)
            self.form.buttonRef2.setEnabled(False)
            self.form.lineRef1.setEnabled(True)
            axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
            if len(axis.References) > 0 and axis.References[0] != None:
                self.form.lineRef1.setText(self.getRefText(axis.References[0]))
            self.form.lineRef2.setEnabled(False)
            self.form.labelRef1.setEnabled(False)
            self.form.spinRef1.setEnabled(False)
            self.form.labelRef2.setEnabled(False)
            self.form.spinRef2.setEnabled(False)
        else:
            # Nothing else defined yet
            pass

    def getStandardButtons(self):
        return int(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)

    def accept(self):
        return True

    def buttonThru(self, toggle):
        if toggle == True:
            self.feature.HoleType = "Thru"

    def buttonDepth(self, toggle):
        if toggle == True:
            self.feature.HoleType = "Depth"

    def checkThreaded(self, checked):
        self.feature.Threaded = checked
        self.updateUI()

    def checkCounterbore(self, checked):
        if checked == True:
            self.feature.Countersink = False
        self.feature.Counterbore = checked
        self.updateUI()

    def checkCountersink(self, checked):
        if checked == True:
            self.feature.Counterbore = False
        self.feature.Countersink = checked
        self.updateUI()

    def checkCustom(self, checked):
        if checked == True:
            self.feature.Norm = "Custom"
        else:
            self.feature.Norm = str(self.form.comboNorm.currentText())
        self.updateUI()

    def comboNorm(self, index):
        self.feature.Norm = str(self.form.comboNorm.itemText(index))
        self.updateUI()

    def comboTolerance(self, index):
        self.feature.NormTolerance = str(self.form.comboTolerance.itemText(index))
        self.updateUI()

    def comboNormDia(self, index):
        diameter = str(self.form.comboNormDia.itemText(index))
        self.feature.NormDiameter = float(diameter[1:])
        self.updateUI()

    def comboNormBoltWasher(self,  index):
        self.feature.ExtraNorm = str(self.form.comboNormBoltWasher.itemText(index))
        self.updateUI()
        
    def comboThreadNorm(self,  index):
        self.feature.NormThread = str(self.form.comboThreadNorm.itemText(index))        
        self.updateUI()
        
    def comboThreadDia(self,  index):
        diameter = str(self.form.comboThreadDia.itemText(index))
        self.feature.NormDiameter = float(diameter[1:])
        self.updateUI()
    
    def checkCustomThreadLength(self, checked):
        if checked == True:
            self.feature.NormThreadFinish = "Custom"
        else:
            self.feature.NormThreadFinish = str(self.form.comboFinishNorm.currentText())
        self.updateUI()
        
    def comboFinishNorm(self,  index):
        self.feature.NormThreadFinish = str(self.form.comboFinishNorm.itemText(index))
        self.updateUI()

    def spinDiameter(self, val):
        if (val > 0.0):
            self.feature.Diameter = val

    def spinDepth(self, val):
        if (val > 0.0):
            self.feature.Depth = val
        self.updateUI() # required to update the thread length

    def spinCounterboreDiameter(self, val):
        if (val > self.feature.Diameter):
            self.feature.CounterboreDiameter = val

    def spinCounterboreDepth(self, val):
        if (val > 0.0):
            self.feature.CounterboreDepth = val

    def spinCountersinkAngle(self, val):
        if (val > 0.0):
            self.feature.CountersinkAngle = val

    def spinThreadLength(self,  val):
        if (val > 0.0):
            self.feature.ThreadLength = val
            
    def comboType(self, index):
        self.feature.PositionType = self.types[index]
        self.updateUI()

    def addSelection(self, document, obj, element, position):
        #FreeCAD.Console.PrintMessage("AddSelection() for " + document + "." + obj + "." + element + "\n")
        # TODO: What is the position parameter?
        if document == self.feature.Document.Name:
            axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
            refs = axis.References
            feature = eval("FreeCAD.getDocument('" + document + "')." + obj)
            shape = eval("feature.Shape." + element)
            if self.selectionMode == "Plane":
                if shape.Surface.__class__ != Part.Plane:
                    FreeCAD.Console.PrintMessage("Selected face must be planar\n")
                    return
                if self.feature.PositionType == self.types[0]:     
                    # The Hole support is also the first reference of the sketch axis in Linear mode with edges selected
                    if len(refs) == 3:
                        refs[0] = (feature, element)
                        axis.References = refs
                self.feature.Support = (feature, [element])       
            elif self.selectionMode == "LinearReference":
                if shape.ShapeType == "Edge":
                    if shape.Curve.__class__ != Part.LineSegment:
                        FreeCAD.Console.PrintMessage("Selected edge must be linear\n")
                        return
                    if len(refs) > 1:
                        refs[1] = (feature, element)
                    else:
                        refs.append((feature, element))
                elif shape.ShapeType == "Face":
                    if shape.Surface.__class__ != Part.Plane:
                        FreeCAD.Console.PrintMessage("Selected face must be planar\n")
                        return
                    if len(refs) > 0:
                        if len(refs) > 2:
                            refs = [(feature, element)]
                        else:
                            refs[0] = (feature, element)
                    else:
                        refs = [(feature, element)]
                else:
                    FreeCAD.Console.PrintMessage("Wrong shape type selected\n")
                    return                
                axis.References = refs
                axis.Document.recompute()
            elif self.selectionMode == "LinearReference2":
                if shape.ShapeType == "Edge":
                    if shape.Curve.__class__ != Part.LineSegment:
                        FreeCAD.Console.PrintMessage("Selected edge must be linear\n")
                        return
                    if len(refs) > 2:
                        refs[2] = (feature, element)
                    else:
                        refs.append((feature, element))
                elif shape.ShapeType == "Face":
                    if shape.Surface.__class__ != Part.Plane:
                        FreeCAD.Console.PrintMessage("Selected face must be planar\n")
                        return
                    if len(refs) > 1:
                        if len(refs) > 2:
                            del refs[2]
                        refs[1] = (feature, element)
                    else:
                        refs.append((feature, element))
                else:
                    FreeCAD.Console.PrintMessage("Wrong shape type selected\n")
                    return                
                axis.References = refs
                axis.Document.recompute()
            elif self.selectionMode == "CircularReference":
                if shape.ShapeType == "Edge":
                    if shape.Curve.__class__ != Part.Circle:
                        FreeCAD.Console.PrintMessage("Selected edge must be arc or circle\n")
                        return
                elif shape.ShapeType == "Face":
                    if shape.Surface.__class__ != Part.Cylinder:
                        FreeCAD.Console.PrintMessage("Selected face must be cylindrical\n")
                        return
                else:
                    FreeCAD.Console.PrintMessage("Wrong shape type selected\n")
                    return                
                refs = [(feature, element)]
                axis.References = refs
                axis.Document.recompute()
            else:
                FreeCAD.Console.PrintMessage("Unknown selection mode: " + self.selectionMode + "\n")
                self.selectionMode = ""
                return

            FreeCADGui.Selection.removeObserver(self)
            FreeCADGui.Selection.clearSelection()
            FreeCADGui.Selection.removeSelectionGate()
            self.selectionMode = ""
            self.updateUI()
            self.showFeature()

    def hideFeature(self):
        # Make sure selection takes place on support, not on hole feature
        if self.feature.Support != None:
            FreeCADGui.ActiveDocument.hide(self.feature.Name)
            (support, elements) = self.feature.Support
            FreeCADGui.ActiveDocument.show(support.Name)

    def showFeature(self):
        if self.feature.Support != None:
            FreeCADGui.ActiveDocument.show(self.feature.Name)
            (support, elements) = self.feature.Support
            FreeCADGui.ActiveDocument.hide(support.Name)

    def buttonSupport(self):
        FreeCADGui.Selection.addSelectionGate("SELECT Part::Feature SUBELEMENT Face COUNT 1")
        FreeCADGui.Selection.addObserver(self)
        # Currently support must be a planar face (but could also be a point or a construction plane in the future)
        self.selectionMode = "Plane"
        self.hideFeature()

    def buttonRef1(self):
        FreeCADGui.Selection.addSelectionGate("SELECT Part::Feature SUBELEMENT Edge COUNT 1 SELECT Part::Feature SUBELEMENT Face COUNT 1")
        FreeCADGui.Selection.addObserver(self)
        if self.feature.PositionType == self.types[0]:
            self.selectionMode = "LinearReference"
        elif self.feature.PositionType == self.types[1]:
            self.selectionMode = "CircularReference"
        self.hideFeature()

    def buttonRef2(self):
        FreeCADGui.Selection.addSelectionGate("SELECT Part::Feature SUBELEMENT Edge COUNT 1 SELECT Part::Feature SUBELEMENT Face COUNT 1")
        FreeCADGui.Selection.addObserver(self)
        self.selectionMode = "LinearReference2"
        self.hideFeature()

    def spinRef1(self, val):
        axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
        axis.Offset = val
        axis.Document.recompute()

    def spinRef2(self, val):
        axis = self.feature.HoleGroove.Sketch.Support[0].References[0][0]
        axis.Offset2 = val
        axis.Document.recompute()
#/****************************************************************************** # * Copyright (c)2012 <NAME> <<EMAIL>> * # * * # * This file is part of the FreeCAD CAx development system. * # * * # * This library is free software; you can redistribute it and/or * # * modify it under the terms of the GNU Library General Public * # * License as published by the Free Software Foundation; either * # * version 2 of the License, or (at your option) any later version. * # * * # * This library is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU Library General Public License for more details. * # * * # * You should have received a copy of the GNU Library General Public * # * License along with this library; see the file COPYING.LIB. If not, * # * write to the Free Software Foundation, Inc., 59 Temple Place, * # * Suite 330, Boston, MA 02111-1307, USA * # * * # ******************************************************************************/ # Show the support again # This always throws an exception: "Sketch support has been deleted" from SketchObject::execute() # using QtGui.QApplication.activeWindow() isn't very reliable because if another # widget than the mainwindow is active (e.g. a dialog) the wrong widget is # returned # Type # Norm # Thread # Data # Position # Connect Signals and Slots # Type # Norm # Thread # Data # Position # Update the UI # Type # Norm # comboNorm # comboTolerance # comboNormDia # comboNormBoltWasher # Dependent values # TODO: Calculate counter bore depth from standard for bolt and washer(s) # Requires accessing all the norms for bolts # self.feature.CounterboreDepth = calcCounterboreDepth(...) # Thread # TODO: Warning message # Dependents # Dependents # Data # Diameter is taken from Norm # Values are taken from Norm # Position # First-time initialization # Linear # Coaxial # Nothing else defined yet # required to update the thread length #FreeCAD.Console.PrintMessage("AddSelection() for " + document + "." + obj + "." + element + "\n") # TODO: What is the position parameter? # The Hole support is also the first reference of the sketch axis in Linear mode with edges selected # Make sure selection takes place on support, not on hole feature # Currently support must be a planar face (but could also be a point or a construction plane in the future)