############################################################################
#
# Author: Ruth HUEY
#
# Copyright: M. Sanner TSRI 2000
#
#############################################################################

# $Header
#
# $Id: hbondCommands.py,v 1.98.2.1 2016/02/11 21:29:43 annao Exp $
#


from ViewerFramework.VFCommand import CommandGUI
from Pmv.moleculeViewer import AddAtomsEvent
from mglutil.gui.BasicWidgets.Tk.colorWidgets import ColorChooser

from mglutil.gui.InputForm.Tk.gui import InputFormDescr
from mglutil.util.callback import CallBackFunction
from mglutil.gui.BasicWidgets.Tk.customizedWidgets \
    import ExtendedSliderWidget, ListChooser
from mglutil.gui.BasicWidgets.Tk.thumbwheel import ThumbWheel
from mglutil.util.misc import ensureFontCase

from Pmv.mvCommand import MVCommand, MVAtomICOM, MVBondICOM
from Pmv.measureCommands import MeasureAtomCommand

from MolKit.molecule import Atom, AtomSet, Bond
from MolKit.molecule import BondSet, HydrogenBond, HydrogenBondSet
from MolKit.distanceSelector import DistanceSelector
from MolKit.hydrogenBondBuilder import HydrogenBondBuilder
from MolKit.pdbWriter import PdbWriter

from DejaVu.Geom import Geom
from DejaVu.Spheres import Spheres
from DejaVu.Points import CrossSet
from DejaVu.Cylinders import Cylinders
from DejaVu.spline import DialASpline, SplineObject
from DejaVu.GleObjects import GleExtrude, GleObject
from DejaVu.Shapes import Shape2D, Triangle2D, Circle2D, Rectangle2D,\
     Square2D, Ellipse2D

from Pmv.stringSelectorGUI import StringSelectorGUI
from PyBabel.util import vec3

import Tkinter, numpy, math, string, os
from string import strip, split
from types import StringType

from opengltk.OpenGL import GL

from DejaVu.glfLabels import GlfLabels


def check_hbond_geoms(VFGUI):
    hbond_geoms_list = VFGUI.VIEWER.findGeomsByName('hbond_geoms')
    if hbond_geoms_list==[]:
        hbond_geoms = Geom("hbond_geoms", shape=(0,0), protected=True)
        VFGUI.VIEWER.AddObject(hbond_geoms, parent=VFGUI.miscGeom)
        hbond_geoms_list = [hbond_geoms]
    return hbond_geoms_list[0]
 


# lists of babel_types for donor-acceptor 
#   derived from MacDonald + Thornton 
#   'Atlas of Side-Chain + Main-Chain Hydrogen Bonding'
# plus sp2Acceptor Nam

sp2Donors = ['Nam', 'Ng+', 'Npl']
sp3Donors = ['N3+', 'O3','S3']
allDonors = []
allDonors.extend(sp2Donors)
allDonors.extend(sp3Donors)
#added Nam because of bdna cytidine N3
#NB: Npl cannot be an acceptor if the sum of its bonds' bondOrder>2
sp2Acceptors = ['O2', 'O-', 'Npl', 'Nam']
sp3Acceptors = ['S3', 'O3']
allAcceptors = []
allAcceptors.extend(sp2Acceptors)
allAcceptors.extend(sp3Acceptors)
a_allAcceptors = []
for n in allAcceptors:
    a_allAcceptors.append('a'+n)


def dist(c1, c2):
    d = numpy.array(c2) - numpy.array(c1) 
    ans = math.sqrt(numpy.sum(d*d))
    return round(ans, 3)


def getAngle(ac, hat, don ):
    acCoords = getTransformedCoords(ac)
    hCoords = getTransformedCoords(hat)
    dCoords = getTransformedCoords(don)
    pt1 = numpy.array(acCoords, 'f')
    pt2 = numpy.array(hCoords, 'f')
    pt3 = numpy.array(dCoords, 'f')
    #pt1 = numpy.array(ac.coords, 'f')
    #pt2 = numpy.array(hat.coords, 'f')
    #pt3 = numpy.array(don.coords, 'f')
    v1 = numpy.array(pt1 - pt2)
    v2 = numpy.array(pt3 - pt2)
    dist1 = math.sqrt(numpy.sum(v1*v1))
    dist2 = math.sqrt(numpy.sum(v2*v2))
    sca = numpy.dot(v1, v2)/(dist1*dist2)
    if sca>1.0:
        sca = 1.0
    elif sca<-1.0:
        sca = -1.0
    ang =  math.acos(sca)*180./math.pi
    return round(ang, 5)


def applyTransformation(pt, mat):
    pth = [pt[0], pt[1], pt[2], 1.0]
    return numpy.dot(mat, pth)[:3]


def getTransformedCoords(atom):
    # when there is no viewer, the geomContainer is None
    if atom.top.geomContainer is None:
        return atom.coords
    g = atom.top.geomContainer.geoms['master']
    c = applyTransformation(atom.coords, g.GetMatrix(g))
    return  c.astype('f')



class GetHydrogenBondDonors(MVCommand):
    """This class allows user to get AtomSets of sp2 hydridized and sp3 hybridized hydrogenBond Donors able.
   \nPackage : Pmv
   \nModule  : bondsCommands
   \nClass   : GetHydrogenBondDonors
   \nCommand :getHBDonors 
   \nSynopsis:\n
        sp2Donors, sp3Donors <- getHBDonors(nodes, donorList, **kw)
   \nRequired Arguments:\n    
   nodes ---
   donorList ---
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly
    
    def onAddCmdToViewer(self):
        if not self.vf.commands.has_key('typeAtoms'):
            self.vf.loadCommand('editCommands', 'typeAtoms', 'Pmv',
                                topCommand=0)


    def __call__(self, nodes, donorList=allDonors,**kw):
        """sp2Donors, sp3Donors <- getHBDonors(nodes, donorList, **kw) """ 
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes = self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        ats = nodes.findType(Atom)
        try:
            ats.babel_type
        except AttributeError:
            tops = ats.top.uniq()
            for t in tops:
                self.vf.typeAtoms(t.allAtoms, topCommand=0)
        return apply(self.doitWrapper, (ats, donorList), kw)


    def checkForPossibleH(self, ats, blen):
        #check that if at has all bonds, at least one is to a hydrogen
        # have to do this by element??
        probAts = AtomSet(ats.get(lambda x, blen=blen: len(x.bonds)==blen))
        #probOAts = ats.get(lambda x, blen=blen: len(x.bonds)==blen)
        #probSAts = ats.get(lambda x, blen=blen: len(x.bonds)==blen)
        if probAts:
            rAts = AtomSet([])
            for at in probAts:
                if not len(at.findHydrogens()):
                    rAts.append(at)
            if len(rAts):
                ats =  ats.subtract(rAts)
        return ats
    

    def doit(self, ats, donorList):
        #getHBDonors 
        sp2 = []
        sp3 = []
        for item in sp2Donors:
            if item in donorList: sp2.append(item)
        for item in sp3Donors:
            if item in donorList: sp3.append(item)
        
        dAts2 = ats.get(lambda x, l=sp2: x.babel_type in l)
        if not dAts2: dAts2=AtomSet([])
        else: 
            dAts2 = self.checkForPossibleH(dAts2, 3)
        
        dAts3 = ats.get(lambda x, l=sp3: x.babel_type in l)
        if not dAts3: dAts3=AtomSet([])
        else: dAts3 = self.checkForPossibleH(dAts3, 4)

        return dAts2, dAts3



class GetHydrogenBondAcceptors(MVCommand):
    """This class allows user to get AtomSets of sp2 hydridized and sp3 hybridized
hydrogenBond Acceptors.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : GetHydrogenBondEnergies
   \nCommand : getHBondEnergies
   \nSynopsis:\n
        sp2Acceptors, sp3Acceptors <- getHBAcceptors(nodes, acceptorList, **kw)
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly

    def onAddCmdToViewer(self):
        if not self.vf.commands.has_key('typeAtoms'):
            self.vf.loadCommand('editCommands', 'typeAtoms', 'Pmv',
                                topCommand=0)


    def __call__(self, nodes, acceptorList=allAcceptors,**kw):
        """sp2Acceptors, sp3Acceptors <- getHBAcceptors(nodes, acceptorList, **kw) """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes = self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        ats = nodes.findType(Atom)
        #no way to know if typeBonds has been called on each molecule!
        try:
            ats._bndtyped
        except:
            tops = ats.top.uniq()
            for t in tops:
                self.vf.typeBonds(t.allAtoms, topCommand=0)
        return apply(self.doitWrapper, (ats, acceptorList), kw)


    def filterAcceptors(self, accAts):
        ntypes = ['Npl', 'Nam']
        npls = accAts.get(lambda x, ntypes=ntypes: x.babel_type=='Npl')
        nams = accAts.get(lambda x, ntypes=ntypes: x.babel_type=='Nam')
        #nAts = accAts.get(lambda x, ntypes=ntypes: x.babel_type in ntypes)
        restAts = accAts.get(lambda x, ntypes=ntypes: x.babel_type not in ntypes)
        if not restAts: restAts = AtomSet([])
        #if nAts:
        if npls:
            #for at in nAts:
            for at in npls:
                s = 0
                for b in at.bonds:
                    if b.bondOrder=='aromatic':
                        s = s + 2
                    else: s = s + b.bondOrder
                #if s<3:
                #apparently this is wrong
                if s<4:
                    restAts.append(at)
        if nams:
            #for at in nAts:
            for at in nams:
                s = 0
                for b in at.bonds:
                    if b.bondOrder=='aromatic':
                        s = s + 2
                    else: s = s + b.bondOrder
                    #s = s + b.bondOrder
                if s<3:
                    restAts.append(at)
        return restAts
                
        

    def doit(self, ats, acceptorList):
        #getHBAcceptors 
        sp2 = []
        sp3 = []
        for item in sp2Acceptors:
            if item in acceptorList: sp2.append(item)
        for item in sp3Acceptors:
            if item in acceptorList: sp3.append(item)

        dAts2 = AtomSet(ats.get(lambda x, l=sp2: x.babel_type in l))
        if dAts2: 
            dAts2 = self.filterAcceptors(dAts2)
        dAts3 = AtomSet(ats.get(lambda x, l=sp3: x.babel_type in l))
        return dAts2, dAts3



class GetHydrogenBondEnergies(MVCommand):
    """This class allows user to get a list of energies of HydrogenBonds. For this calculation, the bond must have a hydrogen atom AND it must know its type.
Energy calculation based on "Protein Flexibility and dynamics using constraint
theory", J. Molecular Graphics and Modelling 19, 60-69, 2001. MF Thorpe, Ming
Lei, AJ Rader, Donald J.Jacobs and Leslie A. Kuhn.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : GetHydrogenBondEnergies
   \nCommand : getHBondEnergies
   \nSynopsis:\n
        [energies] <- getHBondEnergies(nodes, **kw) 
   \nRequired Arguments:\n     
        nodes --- TreeNodeSet holding the current selection
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly

    def onAddCmdToViewer(self):
        self.vf.loadModule('measureCommands', 'Pmv', log=0)
        if not self.vf.commands.has_key('typeAtoms'):
            self.vf.loadCommand('editCommands', 'typeAtoms', 'Pmv',
                                topCommand=0, log=0)
        

    def __call__(self, nodes, **kw):
        """[energies] <- getHBondEnergies(nodes, **kw) 
        \nnodes --- TreeNodeSet holding the current selection
        """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes = self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        ats = nodes.findType(Atom)
        hats = AtomSet([])
        for at in ats:
            if hasattr(at, 'hbonds'): hats.append(at)
        if not len(hats):
            self.warningMsg('no hbonds in specified nodes')
            return 'ERROR'
        return apply(self.doitWrapper, (hats,), kw)


    def getEnergy(self, b):
        accAt = b.accAt
        b0 = accAt.bonds[0]
        acN = b0.atom1
        if id(acN)==id(accAt): acN = b0.atom2
        b.acN = acN
        #b.acN = b.accAt.bonds[0].neighborAtom(b.accAt)
        if b.dlen is None:
            b.dlen = dist(getTransformedCoords(b.donAt), 
                            getTransformedCoords(b.accAt))
            #b.dlen = dist(b.donAt.coords, b.accAt.coords)
        r = 2.8/b.dlen
        if b.phi is None: b.phi = self.vf.measureAngle(b.hAt, b.accAt, 
                acN, topCommand=0)
        if b.theta is None: b.theta = self.vf.measureAngle(b.donAt, b.hAt, 
                b.accAt, topCommand=0)
        b.gamma = self.vf.measureTorsion(b.donAt, b.hAt, b.accAt, 
                acN, topCommand=0)
        #make all angles into radians
        theta = b.theta * math.pi/180.
        newAngle = math.pi - theta
        gamma = b.gamma * math.pi/180.
        phi = b.phi * math.pi/180.
        n = 109.5*math.pi/180.

        if b.type is None:
            #get # of donAt+# of accAt
            try:
                b.donAt.babel_type
                b.accAt.babel_type
            except AttributeError:
                b_ats = AtomSet([b.donAt, b.accAt])
                tops = b_ats.top.uniq()
                for t in tops:
                    self.vf.typeAtoms(t.allAtoms, topCommand=0)
            if b.donAt.babel_type in sp2Donors:
                d1=20
            else:
                d1=30
            if b.accAt.babel_type in sp2Acceptors:
                d2=2
            else:
                d2=3
            b.type = d1+d2
                

        if b.type==33:
            f = (math.cos(theta)**2)*\
                (math.e**(-(newAngle)**6))*(math.cos(phi-n)**2)
        elif b.type==32:
            f = (math.cos(theta)**2)*\
                (math.e**(-(newAngle)**6))*(math.cos(phi)**2)
        elif b.type==23:
            f = ((math.cos(theta)**2)*\
                (math.e**(-(newAngle)**6)))**2
        else:
            f = (math.cos(theta)**2)*\
                (math.e**(-(newAngle)**6))*(math.cos(max(theta, gamma))**2)
        newVal =  8.*(5.*(r**12) - 6.*(r**10))*f
        b.energy =  round(newVal, 3)
        return b.energy


    def doit(self, hats):
        #getHBondEnergies
        energyList = []
        hbonds = hats.hbonds
        #SHOULD hbonds be checked for type??? HERE???????
        for b in hbonds:
            if b.energy:
                energyList.append(b.energy)
            elif b.hAt is None:
                energyList.append(None)
            else:
                energyList.append(self.getEnergy(b))
        #print 'returning ', len(energyList), ' hbond energies: ', energyList
        return energyList



class ShowHBDonorsAcceptors(MVCommand):
    """This class allows user to show AtomSets of HydrogenBond donors and acceptors.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : ShowHBDonorsAcceptors
   \nCommand : showHBDA
   \nSynopsis:\n
        None <- showHBDA(dnodes, anodes, donorList, acceptorList, **kw)
    """


    def onAddCmdToViewer(self):
        self.vf.loadModule('editCommands', 'Pmv', log=0)
        self.vf.loadCommand('interactiveCommands','setICOM', 'Pmv', log=0)
        #setup geoms
        #miscGeom = self.vf.GUI.miscGeom

        self.masterGeom = Geom('hbondDonorAcceptorGeoms',shape=(0,0), 
                                pickable=0, protected=True)
        self.masterGeom.isScalable = 0
        if self.vf.hasGui:
            hbond_geoms = check_hbond_geoms(self.vf.GUI)
            self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=hbond_geoms)
        self.hbdonor2=CrossSet('hbSP2Donors', 
            materials=((0.,1.,1.),), offset=0.4, lineWidth=2,
            inheritMaterial=0, protected=True)
        self.hbdonor2.Set(visible=1, tagModified=False)
        self.hbdonor2.pickable = 0
        self.hbdonor3=CrossSet('hbSP3Donors', 
            materials=((0.,.3,1.),), offset=0.4, lineWidth=2,
            inheritMaterial=0, protected=True)
        self.hbacceptor2=Spheres('hbSP2Acceptors', shape=(0,3),
            radii=0.2, quality=15, materials=((1.,.8,0.),), inheritMaterial=0, protected=True)
        self.hbacceptor3=Spheres('hbSP3Acceptors', shape=(0,3),
            radii=0.2, quality=15, materials=((1.,.2,0.),), inheritMaterial=0, protected=True)
        for item in [self.hbdonor2, self.hbdonor3, self.hbacceptor2,\
                self.hbacceptor3]:
            item.Set(visible=1, tagModified=False)
            item.pickable = 0
            if self.vf.hasGui:
                self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom)
        #setup Tkinter variables
        varNames = ['hideDSel', 'N3', 'O3','S3','Nam', 'Ng', 'Npl',\
                'hideASel',  'aS3', 'aO3', 'aO2', 'aO', 'aNpl',
                'aNam', 'sp2D', 'sp3D', 'sp2A', 'sp3A','useSelection']
                
        for n in varNames:
            exec('self.'+n+'=Tkinter.IntVar()')
            exec('self.'+n+'.set(1)')
        self.showDTypeSel = Tkinter.IntVar()
        self.showATypeSel = Tkinter.IntVar()


    def __call__(self, dnodes, anodes, donorList=allDonors,
            acceptorList=allAcceptors, **kw):
        """None <- showHBDA(dnodes, anodes, donorList, acceptorList, **kw) """
        
        dnodes = self.vf.expandNodes(dnodes)
        donats = dnodes.findType(Atom)
        anodes = self.vf.expandNodes(anodes)
        acats = anodes.findType(Atom)
        if not (len(donats) or len(acats)): return 'ERROR'
        apply(self.doitWrapper, (donats, acats, donorList, acceptorList), kw)


    def doit(self, donats, acats,  donorList, acceptorList):
        #showHBDA
        if len(donats):
            dAts2, dAts3 = self.vf.getHBDonors(donats, 
                    donorList=donorList, topCommand=0)
            msg = ''
            if dAts2:
                newVerts = []
                for at in dAts2:
                    newVerts.append(getTransformedCoords(at).tolist())
                self.hbdonor2.Set(vertices=newVerts, visible=1,
                                  tagModified=False)
                #self.hbdonor2.Set(vertices = dAts2.coords, visible=1,
                #                  tagModified=False)
            else:
                msg = msg + 'no sp2 donors found '
                dAts2 = AtomSet([])
                self.hbdonor2.Set(vertices=[], tagModified=False)
            if dAts3:
                newVerts = []
                for at in dAts3:
                    newVerts.append(getTransformedCoords(at).tolist())
                self.hbdonor3.Set(vertices=newVerts, visible=1,
                                  tagModified=False)
                #self.hbdonor3.Set(vertices = dAts3.coords, visible=1, tagModified=False
            else:
                if len(msg): msg = msg + ';'
                msg = msg + 'no sp3 donors found'
                dAts3 = AtomSet([])
                self.hbdonor3.Set(vertices=[], tagModified=False)
            if len(msg):
                self.warningMsg(msg)
        else:
            dAts2 = AtomSet([])
            dAts3 = AtomSet([])
            self.hbdonor2.Set(vertices=[], tagModified=False)
            self.hbdonor3.Set(vertices=[], tagModified=False)
        #NOW THE ACCEPTORS:
        if len(acats):
            acAts2, acAts3 = self.vf.getHBAcceptors(acats,
                    acceptorList=acceptorList, topCommand=0)
            msg = ''
            if acAts2:
                newVerts = []
                for at in acAts2:
                    newVerts.append(getTransformedCoords(at).tolist())
                self.hbacceptor2.Set(vertices = newVerts, visible=1,
                                     tagModified=False)
                #self.hbacceptor2.Set(vertices = acAts2.coords, visible=1, tagModified=False)
            else:
                msg = msg + 'no sp2 acceptors found '
                acAts2 = AtomSet([])
                self.hbacceptor2.Set(vertices=[], tagModified=False)
            if acAts3:
                newVerts = []
                for at in acAts3:
                    newVerts.append(getTransformedCoords(at).tolist())
                self.hbacceptor3.Set(vertices = newVerts, visible=1,
                                     tagModified=False)
                #self.hbacceptor3.Set(vertices = acAts3.coords, visible=1, tagModified=False)
            else:
                if len(msg): msg = msg + ';'
                msg = msg + 'no sp3 acceptors found'
                acAts3 = AtomSet([])
                self.hbacceptor3.Set(vertices=[], tagModified=False)
            if len(msg): self.warningMsg(msg)
        else:
            acAts2 = AtomSet([])
            acAts3 = AtomSet([])
            self.hbacceptor2.Set(vertices=[], tagModified=False)
            self.hbacceptor3.Set(vertices=[], tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        #PUT UP A FORM TO TOGGLE Visibility etc (?)
        # show/hide sp2/sp3
        # select, save
        if not len(dAts2) and not len(dAts3) and not len(acAts2)\
            and not len(acAts3):
            self.warningMsg('no donors or acceptors found')
            return
        ifd2 = self.ifd2 = InputFormDescr(title = "Show/Hide Donors and Acceptors")
        if len(dAts2):
            ifd2.append({'name':'donor2',
                'widgetType':Tkinter.Checkbutton,
                'wcfg':{'text':'sp2 donors ',
                    'variable': self.sp2D,
                    'command': CallBackFunction(self.showGeom, 
                                self.hbdonor2, self.sp2D),
                    },
                'gridcfg':{'sticky':'w'}})
        if len(dAts3):
            ifd2.append({'name':'donor3',
                'widgetType':Tkinter.Checkbutton,
                'wcfg':{'text':'sp3 donors ',
                    'variable': self.sp3D,
                    'command': CallBackFunction(self.showGeom, 
                                self.hbdonor3, self.sp3D),
                    },
                'gridcfg':{'sticky':'w'}})
               # 'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        if len(acAts2):
            ifd2.append({'name':'accept2',
                'widgetType':Tkinter.Checkbutton,
                'wcfg':{'text':'sp2 acceptors ',
                    'variable': self.sp2A,
                    'command': CallBackFunction(self.showGeom, 
                                self.hbacceptor2, self.sp2A),
                    },
                'gridcfg':{'sticky':'w'}})
        if len(acAts3):
            ifd2.append({'name':'accept3',
                'widgetType':Tkinter.Checkbutton,
                'wcfg':{'text':'sp3 acceptors ',
                    'variable': self.sp3A,
                    'command': CallBackFunction(self.showGeom, 
                                self.hbacceptor3, self.sp3A),
                    },
                'gridcfg':{'sticky':'w'}})
                #'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd2.append({'widgetType': Tkinter.Button,
            'text':'Select',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':'ew' },
            'command':CallBackFunction(self.select_cb,dAts2,dAts3,acAts2,acAts3)})
        ifd2.append({'widgetType': Tkinter.Button,
            'text':'Cancel',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':'ew', 'column':1,'row':-1},
            'command':self.cancel_cb})
        self.form2 = self.vf.getUserInput(self.ifd2, modal=0, blocking=0)
        self.form2.root.protocol('WM_DELETE_WINDOW',self.cancel_cb)



    def showGeom(self, geom, var, event=None):
        geom.Set(visible=var.get(), tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def select_cb(self, dAts2, dAts3, acAts2, acAts3):
        #NB need to set level to Atom
        self.vf.setIcomLevel(Atom, topCommand=0)
        vars = [self.sp2D, self.sp2D, self.sp2A, self.sp3A]
        atL = [dAts2, dAts3, acAts2, acAts3]
        for i in range(4):
            if vars[i].get() and len(atL[i]):
                self.vf.select(atL[i])
                
    
    def cancel_cb(self, event=None):
        for g in [self.hbdonor2, self.hbdonor3, self.hbacceptor2,\
                self.hbacceptor3]:
            g.Set(vertices=[], tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        self.form2.destroy()


    def buildForm(self):
        ifd = self.ifd = InputFormDescr(title = "Select nodes + types of donor-acceptors:")
        ifd.append({'name':'DkeyLab',
                    'widgetType':Tkinter.Label,
                    'text':'For Hydrogen Bonds Donors:',
                    'gridcfg':{'sticky':'w','columnspan':3}})
        ifd.append({'name':'selDRB0',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Use all atoms',
                'variable': self.hideDSel,
                'value':1,
                'command':self.hideDSelector
                },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'selDRB1',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Set atoms to use',
                'variable': self.hideDSel,
                'value':0,
                'command':self.hideDSelector
                },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name': 'keyDSelector',
            'wtype':StringSelectorGUI,
            'widgetType':StringSelectorGUI,
            'wcfg':{ 'molSet': self.vf.Mols,
                    'vf': self.vf,
                    'all':1,
                    'crColor':(0.,1.,.2),
            },
            'gridcfg':{'sticky':'we', 'columnspan':2 }})
        ifd.append({'name':'limitDTypes',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Limit donor types ',
                'variable': self.showDTypeSel,
                'command':self.hideDTypes
                },
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'N3+',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'N3+',
                'variable': self.N3, },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'O3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O3',
                'variable': self.O3, },
            'gridcfg':{'sticky':'w','row':-1, 'column':1}})
        ifd.append({'name':'S3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'S3',
                'variable': self.S3, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})
        ifd.append({'name':'Nam',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Nam',
                'variable': self.Nam, },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'Ng+',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Ng+',
                'variable': self.Ng, },
            'gridcfg':{'sticky':'w','row':-1, 'column':1}})
        ifd.append({'name':'Npl',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Npl',
                'variable': self.Npl, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})
        #now the acceptors
        ifd.append({'name':'AkeyLab',
                    'widgetType':Tkinter.Label,
                    'text':'For Hydrogen Bonds Acceptors:',
                    'gridcfg':{'sticky':'w','columnspan':3}})
        ifd.append({'name':'selARB0',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Use all atoms',
                'variable': self.hideASel,
                'value':1,
                'command':self.hideASelector
                },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'selARB1',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Set atoms to use',
                'variable': self.hideASel,
                'value':0,
                'command':self.hideASelector
                },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name': 'keyASelector',
            'wtype':StringSelectorGUI,
            'widgetType':StringSelectorGUI,
            'wcfg':{ 'molSet': self.vf.Mols,
                    'vf': self.vf,
                    'all':1,
                    'crColor':(0.,1.,.2),
            },
            'gridcfg':{'sticky':'we', 'columnspan':2 }})
        ifd.append({'name':'limitTypes',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Limit acceptor types ',
                'variable': self.showATypeSel,
                'command':self.hideATypes
                },
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'aO3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O3',
                'variable': self.aO3, },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'aS3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'S3',
                'variable': self.aS3, },
            'gridcfg':{'sticky':'w','row':-1, 'column':1}})
        ifd.append({'name':'aO2',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O2',
                'variable': self.aO2, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})
        ifd.append({'name':'aO-',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O-',
                'variable': self.aO,},
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'aNpl',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Npl',
                'variable': self.aNpl, },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name':'aNam',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Nam',
                'variable': self.aNam, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})
        ifd.append({'name':'useSelCB',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'select from current selection',
                'variable': self.useSelection, },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Ok',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':'ew', 'columnspan':2 },
            'command':self.Accept_cb})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Cancel',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':'ew', 'column':2,'row':-1},
            'command':self.Close_cb})
        self.form = self.vf.getUserInput(self.ifd, modal=0, blocking=0)
        self.hideDSelector()
        self.hideDTypes()
        self.hideASelector()
        self.hideATypes()


    def guiCallback(self):
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        if not hasattr(self, 'ifd'):
            self.buildForm()


    def Close_cb(self, event=None):
        self.form.destroy()


    def Accept_cb(self, event=None):
        self.form.withdraw()
        dnodesToCheck = self.ifd.entryByName['keyDSelector']['widget'].get()
        if not len(dnodesToCheck):
            self.warningMsg('no donor atoms specified')
            dats = AtomSet([])
        else:
            dats = dnodesToCheck.findType(Atom)
            if self.useSelection.get():
                curSel = self.vf.getSelection()
                if len(curSel):
                    curAts = curSel.findType(Atom)
                    if curAts != self.vf.allAtoms:
                        dats = curAts.inter(dats)
                        if dats is None:
                            msg = 'no specified donor atoms in current selection'
                            self.warningMsg(msg)
                            dats = AtomSet([])
                else:
                    msg = 'no current selection'
                    self.warningMsg(msg)
                    dats = AtomSet([])
        donorList = []
        for n in allDonors:
            v = self.ifd.entryByName[n]['wcfg']['variable']
            if v.get():
                donorList.append(n)
        if not len(donorList):
            self.warningMsg('no donor types selected')
        ###FIX THIS!!!
        anodesToCheck = self.ifd.entryByName['keyASelector']['widget'].get()
        if not len(anodesToCheck):
            self.warningMsg('no acceptor atoms specified')
            acats = AtomSet([])
            #return
        else:
            acats = anodesToCheck.findType(Atom)
            if self.useSelection.get():
                curSel = self.vf.getSelection()
                if len(curSel):
                    curAts = curSel.findType(Atom)
                    if curAts != self.vf.allAtoms:
                        acats = curAts.inter(acats)
                        if not acats:  #NB inter returns empty set!
                            msg = 'no specified acceptor atoms in current selection'
                            self.warningMsg(msg)
                            acats = AtomSet([])
                else:
                    msg = 'no current selection'
                    self.warningMsg(msg)
                    acats = AtomSet([])
        acceptorList = []
        for n in allAcceptors: 
            name = 'a' + n 
            v = self.ifd.entryByName[name]['wcfg']['variable']
            if v.get():
                acceptorList.append(n)
        if not len(acceptorList):
            self.warningMsg('no acceptor types selected')
        if not (len(donorList) or len(acceptorList)):
            return
        self.Close_cb()
        return self.doitWrapper(dats, acats, donorList, acceptorList, topCommand=0)


    def hideDSelector(self, event=None):
        e = self.ifd.entryByName['keyDSelector']
        if self.hideDSel.get():
            e['widget'].grid_forget()
        else:
            e['widget'].grid(e['gridcfg'])
        self.form.autoSize() 


    def hideDTypes(self, event=None):
        for n in allDonors:
            e = self.ifd.entryByName[n]
            if not self.showDTypeSel.get():
                e['wcfg']['variable'].set(1)
                e['widget'].grid_forget()
            else:
                e['widget'].grid(e['gridcfg'])
        self.form.autoSize() 


    def hideASelector(self, event=None):
        e = self.ifd.entryByName['keyASelector']
        if self.hideASel.get():
            e['widget'].grid_forget()
        else:
            e['widget'].grid(e['gridcfg'])
        self.form.autoSize() 


    def hideATypes(self, event=None):
        for n in a_allAcceptors: 
            e = self.ifd.entryByName[n]
            if not self.showATypeSel.get():
                e['wcfg']['variable'].set(1)
                e['widget'].grid_forget()
            else:
                e['widget'].grid(e['gridcfg'])
        self.form.autoSize() 



ShowHBDonorsAcceptorsGuiDescr = {'widgetType':'Menu',
                                      'menuBarName':'menuRoot',
                                      'menuButtonName':'Hydrogen Bonds',
                                      'menuEntryLabel':'Show Donors + Acceptors',
                                      'menuCascadeName': 'Build'}


ShowHBDonorsAcceptorsGUI = CommandGUI()
ShowHBDonorsAcceptorsGUI.addMenuCommand('menuRoot','Hydrogen Bonds','Show Donors + Acceptors', cascadeName = 'Build')




class ShowHydrogenBonds(MVCommand):
    """This class allows user to visualize pre-existing HydrogenBonds between 
atoms in viewer
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : ShowHydrogenBonds
   \nCommand : showHBonds
   \nSynopsis:\n
    None <- showHBonds(nodes, **kw)
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly


    def onAddCmdToViewer(self):
        from DejaVu.IndexedPolylines import IndexedPolylines
        #from DejaVu.Labels import Labels
        miscGeom = self.vf.GUI.miscGeom
        hbond_geoms = check_hbond_geoms(self.vf.GUI)
        self.masterGeom = Geom('HbondsAsLinesGeoms',shape=(0,0), 
                                pickable=0, protected=True)
        self.masterGeom.isScalable = 0
        self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=hbond_geoms)
        self.lines = IndexedPolylines('showHbondLines', 
                    materials = ((0,1,0),), lineWidth=4, 
                    stippleLines=1, inheritMaterial=0, protected=True)
        self.labels = GlfLabels(name='showHbondLabs', shape=(0,3),
                            materials = ((0,1,1),), inheritMaterial=0,
                            billboard=True, fontStyle='solid',
                            fontScales=(.3,.3,.3,)) 
        self.labels.font = 'arial1.glf'
        self.thetaLabs = GlfLabels(name='hbondThetaLabs', shape=(0,3),
                            materials = ((1,1,0),), inheritMaterial=0,
                            billboard=True, fontStyle='solid',
                            fontScales=(.3,.3,.3,)) 
        self.thetaLabs.font = 'arial1.glf'
        self.phiLabs = GlfLabels(name='hbondPhiLabs', shape=(0,3),
                            materials = ((1,.2,0),), inheritMaterial=0,
                            billboard=True, fontStyle='solid',
                            fontScales=(.3,.3,.3,)) 
        self.phiLabs.font = 'arial1.glf'
        self.engLabs = GlfLabels(name='hbondELabs', shape=(0,3),
                            materials = ((1,1,1),), inheritMaterial=0,
                            billboard=True, fontStyle='solid',
                            fontScales=(.3,.3,.3,)) 
        self.engLabs.font = 'arial1.glf'

        geoms = [self.lines, self.labels, self.thetaLabs,
                        self.phiLabs, self.engLabs]
        for item in geoms:
            self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom)

        self.vf.loadModule('labelCommands', 'Pmv', log=0)
        self.showAll = Tkinter.IntVar()
        self.showAll.set(1)
        self.showDistLabels = Tkinter.IntVar()
        self.showDistLabels.set(1)
        self.dispTheta = Tkinter.IntVar()
        self.dispTheta.set(0)
        self.dispPhi = Tkinter.IntVar()
        self.dispPhi.set(0)
        self.dispEnergy = Tkinter.IntVar()
        self.dispEnergy.set(0)
        self.dverts = []
        self.dlabs = []
        self.angVerts = []
        self.phi_labs = []
        self.theta_labs = []
        self.e_labs = []
        self.hasDist = 0
        self.hasAngles = 0
        self.hasEnergies = 0
        self.height = None
        self.width = None
        self.winfo_x = None
        self.winfo_y = None


    def __call__(self, nodes, **kw):
        """None <- showHBonds(nodes, **kw) """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes =self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        ats = nodes.findType(Atom)
        apply(self.doitWrapper, (ats,), kw)


    def reset(self):
        self.hasAngles = 0
        self.hasEnergies = 0
        self.hasDist = 0
        self.lines.Set(vertices=[], tagModified=False)
        self.labels.Set(vertices=[], tagModified=False)
        self.engLabs.Set(vertices=[], tagModified=False)
        self.dverts = []
        self.dlabs = []
        self.angVerts = []
        self.phi_labs = []
        self.theta_labs = []
        self.e_labs = []


    def doit(self, ats):
        #asLines: ShowHBonds
        hbats = AtomSet(ats.get(lambda x: hasattr(x, 'hbonds')))
        self.reset()
        if not hbats:
            self.warningMsg('1:no hydrogen bonds found')
            return 'ERROR'
        hats = AtomSet(hbats.get(lambda x: x.element=='H'))
        atNames = []
        if hats:
            for h in hats:
                atNames.append((h.full_name(), None))
        else:
            hats = AtomSet([])
            bnds = []
            for at in hbats:
                bnds.extend(at.hbonds)
            bndD = {}
            for b in bnds:
                bndD[b] = 0
            hbnds2 = bndD.keys()
            hbnds2.sort()
            for b in hbnds2:
                atNames.append((b.donAt.full_name(), None))
                hats.append(b.donAt)
        self.showAllHBonds(hats)
        if not hasattr(self, 'ifd'):
            ifd = self.ifd=InputFormDescr(title = 'Hydrogen Bonds:')
            ifd.append({'name': 'hbondLabel',
                'widgetType': Tkinter.Label,
                'wcfg':{'text': str(len(atNames)) + ' Atoms in hbonds:'},
                'gridcfg':{'sticky': 'we', 'columnspan':2}})
            ifd.append({'name': 'atsLC',
                'widgetType':ListChooser,
                'wcfg':{
                    'entries': atNames,
                    'mode': 'single',
                    'title': '',
                    'command': CallBackFunction(self.showHBondLC, atNames),
                    'lbwcfg':{'height':5, 
                        'selectforeground': 'red',
                        'exportselection': 0,
                        'width': 30},
                },
                'gridcfg':{'sticky':'we', 'weight':2,'row':2,
                        'column':0, 'columnspan':2}})
            ifd.append({'name':'showAllBut',
                'widgetType':Tkinter.Checkbutton,
                'wcfg': { 'text':'Show All ',
                    'variable': self.showAll,
                    'command': CallBackFunction(self.showAllHBonds, \
                           hats)},
                'gridcfg':{'sticky':'we','weight':2}})
            ifd.append({'name':'showDistBut',
                'widgetType':Tkinter.Checkbutton,
                'wcfg': { 'text':'Show Distances ',
                    'variable': self.showDistLabels,
                    'command': self.showDistances},
                'gridcfg':{'sticky':'we', 'row':-1, 'column':1,'weight':2}})
            ifd.append({'name':'showThetaBut',
                'widgetType':Tkinter.Checkbutton,
                'wcfg': { 'text':'Show theta\n(don-h->acc)',
                    'variable': self.dispTheta,
                    'command': CallBackFunction(self.showAngles, hats)},
                'gridcfg':{'sticky':'we','weight':2}})
            ifd.append({'name':'showPhiBut',
                'widgetType':Tkinter.Checkbutton,
                'wcfg': { 'text':'Show phi\n(h->acc-accN)',
                    'variable': self.dispPhi,
                    'command': CallBackFunction(self.showAngles, hats)},
                'gridcfg':{'sticky':'we', 'row':-1, 'column':1,'weight':2}})
            ifd.append({'name':'showEnBut',
                'widgetType':Tkinter.Checkbutton,
                'wcfg': { 'text':'Show Energy',
                    'variable': self.dispEnergy,
                    'command': CallBackFunction(self.showEnergies, hats)},
                'gridcfg':{'sticky':'we','weight':2}})
            ifd.append({'name':'selAllBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Select All ',
                    'command': CallBackFunction(self.selAllHBonds, hats)},
                'gridcfg':{'sticky':'we','weight':2}})
            ifd.append({'name':'closeBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Dismiss',
                    'command': self.dismiss_cb},
                'gridcfg':{'sticky':'we', 'row':-1, 'column':1,'weight':2}})
            self.form  = self.vf.getUserInput(ifd, modal=0, blocking=0,
                     width=350, height=350)
            self.form.root.protocol('WM_DELETE_WINDOW',self.dismiss_cb)
            self.toplevel = self.form.f.master.master
            self.toplevel.positionfrom(who='user')
            if self.winfo_x is not None and self.winfo_y is not None:
                geom = self.width +'x'+ self.height +'+'
                geom += self.winfo_x + '+' + self.winfo_y
                self.toplevel.geometry(newGeometry=geom)

        else:
            self.ifd.entryByName['showAllBut']['widget'].config(command= \
                                     CallBackFunction(self.showAllHBonds, hats))
            self.ifd.entryByName['showThetaBut']['widget'].config(command= \
                                     CallBackFunction(self.showAngles, hats))
            self.ifd.entryByName['showPhiBut']['widget'].config(command= \
                                     CallBackFunction(self.showAngles, hats))
            self.ifd.entryByName['showEnBut']['widget'].config(command= \
                                     CallBackFunction(self.showEnergies, hats))
            self.ifd.entryByName['selAllBut']['widget'].config(command= \
                                     CallBackFunction(self.selAllHBonds, hats))
            if self.winfo_x is not None and self.winfo_y is not None:
                geom = self.width +'x'+ self.height +'+'
                geom += self.winfo_x + '+' + self.winfo_y
                self.toplevel.geometry(newGeometry=geom)


    def showHBondLC(self, hats, event=None):
        #this caused infinite loop:
        if not hasattr(self, 'ifd'):
            self.doit(hats)
        lb = self.ifd.entryByName['atsLC']['widget'].lb
        if lb.curselection() == (): return
        self.showAll.set(0)
        atName = lb.get(lb.curselection())
        ind = int(lb.curselection()[0])
        ats = self.vf.Mols.NodesFromName(atName)
        at = ats[0]
        #to deal with two atoms with the exact same name:
        if len(ats)>1:
            for a in ats:
                if hasattr(a, 'hbonds'):
                    at = a
                    break
        b = at.hbonds[0]
        faces = ((0,1),)
        if b.hAt is not None:
            lineVerts = (getTransformedCoords(b.hAt).tolist(), getTransformedCoords(b.accAt).tolist())
            #lineVerts = (b.hAt.coords, b.accAt.coords)
        else:
            lineVerts = (getTransformedCoords(b.donAt).tolist(), getTransformedCoords(b.accAt).tolist())
            #lineVerts = (b.donAt.coords, b.accAt.coords)
            faces = ((0,1),)
        if self.hasAngles:
            verts = [self.angVerts[ind]]
            labs = [self.theta_labs[ind]]
            self.thetaLabs.Set(vertices=verts, labels=labs, tagModified=False)
            labs = [self.phi_labs[ind]]
            self.phiLabs.Set(vertices=verts, labels=labs, tagModified=False)
        if self.hasEnergies:
            labs = [self.e_labs[ind]]
            verts = [self.dverts[ind]]
            self.engLabs.Set(vertices=verts,labels=labs,\
                    visible=self.dispEnergy.get(), tagModified=False)
        labelVerts = [self.dverts[ind]]
        labelStrs = [self.dlabs[ind]]
        self.labels.Set(vertices=labelVerts, labels=labelStrs,
                        tagModified=False)
        self.lines.Set(vertices=lineVerts, type=GL.GL_LINE_STRIP,
                           faces=faces, freshape=1, tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def dismiss_cb(self, event=None, **kw):
        #showHBonds
        self.lines.Set(vertices=[], tagModified=False)
        self.labels.Set(vertices=[], tagModified=False)
        self.thetaLabs.Set(vertices=[], tagModified=False)
        self.phiLabs.Set(vertices=[], tagModified=False)
        self.engLabs.Set(vertices=[], tagModified=False)
        try:
            self.width = str(self.form.f.master.master.winfo_width())
            self.height = str(self.form.f.master.master.winfo_height())
            self.winfo_x = str(self.form.f.master.master.winfo_x())
            self.winfo_y = str(self.form.f.master.master.winfo_y())
        except:
            pass
        self.vf.GUI.VIEWER.Redraw()
        if hasattr(self, 'ifd'):
            delattr(self, 'ifd')
        if hasattr(self, 'form'):
            self.form.destroy()
        if hasattr(self, 'palette'):
            self.palette.hide()


    def showDistances(self, event=None):
        self.labels.Set(visible=self.showDistLabels.get(), tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def showEnergies(self, hats):
        if not self.hasEnergies:
            self.buildEnergies(hats)
            self.hasEnergies = 1
        if not self.showAll.get():
            self.showHBondLC(hats)
        else:
            self.engLabs.Set(labels = self.e_labs, vertices = self.dverts,
                visible=self.dispEnergy.get(), tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        

    def buildEnergies(self, hats):
        #FIX THIS: for some reason, eList is disordered...???
        eList = self.vf.getHBondEnergies(hats, topCommand=0)
        for hat in hats:
            self.e_labs.append(str(hat.hbonds[0].energy))


    def showAngles(self, hats, event=None):
        if not self.hasAngles:
            self.buildAngles(hats)
            self.hasAngles = 1
        if not self.showAll.get():
            self.showHBondLC(hats)
        else:
            self.thetaLabs.Set(vertices=self.angVerts, labels=self.theta_labs,
                               tagModified=False)
            self.phiLabs.Set(vertices = self.angVerts, labels = self.phi_labs,
                             tagModified=False)
        self.thetaLabs.Set(visible=self.dispTheta.get(), tagModified=False)
        self.phiLabs.Set(visible=self.dispPhi.get(), tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def buildAngles(self, hats):
        #NB: hats are either donAts or hAts
        # theta is don-h->acc, phi is h->acc-accN
        # calc 1 angle for each closeAt
        # hat is h, 
        if self.hasAngles: return
        tlabs = []
        plabs = []
        verts = []
        for hat in hats:
            b = hat.hbonds[0]
            accAt = b.accAt
            donAt = b.donAt
            if b.hAt is not None:
                ang = getAngle(donAt, hat, accAt)
                ang = round(ang, 3)
                b.theta = ang
                b0 = accAt.bonds[0]
                accN = b0.atom1
                if id(accN)==id(accAt): accN = b0.atom2
                #accN = b.accAt.bonds[0].neighborAtom(b.accAt)
                ang = getAngle(hat, accAt, accN)
                ang = round(ang, 3)
                b.phi = ang
            p1=numpy.array(getTransformedCoords(hat))
            p2=numpy.array(getTransformedCoords(accAt))
            #p1=numpy.array(hat.coords)
            #p2=numpy.array(accAt.coords)
            newVert = tuple((p1+p2)/2.0 + .2)
            tlabs.append(str(b.theta))
            verts.append(newVert)
            plabs.append(str(b.phi))
            self.thetaLabs.Set(vertices=verts, labels=tlabs, tagModified=False)
            self.angVerts = verts
            self.theta_labs = tlabs
            self.phi_labs = plabs
            self.phiLabs.Set(vertices=verts, labels=plabs, tagModified=False)
               

    def setupDisplay(self):
        #draw lines between hAts OR donAts and accAts
        self.labels.Set(vertices=self.dverts, labels=self.dlabs,
                        tagModified=False)
        self.lines.Set(vertices=self.lineVerts, type=GL.GL_LINE_STRIP,
                       faces=self.faces, freshape=1, tagModified=False)
        if self.hasAngles:
            self.thetaLabs.Set(vertices=self.angVerts, labels=self.theta_labs,
                               tagModified=False)
            self.phiLabs.Set(vertices=self.angVerts, labels=self.theta_labs,
                             tagModified=False)

        if self.hasEnergies:
            self.engLabs.Set(vertices=self.dverts, labels=self.e_labs,
                             tagModified=False)

        self.vf.GUI.VIEWER.Redraw()


    def showAllHBonds(self, hats, event=None):
        if not self.showAll.get() and hasattr(self, 'ifd'):
            self.showHBondLC(hats)
        elif not self.hasDist:
            lineVerts = []
            faces = []
            labelVerts = []
            labelStrs = []
            dlabelStrs = []
            atCtr = 0
            for at in hats:
                for b in at.hbonds:
                    dCoords = getTransformedCoords(b.donAt)
                    aCoords = getTransformedCoords(b.accAt)
                    if b.hAt is not None:
                        hCoords = getTransformedCoords(b.hAt)
                        lineVerts.append(hCoords)
                    else:
                        lineVerts.append(dCoords)
                    lineVerts.append(aCoords)
                    #lineVerts.append(b.accAt.coords)
                    faces.append((atCtr, atCtr+1))
                    #c1 = numpy.array(at.coords, 'f')
                    c1 = getTransformedCoords(at)
                    c3 = aCoords
                    #c3 = numpy.array(b.accAt.coords, 'f')
                    labelVerts.append(tuple((c1 + c3)/2.0))
                    if b.hAt is not None:
                        if b.hlen is not None:
                            labelStrs.append(str(b.hlen))
                        else:
                            #d = dist(hCoords,aCoords)
                            #d=self.vf.measureDistance(b.hAt,b.accAt, topCommand=0)
                            b.hlen=round(dist(hCoords, aCoords),3)
                            labelStrs.append(str(b.hlen))
                    if b.dlen is not None:
                        dlabelStrs.append(str(b.dlen))
                    else:
                        #d = dist(dCoords, aCoords)
                        ##d = dist(b.donAt.coords, b.accAt.coords)
                        #d=self.vf.measureDistance(b.donAt,b.accAt, topCommand=0)
                        #b.dlen=round(d,3)
                        b.dlen=round(dist(dCoords, aCoords) ,3)
                        dlabelStrs.append(str(b.dlen))
                    atCtr = atCtr + 2
            self.lineVerts = lineVerts
            self.faces = faces
            self.dverts = labelVerts
            if len(labelStrs):
                self.dlabs = labelStrs
            else:
                self.dlabs = dlabelStrs
            self.hasDist = 1
            self.setupDisplay()
        else:
            self.setupDisplay()


    def selAllHBonds(self, hats, event=None):
        self.vf.select(hats)
                

    def guiCallback(self):
        #showHbonds
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        sel =  self.vf.getSelection()
        #put a selector here
        if len(sel):
            ats = sel.findType(Atom)
            apply(self.doitWrapper, (ats,), {})


ShowHydrogenBondsGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'As Lines',
                                          'menuCascadeName': 'Display'}


ShowHydrogenBondsGUI = CommandGUI()
ShowHydrogenBondsGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'As Lines', cascadeName='Display')



class BuildHydrogenBondsGUICommand(MVCommand):
    """This class provides Graphical User Interface to BuildHydrogenBonds which is invoked by it.
    \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : BuildHydrogenBondsGUICommand
   \nCommand : buildHydrogenBondsGC
   \nSynopsis:\n
        None<---buildHydrogenBondsGC(nodes1, nodes2, paramDict, reset=1)
    """
    

    def onAddCmdToViewer(self):
        self.showSel = Tkinter.IntVar()
        self.resetVar = Tkinter.IntVar()
        self.resetVar.set(1)
        self.showDef = Tkinter.IntVar()
        self.distOnly = Tkinter.IntVar()
        self.hideDTypeSel = Tkinter.IntVar()
        self.hideATypeSel = Tkinter.IntVar()
        self.showTypes = Tkinter.IntVar()
        varNames = ['hideDSel',  'N3', 'O3','S3','Nam', 'Ng', 'Npl',\
                'hideASel',  'aS3', 'aO3', 'aO2', 'aO', 'aNpl',
                'aNam']
        for n in varNames:
            exec('self.'+n+'=Tkinter.IntVar()')
            exec('self.'+n+'.set(1)')



    def doit(self, nodes1, nodes2, paramDict, reset=1):
        #buildHbondGC
        self.hasAngles = 0
        atDict = self.vf.buildHBonds(nodes1, nodes2, paramDict, reset)
        if not len(atDict.keys()):
            self.warningMsg('3:no hydrogen bonds found')
            return 'ERROR'
        else:
            msg = str(len(atDict.keys()))+' hydrogen bonds formed'
            self.warningMsg(msg)
            
        #ats = AtomSet(atDict.keys())
        #ats.parent.sort()
        #self.vf.showHBonds(ats)


    def guiCallback(self):
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        if not hasattr(self, 'ifd'):
            self.buildForm()
        else:
            self.form.deiconify()


    def buildForm(self):
        ifd = self.ifd = InputFormDescr(title = "Select nodes + change parameters(optional):")
        ifd.append({'name':'keyLab',
                    'widgetType':Tkinter.Label,
                    'text':'For Hydrogen Bond detection:',
                    'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'selRB0',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Use all atoms',
                'variable': self.showSel,
                'value':0,
                'command':self.hideSelector
                },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'selRB1',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Specify two sets:',
                'variable': self.showSel,
                'value':1,
                'command':self.hideSelector
                },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name': 'group1',
            'wtype':StringSelectorGUI,
            'widgetType':StringSelectorGUI,
            'wcfg':{ 'molSet': self.vf.Mols,
                    'vf': self.vf,
                    'all':1,
                    'crColor':(0.,1.,.2),
            },
            'gridcfg':{'sticky':'we', 'columnspan':2 }})
        ifd.append({'name': 'group2',
            'wtype':StringSelectorGUI,
            'widgetType':StringSelectorGUI,
            'wcfg':{ 'molSet': self.vf.Mols,
                    'vf': self.vf,
                    'all':1,
                    'crColor':(1.,2.,0.),
            },
            'gridcfg':{'sticky':'we', 'columnspan':2}})

        ifd.append({'name':'typeRB0',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Use all donor-acceptorTypes',
                'variable': self.showTypes,
                'value':0,
                'command':self.hideTypeCBs,
                },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'selRB1',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Limit types:',
                'variable': self.showTypes,
                'value':1,
                'command':self.hideTypeCBs
                },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name':'limitDTypes',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Limit donor types ',
                'variable': self.hideDTypeSel,
                'command':self.hideDTypes
                },
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'N3+',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'N3+',
                'variable': self.N3, },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'O3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O3',
                'variable': self.O3, },
            'gridcfg':{'sticky':'w','row':-1, 'column':1}})
        ifd.append({'name':'S3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'S3',
                'variable': self.S3, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})
        ifd.append({'name':'Nam',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Nam',
                'variable': self.Nam, },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'Ng+',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Ng+',
                'variable': self.Ng, },
            'gridcfg':{'sticky':'w','row':-1, 'column':1}})
        ifd.append({'name':'Npl',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Npl',
                'variable': self.Npl, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})

        #now the acceptors
        ifd.append({'name':'limitATypes',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Limit acceptor types ',
                'variable': self.hideATypeSel,
                'command':self.hideATypes
                },
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'aO3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O3',
                'variable': self.aO3, },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'aS3',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'S3',
                'variable': self.aS3, },
            'gridcfg':{'sticky':'w','row':-1, 'column':1}})
        ifd.append({'name':'aO2',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O2',
                'variable': self.aO2, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})
        ifd.append({'name':'aO-',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'O-',
                'variable': self.aO,},
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'aNpl',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Npl',
                'variable': self.aNpl, },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name':'aNam',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Nam',
                'variable': self.aNam, },
            'gridcfg':{'sticky':'w','row':-1, 'column':2}})

        ifd.append({'name':'distOnly',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Use distance as only criteria',
                'variable': self.distOnly,
                },
            'gridcfg':{'sticky':'w', 'columnspan':2}})

        ifd.append({'name':'defRB0',
            'widgetType':Tkinter.Checkbutton,
            'wcfg':{'text':'Adjust default hydrogen bond parameters',
                'variable': self.showDef,
                'command':self.hideDefaults
                },
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'distCutoff',
                    'widgetType':ExtendedSliderWidget,
                    'wcfg':{'label': 'H-Acc Distance Cutoff',
                            'minval':1.5,'maxval':3.0 ,
                            'init':2.25,
                            'labelsCursorFormat':'%1.2f',
                            'sliderType':'float',
                            'entrywcfg':{'width':4},
                            'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}})
        ifd.append({'name':'distCutoff2',
                    'widgetType':ExtendedSliderWidget,
                    'wcfg':{'label': 'Donor-Acc Distance Cutoff',
                            'minval':1.5,'maxval':5.0 ,
                            'init':3.00,
                            'labelsCursorFormat':'%1.2f',
                            'sliderType':'float',
                            'entrywcfg':{'width':4},
                            'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}})
        #min+max theta values for sp2 and for sp3 donors:
        ifd.append({ 'name':'d2lab',
            'widgetType':Tkinter.Label,
            'wcfg':{ 'text': 'sp2 donor-hydrogen-acceptor angle limits:',
                'font':(ensureFontCase('helvetica'),12,'bold')},
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'d2min',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'min', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':100, 'max':180, 'type':float, 'precision':3,
                'value':120,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we'}})
        #modified this 10/1 because of 1crn:ILE35NH to THR1:O hbond
        ifd.append({'name':'d2max',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'max', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':100, 'max':190, 'type':float, 'precision':3,
                'value':180,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we', 'row':-1, 'column':1}})
        ifd.append({ 'name':'d3lab',
            'widgetType':Tkinter.Label,
           'wcfg':{ 'text': 'sp3 donor-hydrogen-acceptor angle limits:',
                'font':(ensureFontCase('helvetica'),12,'bold')},
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'d3min',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'min', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':100, 'max':180, 'type':float, 'precision':3,
                'value':120,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we'}})
        ifd.append({'name':'d3max',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'max', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':100, 'max':180, 'type':float, 'precision':3,
                'value':180,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we', 'row':-1, 'column':1}})
        #min+max phi values for sp2 and for sp3 donors:
        ifd.append({ 'name':'a2lab',
            'widgetType':Tkinter.Label,
            'wcfg':{ 'text': 'sp2 donor-acceptor-acceptorN angle  limits:',
                'font':(ensureFontCase('helvetica'),12,'bold')},
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'a2min',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'min', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':90, 'max':180, 'type':float, 'precision':3,
                'value':110,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we'}})
        ifd.append({'name':'a2max',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'max', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':100, 'max':180, 'type':float, 'precision':3,
                'value':150,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we', 'row':-1, 'column':1}})
        ifd.append({ 'name':'a3lab',
            'widgetType':Tkinter.Label,
            'wcfg':{ 'text': 'sp3 donor-acceptor-acceptorN angle limits:',
                'font':(ensureFontCase('helvetica'),12,'bold')},
            'gridcfg':{'sticky':'w', 'columnspan':2}})
        ifd.append({'name':'a3min',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'min', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':90, 'max':180, 'type':float, 'precision':3,
                'value':100,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we'}})
        ifd.append({'name':'a3max',
            'wtype':ThumbWheel,
            'widgetType':ThumbWheel,
            'wcfg':{ 'labCfg':{'text':'max', 
                        'font':(ensureFontCase('helvetica'),12,'bold')},
                'showLabel':2, 'width':100,
                'min':100, 'max':180, 'type':float, 'precision':3,
                'value':150,
                'showLabel':1,
                'continuous':1, 'oneTurn':2, 'wheelPad':2, 'height':20},
            'gridcfg':{'sticky':'we', 'row':-1, 'column':1}})
        ifd.append({'name':'resetRB0',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Remove all previous hbonds',
                'variable': self.resetVar,
                'value':1,
                },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'resetRB1',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Add to previous hbonds',
                'variable': self.resetVar,
                'value':0,
                },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Ok',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':'we'},
            'command':self.Accept_cb})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Cancel',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':'we', 'column':1,'row':-1},
            'command':self.Close_cb})
        self.form = self.vf.getUserInput(self.ifd, modal=0, blocking=0)
        self.form.root.protocol('WM_DELETE_WINDOW',self.Close_cb)
        self.hideSelector()
        self.hideDefaults()
        self.hideDTypes()
        self.hideATypes()
        self.hideTypeCBs()
        self.form.autoSize()


    def cleanUpCrossSet(self):
        chL = []
        for item in self.vf.GUI.VIEWER.rootObject.children:
            if isinstance(item, CrossSet) and item.name[:8]=='strSelCr':
                chL.append(item)
        if len(chL):
            for item in chL: 
                item.Set(vertices=[], tagModified=False)
                self.vf.GUI.VIEWER.Redraw()
                self.vf.GUI.VIEWER.RemoveObject(item)


    def Close_cb(self, event=None):
        #self.cleanUpCrossSet()
        self.form.withdraw()
        #self.form.destroy()


    def hideTypeCBs(self, event=None):
        if not self.showTypes.get():
            for n in ['limitDTypes', 'limitATypes']:
                e = self.ifd.entryByName[n]
                e['widget'].grid_forget()
            self.hideDTypeSel.set(0)
            self.hideDTypes()
            self.hideATypeSel.set(0)
            self.hideATypes()
            #reset all variables to 1 here
            varNames = ['N3', 'O3','S3','Nam', 'Ng', 'Npl',\
                 'aS3', 'aO3', 'aO2', 'aO', 'aNpl', 'aNam']
            for n in varNames:
                exec('self.'+n+'.set(1)')
        else:
            for n in ['limitDTypes', 'limitATypes']:
                e = self.ifd.entryByName[n]
                e['widget'].grid(e['gridcfg'])
        self.form.autoSize()


    def hideDTypes(self, event=None):
        for n in allDonors:
            e = self.ifd.entryByName[n]
            if not self.hideDTypeSel.get():
                e['widget'].grid_forget()
            else:
                e['widget'].grid(e['gridcfg'])
        self.form.autoSize()


    def hideASelector(self, event=None):
        e = self.ifd.entryByName['keyASelector']
        if self.hideASel.get():
            e['widget'].grid_forget()
        else:
            e['widget'].grid(e['gridcfg'])
        self.form.autoSize()


    def hideATypes(self, event=None):
        for n in a_allAcceptors: 
            e = self.ifd.entryByName[n]
            if not self.hideATypeSel.get():
                e['widget'].grid_forget()
            else:
                e['widget'].grid(e['gridcfg'])
        self.form.autoSize()


    def Accept_cb(self, event=None):
        self.form.withdraw()
        if self.showSel.get():
            nodesToCheck1 = self.ifd.entryByName['group1']['widget'].get()
            nodesToCheck2 = self.ifd.entryByName['group2']['widget'].get()
        else:
            # user didn't choose to specify two sets:
            nodesToCheck1 = nodesToCheck2 = self.vf.getSelection()
            #nodesToCheck2 = self.vf.getSelection()
        if not len(nodesToCheck1):
            self.warningMsg('no atoms in first set')
            return
        if not len(nodesToCheck2):
            self.warningMsg('no atoms in second set')
            return
        distCutoff = self.ifd.entryByName['distCutoff']['widget'].get()
        distCutoff2 = self.ifd.entryByName['distCutoff2']['widget'].get()
        d2min = self.ifd.entryByName['d2min']['widget'].get()
        d2max = self.ifd.entryByName['d2max']['widget'].get()
        d3min = self.ifd.entryByName['d3min']['widget'].get()
        d3max = self.ifd.entryByName['d3max']['widget'].get()

        a2min = self.ifd.entryByName['a2min']['widget'].get()
        a2max = self.ifd.entryByName['a2max']['widget'].get()
        a3min = self.ifd.entryByName['a3min']['widget'].get()
        a3max = self.ifd.entryByName['a3max']['widget'].get()
        if nodesToCheck1[0].__class__!=Atom:
            ats1 = nodesToCheck1.findType(Atom)
            nodesToCheck1 = ats1
        if not self.showSel.get():
            nodesToCheck2 = nodesToCheck1
        elif nodesToCheck2[0].__class__!=Atom:
            ats2 = nodesToCheck2.findType(Atom)
            nodesToCheck2 = ats2
        donorList = []
        for w in allDonors:
            if self.ifd.entryByName[w]['wcfg']['variable'].get():
                donorList.append(w)
        acceptorList = []
        for w in a_allAcceptors:
            if self.ifd.entryByName[w]['wcfg']['variable'].get():
                acceptorList.append(w[1:])
        self.Close_cb()
        paramDict = {}
        paramDict['distCutoff'] = distCutoff
        paramDict['distCutoff2'] = distCutoff2
        paramDict['d2min'] = d2min
        paramDict['d2max'] = d2max
        paramDict['d3min'] = d3min
        paramDict['d3max'] = d3max
        paramDict['a2min'] = a2min
        paramDict['a2max'] = a2max
        paramDict['a3min'] = a3min
        paramDict['a3max'] = a3max
        paramDict['distOnly'] = self.distOnly.get()
        paramDict['donorTypes'] = donorList
        paramDict['acceptorTypes'] = acceptorList
        if  len(donorList)==0 and len(acceptorList)==0:
            self.warningMsg('no donors or acceptors possible')
            return 'ERROR'
        #print 'donorList=', donorList
        #print 'acceptorList=', acceptorList
        return self.doitWrapper(nodesToCheck1, nodesToCheck2, 
            paramDict, reset=self.resetVar.get(), topCommand=0)


    def hideSelector(self, event=None):
        e = self.ifd.entryByName['group1']
        e2 = self.ifd.entryByName['group2']
        if not self.showSel.get():
            e['widget'].grid_forget()
            e2['widget'].grid_forget()
        else:
            e['widget'].grid(e['gridcfg'])
            e2['widget'].grid(e2['gridcfg'])
        self.form.autoSize() 


    def hideDefaults(self, event=None):
        e = self.ifd.entryByName['distCutoff']
        e2 = self.ifd.entryByName['distCutoff2']
        if not self.showDef.get():
            e['widget'].frame.grid_forget()
            e2['widget'].frame.grid_forget()
        else:
            e['widget'].frame.grid(e['gridcfg'])
            e2['widget'].frame.grid(e2['gridcfg'])
        wids =  ['d2lab', 'd2min', 'd2max', 'd3lab', 'd3min', 'd3max',
            'a2lab', 'a2min', 'a2max', 'a3lab', 'a3min', 'a3max']
        for item in wids:
            e = self.ifd.entryByName[item]
            if not self.showDef.get():
                e['widget'].grid_forget()
            else:
                e = self.ifd.entryByName[item]
                e['widget'].grid(e['gridcfg'])
        self.form.autoSize() 



BuildHydrogenBondsGUICommandGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'Set Params + Build',
                                          'menuCascadeName':'Build'}


BuildHydrogenBondsGUICommandGUI = CommandGUI()
BuildHydrogenBondsGUICommandGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds', 'Set Parms + Build', cascadeName = 'Build')



class BuildHydrogenBonds(MVCommand):
    """This command finds hydrogen donor atoms within 2.4*percentCutoff angstrom distance of hydrogen acceptor atoms. It builds and returns a dictionary atDict whose keys are hydrogen atoms (or hydrogenbond donor atoms if there are no hydrogens) and whose values are potential h-bond acceptors and distances to these atoms, respectively
    \nPackage : Pmv
    \nModule  : hbondCommands
    \nClass   : BuildHydrogenBonds 
    \nCommand : buildHbonds
    \nSynopsis:\n
        atDict <- buildHbonds(group1, group2, paramDict, **kw)
    \nRequired Arguments:\n       
        group1 ---  atoms\n 
        group2 --- atoms\n 
        paramDict --- a dictionary with these keys and default values\n
        keywords --\n
            distCutoff: 2.25  hydrogen--acceptor distance\n
            distCutoff2: 3.00 donor... acceptor distance\n
            d2min: 120 <min theta for sp2 hybridized donors>\n
            d2max: 180 <max theta for sp2 hybridized donors>\n
            d3min: 120 <min theta for sp3 hybridized donors>\n
            d3max: 170 <max theta for sp3 hybridized donors>\n

            a2min: 120 <min phi for sp2 hybridized donors>\n
            a2max: 150 <max phi for sp2 hybridized donors>\n
            a3min: 100 <min phi for sp3 hybridized donors>\n
            a3max: 150 <max phi for sp3 hybridized donors>\n
            donorTypes = allDonors\n
            acceptorTypes = allAcceptors\n
            reset: remove all previous hbonds\n
    """


    def onAddCmdToViewer(self):
        self.distSelector = DistanceSelector(return_dist=0)
        if not self.vf.commands.has_key('typeAtoms'):
            self.vf.loadCommand('editCommands', 'typeAtoms', 'Pmv',
                                topCommand=0)


    def reset(self):
        ct = 0
        for at in self.vf.allAtoms:
            if hasattr(at, 'hbonds'):
                for item in at.hbonds:
                    del item
                    ct = ct +1
                delattr(at, 'hbonds')
        #print 'reset: deleted  hbonds from ' + str(ct)+ ' atoms '


    def doit(self, group1, group2, paramDict, reset):
        hb = HydrogenBondBuilder()
        atDict = hb.build(group1, group2,reset=reset, paramDict=paramDict)
        return atDict
#        #buildHbonds
#        # two steps:
#        # group1 donors v group2 acceptors and group1 acceptors vs group2 donors
#        if reset: 
#            #delattr(group1, 'hbonds')
#            #delattr(group2, 'hbonds')
#            self.reset()
#        atDict = {}

#        dict1 = self.buildD(group1, paramDict)

#        if group1==group2:
#            atD1 =self.process(dict1, dict1, paramDict)
#            #print 'len(atD1).keys()=', len(atD1.keys())
#        else:
#            dict2 = self.buildD(group2, paramDict)
#            atD1 = self.process(dict1, dict2, paramDict)
#            atD2 = self.process(dict2, dict1, paramDict)
#            #print 'len(atD1).keys()=', len(atD1.keys())
#            #print 'len(atD2).keys()=', len(atD2.keys())

#        #if called with 1 atom could get tuple of two empty dictionaries
#        if type(atD1)==type(atDict):
#            atDict.update(atD1)
#            if group1!=group2 and type(atD2)==type(atDict):
#                atDict.update(atD2)

#        return atDict


#    def buildD(self, ats, paramDict):
#        d = {}
#        donorTypes = paramDict['donorTypes']
#        donor2Ats, donor3Ats = self.vf.getHBDonors(ats,
#                donorTypes, topCommand=0)
#        d23 = donor2Ats+donor3Ats
#        #hAts = ats.get(lambda x, d23=d23: x.element=='H' \
#                    #and x.bonds[0].neighborAtom(x) in d23)
#        hAts = AtomSet(ats.get(lambda x, donorTypes=donorTypes: x.element=='H' \
#                    and (x.bonds[0].atom1.babel_type in donorTypes\
#                    or x.bonds[0].atom2.babel_type in donorTypes)))
#        d['hAts'] = hAts
#        d['donor2Ats'] = donor2Ats
#        d['donor3Ats'] = donor3Ats

#        acceptor2Ats, acceptor3Ats = self.vf.getHBAcceptors(ats, 
#                    paramDict['acceptorTypes'], topCommand=0)
#        d['acceptor2Ats'] = acceptor2Ats
#        d['acceptor3Ats'] = acceptor3Ats
#        if acceptor2Ats:
#            acceptorAts = acceptor2Ats
#            if acceptor3Ats:
#                acceptorAts = acceptorAts + acceptor3Ats
#        elif acceptor3Ats:
#            acceptorAts = acceptor3Ats
#        else:
#            #CHECK THIS: should it be None or AtomSet([])
#            acceptorAts = None
#        d['acceptorAts'] = acceptorAts
#        return d
#            

#    def getMat(self, ats):
#        tops = ats.top.uniq()
#        if len(tops)>1: 
#            self.warningMsg('transformation mat=None:>1 mol in atomset!')
#            return None
#        g = tops[0].geomContainer.geoms['master']
#        return g.GetMatrix(g)

#    def process(self, dict1, dict2, paramDict):
#        #hAts are keys, aceptorAts are checks
#        hAts = dict1['hAts']
#        tAts = hAts
#        dist = paramDict['distCutoff']
#        distOnly = paramDict['distOnly']

#        if not hAts:
#            #then use donors and a different distance
#            tAts = dict1['donor2Ats'] + dict1['donor3Ats']
#            dist = paramDict['distCutoff2']
#            
#        acceptorAts = dict2['acceptorAts']
#        if not acceptorAts or not tAts: #6/14/2004
#            return {}, {}

#        #call distanceSelector on two groups of atoms with dist
#        keyMat = self.getMat(tAts)
#        checkMat = self.getMat(acceptorAts)
#        atDict = self.distSelector.select(tAts, acceptorAts, dist,
#            keyMat=keyMat, checkMat=checkMat)
#        #atDict = self.distSelector.select(tAts, acceptorAts, dist)
#        #first remove bonded angles
#        atDict = self.removeNeighbors(atDict)

#        donor2Ats = dict1['donor2Ats']
#        donor3Ats = dict1['donor3Ats']
#        acceptor2Ats = dict2['acceptor2Ats']
#        acceptor3Ats = dict2['acceptor3Ats']

#        if distOnly: 
#            #need to build hbonds and return dictionary
#            self.makeBonds(atDict, donor2Ats, donor3Ats, \
#                    acceptor2Ats, acceptor3Ats, paramDict)
#            return atDict

#        badAtDict = self.filterBasedOnAngs(atDict, donor2Ats, donor3Ats, \
#                    acceptor2Ats, acceptor3Ats, paramDict)
#        atDict = self.removeBadAts(atDict, badAtDict)
#        if atDict is None:
#            atDict = {}
#        return atDict


#    def makeBonds(self, pD, d2Ats, d3Ats, a2Ats, a3ats, paramDict):
#        for k in pD.keys():
#            if k.element=='H':
#                if hasattr(k, 'hbonds') and len(k.hbonds):
#                    continue
#                d = k.bonds[0].atom1
#                if id(d)==id(k): d = k.bonds[0].atom2
#                #d = k.bonds[0].neighborAtom(k)
#                h = k
#            else: 
#                d = k
#                h = None
#            #pD[k] is a list of close-enough ats
#            for ac in pD[k]:
#                if ac==d: continue
#                dSp2 = d in d2Ats
#                aSp2 = ac in a2Ats
#                if dSp2:
#                    if aSp2: typ = 22
#                    else: typ = 23
#                elif aSp2: typ = 32
#                else: typ = 33
#                #THEY could be already bonded
#                alreadyBonded = 0
#                if hasattr(d, 'hbonds') and hasattr(ac,'hbonds'):
#                    for hb in d.hbonds:
#                        if hb.donAt==ac or hb.accAt==ac:
#                            alreadyBonded = 1
#                            
#                if not alreadyBonded:
#                    newHB = HydrogenBond(d, ac, h, typ=typ)
#                    if not hasattr(ac, 'hbonds'):
#                        ac.hbonds=[]
#                    if not hasattr(d, 'hbonds'):
#                        d.hbonds=[]
#                    ac.hbonds.append(newHB)
#                    d.hbonds.append(newHB)
#                    if h is not None:
#                        #hydrogens can have only 1 hbond
#                        h.hbonds = [newHB]


#    def filterBasedOnAngs(self, pD, d2Ats, d3Ats, a2Ats, a3ats, paramDict):
#        badAtDict = {}
#        d2max = paramDict['d2max']
#        d2min = paramDict['d2min']
#        d3max = paramDict['d3max']
#        d3min = paramDict['d3min']
#        #NEED these parameters
#        a2max = paramDict['a2max']
#        a2min = paramDict['a2min']
#        a3max = paramDict['a3max']
#        a3min = paramDict['a3min']
#        #NB now pD keys could be hydrogens OR donors
#        for k in pD.keys():
#            if k.element=='H':
#                d = k.bonds[0].atom1
#                if id(d)==id(k): d = k.bonds[0].atom2
#                #d = k.bonds[0].neighborAtom(k)
#                h = k
#            else: 
#                d = k
#                h = None
#            badAts = AtomSet([])
#            ct = 0
#            for ac in pD[k]:
#                if h is not None:
#                    ang = getAngle(ac, h, d)
#                else:
#                    acN = ac.bonds[0].atom1
#                    if id(acN) == id(ac): acN = ac.bonds[0].atom2
#                    #acN = ac.bonds[0].neighborAtom(ac)
#                    ang = getAngle(d, ac, acN)
#                #print 'ang=', ang
#                dSp2 = d in d2Ats
#                aSp2 = ac in a2Ats
#                #these limits could be adjustable
#                if h is not None:
#                    if dSp2:
#                        upperLim = d2max
#                        lowerLim = d2min
#                        #upperLim = 170
#                        #lowerLim = 130
#                    else:
#                        upperLim = d3max
#                        lowerLim = d3min
#                        #upperLim = 180
#                        #lowerLim = 120
#                else:
#                    #if there is no hydrogen use d-ac-acN angles
#                    if dSp2:
#                        upperLim = a2max
#                        lowerLim = a2min
#                        #upperLim = 150
#                        #lowerLim = 110
#                    else:
#                        upperLim = a3max
#                        lowerLim = a3min
#                        #upperLim = 150
#                        #lowerLim = 100
#                if ang>lowerLim and ang <upperLim:
#                    #AT THIS MOMENT BUILD HYDROGEN BOND:
#                    if dSp2:
#                        if aSp2: typ = 22
#                        else: typ = 23
#                    elif aSp2: typ = 32
#                    else: typ = 33
#                    #THEY could be already bonded
#                    alreadyBonded = 0
#                    if hasattr(d, 'hbonds') and hasattr(ac,'hbonds'):
#                        for hb in d.hbonds:
#                            if hb.donAt==ac or hb.accAt==ac:
#                                alreadyBonded = 1
#                    if not alreadyBonded:
#                        newHB = HydrogenBond(d, ac, h, theta=ang, typ=typ)
#                        if not hasattr(ac, 'hbonds'):
#                            ac.hbonds=[]
#                        if not hasattr(d, 'hbonds'):
#                            d.hbonds=[]
#                        ac.hbonds.append(newHB)
#                        d.hbonds.append(newHB)
#                        if h is not None:
#                            #hydrogens can have only 1 hbond
#                            h.hbonds = [newHB]
#                        #    newHB.hlen = dist
#                        #else:
#                        #    newHB.dlen = dist
#                else:
#                    badAts.append(ac)
#                ct = ct + 1
#            badAtDict[k] = badAts
#        return badAtDict


#    def removeBadAts(self, atDict, badAtDict):
#        #clean-up function called after filtering on angles
#        badKeys= badAtDict.keys()
#        for at in atDict.keys():
#            if at not in badKeys:
#                continue
#            if not len(badAtDict[at]):
#                continue
#            closeAts = atDict[at]
#            badAts = badAtDict[at]
#            goodAts = []
#            for i in range(len(closeAts)):
#                cAt = closeAts[i]
#                if cAt not in badAts:
#                    goodAts.append(cAt)
#            if len(goodAts):
#                atDict[at] = goodAts
#            else:
#                del atDict[at]
#        return atDict


#    def removeNeighbors(self, atDict):
#        #filter out at-itself and at-bondedat up to 1:4
#        #NB keys could be hydrogens OR donors
#        for at in atDict.keys():
#            closeAts = atDict[at]
#            bondedAts = AtomSet([])
#            for b in at.bonds:
#                ###at2 = b.neighborAtom(at)
#                at2 = b.atom1
#                if id(at2)==id(at): at2 = b.atom2
#                bondedAts.append(at2)
#                #9/13 remove this:
#                ##also remove 1-3
#                for b2 in at2.bonds:
#                    at3 = b2.atom1
#                    if id(at3)==id(at2): at3 = b.atom2
#                    #at3 = b2.neighborAtom(at2)
#                    if id(at3)!=id(at):
#                        bondedAts.append(at3)
#                    #for b3 in at3.bonds:
#                        #at4 = b2.neighborAtom(at3)
#                        #if at4!=at and at4!=at2:
#                            #bondedAts.append(at4)
#            bondedAts = bondedAts.uniq()
#            goodAts = []
#            for i in range(len(closeAts)):
#                cAt = closeAts[i]
#                if cAt not in bondedAts:
#                    goodAts.append(cAt)
#            if len(goodAts):
#                atDict[at] = goodAts
#            else:
#                del atDict[at]
#        return atDict
#                    

#    def getDonors(self, nodes, paramDict):
#        donorList = paramDict['donorTypes']
#        print 'donorList=', donorList
#        # currently this is a set of hydrogens
#        hats = AtomSet(nodes.get(lambda x: x.element=='H'))
#        #hats are optional: if none, process donors
#        # if there are hats: dAts are all atoms bonded to all hydrogens
#        if hats:
#            dAts = AtomSet([])
#            for at in hats:
#                for b in at.bonds:
#                    at2 = b.atom1
#                    if id(at2)==id(at): at2 = b.atom2
#                    dAts.append(at2)
#                    #dAts.append(b.neighborAtom(at))
#        else:
#            dAts = nodes
#        #get the sp2 hybridized possible donors which are all ns
#        sp2 = []
#        for t in ['Nam', 'Ng+', 'Npl']:
#            if t in donorList:
#                sp2.append(t)
#        #ntypes = ['Nam', 'Ng+', 'Npl']

#        sp2DAts = None
#        if len(sp2):
#            sp2DAts = AtomSet(dAts.get(lambda x, sp2=sp2: x.babel_type in sp2))

#        hsp2 = AtomSet([])
#        if sp2DAts:
#            if hats:
#                hsp2 = AtomSet(hats.get(lambda x, sp2DAts=sp2DAts:x.bonds[0].atom1 \
#                        in sp2DAts or x.bonds[0].atom2 in sp2DAts))
#        if sp2DAts:
#            #remove any sp2 N atoms which already have 3 bonds not to hydrogens
#            n2Dons = AtomSet(sp2DAts.get(lambda x: x.element=='N'))
#            if n2Dons:
#                n2Dons.bl=0
#                for at in n2Dons:
#                    for b in at.bonds:
#                        if type(b.bondOrder)==type(2):
#                            at.bl = at.bl + b.bondOrder
#                        else:
#                            at.bl = at.bl + 2
#                        #allow that there might already be a hydrogen
#                    nH = at.findHydrogens()
#                    at.bl = at.bl - len(nH)
#                badAts = AtomSet(n2Dons.get(lambda x: x.bl>2))
#                if badAts:
#                    sp2DAts = sp2DAts - badAts
#                delattr(n2Dons,'bl')
#        #get the sp3 hybridized possible donors
#        sp3 = []
#        for t in ['N3+', 'S3', 'O3']:
#            if t in donorList:
#                sp3.append(t)
#        n3DAts = None
#        if 'N3+' in sp3:
#            n3DAts = AtomSet(dAts.get(lambda x: x.babel_type=='N3+'))
#        o3DAts = None
#        if 'O3' in sp3:
#            o3DAts = AtomSet(dAts.get(lambda x: x.babel_type=='O3'))
#        if o3DAts:
#            #remove any O3 atoms which already have 2 bonds not to hydrogens
#            badO3s = AtomSet([])
#            for at in o3DAts:
#                if len(at.bonds)<2: continue
#                if len(at.findHydrogens()): continue
#                else:
#                    badO3s.append(at)
#            if len(badO3s):
#                o3DAts = o3DAts - badO3s
#        s3DAts = None
#        if 'S3' in sp3:
#            s3DAts = AtomSet(dAts.get(lambda x: x.babel_type=='S3'))
#        sp3DAts = AtomSet([])
#        for item in [n3DAts, o3DAts, s3DAts]:
#            if item:
#                sp3DAts = sp3DAts + item
#        hsp3 = AtomSet([])
#        if sp3DAts:
#            if hats:
#                hsp3 = AtomSet(hats.get(lambda x, sp3DAts=sp3DAts:x.bonds[0].atom1 \
#                    in sp3DAts or x.bonds[0].atom2 in sp3DAts))
#        hsp = hsp2 + hsp3
#        #print 'hsp=', hsp.name
#        #print 'sp2DAts=', sp2DAts.name
#        #print 'sp3DAts=', sp3DAts.name
#        return hsp, sp2DAts, sp3DAts


#    def getAcceptors(self, nodes, paramDict):
#        acceptorList = paramDict['acceptorTypes']
#        print 'acceptorList=', acceptorList

#        sp2 = []
#        for t in ['Npl', 'Nam']:
#            if t in acceptorList: sp2.append(t)
#        n2Accs = None
#        if 'Npl' in sp2:
#            n2Accs = AtomSet(nodes.get(lambda x: x.babel_type=='Npl'))
#        if 'Nam' in sp2:
#            n2Accs2 = AtomSet(nodes.get(lambda x: x.babel_type=='Nam'))
#            if n2Accs2:
#                if n2Accs:
#                    n2Accs = n2Accs+n2Accs2
#                else:
#                    n2Accs = n2Accs2
#        if n2Accs is None: 
#            n2Accs = AtomSet([])

#        o_sp2 = []
#        for t in ['O2', 'O-']:
#            if t in acceptorList: sp2.append(t)

#        o2Accs = None
#        if 'O2' in o_sp2:
#            o2Accs = AtomSet(nodes.get(lambda x: x.babel_type=='O2'))
#        if 'O-' in sp2:
#            o2Accs2 = AtomSet(nodes.get(lambda x: x.babel_type=='O-'))
#            if o2Accs2:
#                if o2Accs:
#                    o2Accs = o2Accs+o2Accs2
#                else:
#                    o2Accs = o2Accs2
#        if o2Accs is None: 
#            o2Accs = AtomSet([])

#        
#        o3Accs = None
#        if 'O3' in acceptorList:
#            o3Accs = AtomSet(nodes.get(lambda x: x.babel_type=='O3'))
#        if o3Accs is None: o3Accs = AtomSet([])

#        s3Accs = None
#        if 'S3' in acceptorList:
#            s3Accs = AtomSet(nodes.get(lambda x: x.babel_type=='S3'))
#        if s3Accs is None: s3Accs = AtomSet([])

#        ret2Ats = AtomSet([])
#        for item in [n2Accs, o2Accs]:
#            ret2Ats = ret2Ats + item

#        ret3Ats = AtomSet([])
#        for item in [s3Accs, o3Accs]:
#            ret3Ats = ret3Ats + item
#        if ret2Ats: print 'ret2Ats=', ret2Ats.name
#        else: print 'no ret2Ats'
#        if ret3Ats: print 'ret3Ats=', ret3Ats.name
#        else: print 'no ret3Ats'
#        return ret2Ats, ret3Ats



    def __call__(self, group1, group2=None, paramDict={}, reset=1,  **kw):
        """atDict <--- buildHbonds(group1, group2, paramDict, **kw)
           \ngroup1 ---  atoms 
           \ngroup2 --- atoms 
           \nparamDict --- a dictionary with these keys and default values
           \nkeywprds ---\n
           \ndistCutoff: 2.25  hydrogen--acceptor distance
           \ndistCutoff2: 3.00 donor... acceptor distance
           \nd2min: 120 <min theta for sp2 hybridized donors>
           \nd2max: 180 <max theta for sp2 hybridized donors>
           \nd3min: 120 <min theta for sp3 hybridized donors>
           \nd3max: 170 <max theta for sp3 hybridized donors>

           \na2min: 120 <min phi for sp2 hybridized donors>
           \na2max: 150 <max phi for sp2 hybridized donors>
           \na3min: 100 <min phi for sp3 hybridized donors>
           \na3max: 150 <max phi for sp3 hybridized donors>
           \ndonorTypes = allDonors
           \nacceptorTypes = allAcceptors
           \nreset: remove all previous hbonds
           """
        group1 = self.vf.expandNodes(group1)
        if group2 is None:
            group2 = group1
        else:
            group2 = self.vf.expandNodes(group2)
        if not (len(group1) and len(group2)):
            return 'ERROR'
        #for item in [group1, group2]:
        if group1.__class__!=Atom:
                group1 = group1.findType(Atom)
        try:
            group1.babel_type
        except:
            tops = group1.top.uniq()
            for t in tops:
                t.buildBondsByDistance()
                self.vf.typeAtoms(t.allAtoms, topCommand=0)

        if group2.__class__!=Atom:
                group2 = group2.findType(Atom)
        try:
            group2.babel_type
        except:
            tops = group2.top.uniq()
            for t in tops:
                self.vf.typeAtoms(t.allAtoms, topCommand=0)
        if not paramDict.has_key('distCutoff'):
            paramDict['distCutoff'] = 2.25
        if not paramDict.has_key('distCutoff2'):
            paramDict['distCutoff2'] = 3.00
        if not paramDict.has_key('d2min'):
            paramDict['d2min'] = 120.
        if not paramDict.has_key('d2max'):
            paramDict['d2max'] = 180.
        if not paramDict.has_key('d3min'):
            paramDict['d3min'] = 120.
        if not paramDict.has_key('d3max'):
            paramDict['d3max'] = 170.

        if not paramDict.has_key('a2min'):
            paramDict['a2min'] = 130.
        if not paramDict.has_key('a2max'):
            paramDict['a2max'] = 170.
        if not paramDict.has_key('a3min'):
            paramDict['a3min'] = 120.
        if not paramDict.has_key('a3max'):
            paramDict['a3max'] = 170.
        if not paramDict.has_key('distOnly'):
            paramDict['distOnly'] = 0
        if not paramDict.has_key('donorTypes'):
            paramDict['donorTypes'] = allDonors
        if not paramDict.has_key('acceptorTypes'):
            paramDict['acceptorTypes'] = allAcceptors
        return apply( self.doitWrapper, (group1, group2, paramDict, reset), kw)






class AddHBondHydrogensGUICommand(MVCommand):
    """This class provides Graphical User Interface to AddHBondHydrogens
which is invoked by it.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : AddHBondHydrogensGUICommand
   \nCommand : addHBondHsGC
   \nSynopsis:\n
        None<---addHBondHsGC(nodes)
    """
    

    def onAddCmdToViewer(self):
        self.showSel = Tkinter.IntVar()
        self.showSel.set(0)


    def doit(self, nodes):
        #addhydrogens to hbonds GC
        self.hasAngles = 0
        newHs = self.vf.addHBondHs(nodes)
        #print 'newHs=', newHs
        if not len(newHs):
            self.warningMsg('no hbonds found missing hydrogens')
            return 'ERROR'
        else:
            msg = str(len(newHs)) + ' hydrogens added to hbonds'
            self.warningMsg(msg)


    def guiCallback(self):
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        ifd = self.ifd = InputFormDescr(title = "Select nodes + change parameters(optional):")
        ifd.append({'name':'keyLab',
                    'widgetType':Tkinter.Label,
                    'text':'Add missing hydrogens to hbonds for:',
                    'gridcfg':{'sticky':Tkinter.W, 'columnspan':2}})
        ifd.append({'name':'selRB0',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'all atoms',
                'variable': self.showSel,
                'value':0,
                'command':self.hideSelector
                },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'selRB1',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'a subset of atoms ',
                'variable': self.showSel,
                'value':1,
                'command':self.hideSelector
                },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name': 'keySelector',
            'wtype':StringSelectorGUI,
            'widgetType':StringSelectorGUI,
            'wcfg':{ 'molSet': self.vf.Mols,
                    'vf': self.vf,
                    'all':1,
                    'crColor':(0.,1.,.2),
            },
            'gridcfg':{'sticky':'we', 'columnspan':2 }})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Ok',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':Tkinter.E+Tkinter.W},
            'command':self.Accept_cb})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Cancel',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':Tkinter.E+Tkinter.W, 'column':1,'row':-1},
            'command':self.Close_cb})
        self.form = self.vf.getUserInput(self.ifd, modal=0, blocking=0)
        self.form.root.protocol('WM_DELETE_WINDOW',self.Close_cb)
        self.hideSelector()


    def cleanUpCrossSet(self):
        chL = []
        for item in self.vf.GUI.VIEWER.rootObject.children:
            if isinstance(item, CrossSet) and item.name[:8]=='strSelCr':
                chL.append(item)
        if len(chL):
            for item in chL: 
                item.Set(vertices=[], tagModified=False)
                self.vf.GUI.VIEWER.Redraw()
                self.vf.GUI.VIEWER.RemoveObject(item)


    def Close_cb(self, event=None):
        self.cleanUpCrossSet()
        self.form.destroy()


    def Accept_cb(self, event=None):
        self.form.withdraw()
        nodesToCheck = self.ifd.entryByName['keySelector']['widget'].get()
        if not len(nodesToCheck):
            self.warningMsg('no atoms specified')
            return
        if nodesToCheck[0].__class__!='Atom':
            ats = nodesToCheck.findType(Atom)
            nodesToCheck = AtomSet(ats.get(lambda x: hasattr(x, 'hbonds')))
        self.Close_cb()
        if not nodesToCheck:
            self.warningMsg('no hbonds present in specified atoms')
            return
        else:
            return self.doitWrapper(ats, topCommand=0)


    def hideSelector(self, event=None):
        e = self.ifd.entryByName['keySelector']
        if not self.showSel.get():
            e['widget'].grid_forget()
        else:
            e['widget'].grid(e['gridcfg'])
        self.form.autoSize() 



AddHBondHydrogensGUICommandGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'Add Hydrogens to Hbonds',
                                          'menuCascadeName':'Build'}


AddHBondHydrogensGUICommandGUI = CommandGUI()
AddHBondHydrogensGUICommandGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds', 'Add Hydrogens to Hbonds', cascadeName='Build')



class AddHBondHydrogens(MVCommand):
    """This command adds hydrogen atoms to preexisting hydrogen bonds
    \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : AddHBondHydrogens
   \nCommand : addHBondHs
   \nSynopsis:\n
    newHs <- addHBondHs(nodes, **kw):
   \nRequired Arguments:\n        
            nodes --- atoms \n
            newHs are new atoms, added to hbond.donAt\n
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly

    def onAddCmdToViewer(self):
        if not self.vf.commands.has_key('typeAtoms'):
            self.vf.loadCommand('editCommands', 'typeAtoms', 'Pmv',
                                topCommand=0)


    def dist(self, c1, c2):
        c1 = numpy.array(c1)
        c2 = numpy.array(c2)
        d = c2 - c1
        return math.sqrt(numpy.sum(d*d))


    def chooseH(self, at, accAt):
        #do this for sp2 nitrogens with 1 bond
        c2 = at.bonds[0].neighborAtom(at)
        c3 = None
        c4 = None
        for b in c2.bonds:
            if b.neighborAtom(c2)==at:
                continue
            elif c3 is None:
                c3 = b.neighborAtom(c2)
            elif c4 is None:
                c4 = b.neighborAtom(c2)
        accCoords = getTransformedCoords(accAt)
        c2Coords = getTransformedCoords(c2)
        c3Coords = getTransformedCoords(c3)
        c4Coords = getTransformedCoords(c4)
        v = vec3(c3Coords, c2Coords, 1.020)
        v1 = vec3(c4Coords, c2Coords, 1.020)
        #v = vec3(c3.coords, c2.coords, 1.020)
        #v1 = vec3(c4.coords, c2.coords, 1.020)
        #check angles of c3-at-b.accAt
        c = getTransformedCoords(at)
        #c = at.coords
        coords1 = [c[0]+v[0], c[1]+v[1], c[2]+v[2]]
        coords2 = [c[0]+v1[0], c[1]+v1[1], c[2]+v1[2]]
        dist1 =  dist(c3Coords, accCoords)
        dist2 =  dist(c4Coords, accCoords)
        #dist1 =  dist(c3.coords, accAt.coords)
        #dist2 =  dist(c4.coords, accAt.coords)
        if dist1 > dist2:
            coords = coords1
        else:
            coords = coords2
        name = 'H' + at.name 
        childIndex = at.parent.children.index(at)+1
        atom = Atom(name, at.parent, top=at.top, childIndex=childIndex,
                        assignUniqIndex=0)
        atom._coords = [coords]
        if hasattr(at, 'segID'): atom.setID = at.segID
        atom.hetatm = at.hetatm
        atom.alternate = []
        atom.element = 'H'
        atom.occupancy = 1.0
        atom.conformation = 0
        atom.temperatureFactor = 0.0
        atom.babel_atomic_number = 1
        atom.babel_type = 'H'
        atom.babel_organic = 1
        bond = Bond(at, atom)
        atom.colors = {}
        for key, value in at.colors.items():
            atom.colors[key] = (0.0, 1.0, 1.0)
            atom.opacities[key] = 1.0
        atom.number = len(at.parent.children)
        return atom


    def doit(self, ats, renumber=1):
        #addhydrogens to hbonds 
        withHBondAts = AtomSet(ats.get(lambda x: hasattr(x, 'hbonds')))
        if not withHBondAts:
            return 'ERROR'
        newHs = AtomSet([])
        for at in withHBondAts:
            for b in at.hbonds:
                if at!=b.donAt: 
                    continue
                if b.hAt is None:
                    if len(at.bonds)==1 and \
                        at.babel_type in ['Ng+', 'Nam','Npl']:
                        h = self.chooseH(at, b.accAt)
                    else:
                        self.vf.add_h(AtomSet([b.donAt]), 1,'noBondOrder',
                                      1,topCommand=0)
                        h = at.bonds[-1].neighborAtom(at)
                    h.hbonds = [b]
                    newHs.append(h)
                    b.hAt = h
        if renumber:
            for mol in ats.top.uniq():
                mol.allAtoms.sort()
                fst = mol.allAtoms[0].number
                mol.allAtoms.number = range(fst, len(mol.allAtoms)+fst)

        # MS
        #assert len(newHs)==len(newHs.uniq())
        # tester -V Pmv.Tests.test_hbondCommands.hbond_hbondTests.test_addhbonds_hsGC__log_checks_that_it_runs
        # creates
        #hbond_barrel: :GLY22:HN1
        #hbond_barrel: :THR23:HG1
        #hbond_barrel: :THR23:HG1
        #hbond_barrel: :ALA28:HN
        # ...
        #  THR32:HG1 is double !
        #print 'OKKKKKKKKKKKKKKKKKKKKKK', newHs
        #for a in newHs:
        #    print a.full_name()
        set = newHs.uniq()
        # (MS) seems like we do not add atom here, They have been added by the
        # self.vf.add_h above
        #event = AddAtomsEvent(objects=set)
        #self.vf.dispatchEvent(event)

        return set


    def __call__(self, nodes, **kw):
        """newHs <--- addHBondHs(nodes, **kw)
          \nnodes --- atoms 
          \nnewHs are new atoms, added to hbond.donAt
           """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes =self.vf.expandNodes(nodes)
        if not len(nodes):
            return 'ERROR'
        if nodes.__class__!=Atom:
            ats = nodes.findType(Atom)
        else:
            ats = nodes
        try:
            ats.babel_type
        except AttributeError:
            tops = ats.top.uniq()
            for t in tops: 
                self.vf.typeAtoms(t.allAtoms, topCommand=0)
        return apply( self.doitWrapper, (ats,), kw)



class LimitHydrogenBonds(MVCommand):
    """This class allows user to detect pre-existing HydrogenBonds with energy values 
'lower' than a designated cutoff.  Optionally the user can remove these bonds.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : LimitHydrogenBonds
   \nCommand : limitHBonds
   \nSynopsis:\n
        None <- limitHBonds(nodes, energy, **kw)
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly

    def onRemoveObjectFromViewer(self, obj):
        if not len(self.hbonds):
            return
        removedAts = obj.findType(Atom)
        changed = 0
        bndsToRemove=[]
        for b in self.hbonds:
            ats = AtomSet([b.donAt,b.accAt])
            if b.hAt is not None:
                ats.append(b.hAt)
            if ats.inter(removedAts):
                bndsToRemove.append(b)
        for b in bndsToRemove:
            self.hbonds.remove(b)
            changed = 1
            ats = AtomSet([b.donAt,b.accAt])
            if b.hAt is not None:
                ats.append(b.hAt)
            for at in ats:
                at.hbonds.remove(b)
                if not len(at.hbonds):
                    delattr(at, 'hbonds')
        if changed:
            self.update()
                


    def onAddCmdToViewer(self):
        from DejaVu.IndexedPolylines import IndexedPolylines
        #from DejaVu.Labels import Labels
        miscGeom = self.vf.GUI.miscGeom
        hbond_geoms = check_hbond_geoms(self.vf.GUI)
        self.masterGeom = Geom('limitHbondGeoms',shape=(0,0), 
                                pickable=0, protected=True)
        self.masterGeom.isScalable = 0
        self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=hbond_geoms)
        self.lines = IndexedPolylines('limitHbondLines', 
                    materials = ((1,0,0),), lineWidth=8, 
                    stippleLines=1, inheritMaterial=0, protected=True)
        self.labels = GlfLabels(name='limitELabs', shape=(0,3),
                            materials=((1,1,0),), inheritMaterial=0,
                            billboard=True, fontStyle='solid',
                            fontScales=(.3,.3,.3,)) 
        self.labels.font = 'arial1.glf'
        geoms = [self.lines, self.labels]
        for item in geoms:
            self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom)

        self.vf.loadModule('labelCommands', 'Pmv', log=0)
        self.showAll = Tkinter.IntVar()
        self.showAll.set(1)
        self.disp_energy = Tkinter.IntVar()
        self.disp_energy.set(0)
        self.everts = []
        self.e_labs = []
        self.hideSel = Tkinter.IntVar()
        self.hideSel.set(1)
        self.bondsToDelete = []
        #SHOULD THIS BE A HYDROGENBONDSET?
        self.hbonds = []


    def __call__(self, nodes, **kw):
        """None <- limitHBonds(nodes, energy, **kw) """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes =self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        ats = nodes.findType(Atom)
        apply(self.doitWrapper, (ats,), kw)


    def doit(self, ats):
        #limit HBonds by energy
        self.hasEnergies = 0
        self.bondsToDelete = []
        self.hbonds = []
        self.everts = []
        self.e_labs = []
        self.resetDisplay()

        hbats = AtomSet(ats.get(lambda x: hasattr(x, 'hbonds')))
        if not hbats:
            self.warningMsg('no hydrogen bonds found')
            return 'ERROR'
        hats = AtomSet(hbats.get(lambda x: x.element=='H'))

        atNames = []
        if hats:
            for h in hats:
                atNames.append((h.full_name(), None))
                #self.hbonds.append(h.hbonds[0])
                for b in h.hbonds:
                    self.hbonds.append(b)
        else:
            hats = AtomSet([])
            bnds = []
            for at in hbats:
                bnds.extend(at.hbonds)
            bndD = {}
            for b in bnds:
                bndD[b] = 0
            hbnds2 = bndD.keys()
            hbnds2.sort()
            for b in hbnds2:
                atNames.append((b.donAt.full_name(), None))
                hats.append(b.donAt)
                self.hbonds.append(b)
        #self.vf.showHBonds.doit(hats)
        #self.vf.showHBonds.showAllHBonds(hats)
        #self.vf.showHBonds.buildEnergies(hats)
        self.vf.getHBondEnergies(hats, topCommand=0)
        #self.vf.showHBonds.showDistLabels.set(0)
        #self.vf.showHBonds.showDistances()
        #self.vf.showHBonds.dispEnergy.set(0)
        #self.vf.showHBonds.showEnergies(hats)
        self.origLabel = '0 hbonds to delete:'
        ifd = self.ifd=InputFormDescr(title = 'Hydrogen Bonds to Delete:')
        ifd.append({'name': 'hbondLabel',
            'widgetType': Tkinter.Label,
            'wcfg':{'text': self.origLabel},
            'gridcfg':{'sticky': 'wens', 'columnspan':2}})
        ifd.append({'name': 'atsLC',
            'widgetType':ListChooser,
            'wcfg':{
                'entries': atNames,
                'mode': 'multiple',
                'title': '',
                'command': CallBackFunction(self.showHBondLC, atNames),
                'lbwcfg':{'height':5, 
                    'selectforeground': 'red',
                    'exportselection': 0,
                    'width': 30},
            },
            'gridcfg':{'sticky':'wens', 'row':2,'column':0, 'columnspan':2}})
        ifd.append( {'name':'energy',
            'widgetType': ExtendedSliderWidget,
            'wcfg':{'label':'energy limit ',
                 'minval':-10, 'maxval':10,
                'immediate':1,
                'init':-2.0,
                'width':150,
                'command':self.update,
                'sliderType':'float',
                'entrypackcfg':{'side':'bottom'}},
            'gridcfg':{'sticky':'we', 'columnspan':2}})
        ifd.append({'name':'resetBut',
            'widgetType':Tkinter.Button,
            'wcfg': { 'text':'Reset',
                'command': self.reset},
            'gridcfg':{'sticky':'wens'}})
        ifd.append({'name':'showEnBut',
            'widgetType':Tkinter.Checkbutton,
            'wcfg': { 'text':'Show Energy',
                'variable': self.disp_energy,
                'command': CallBackFunction(self.showEnergies, hats)},
            'gridcfg':{'sticky':'wens', 'column':1, 'row':-1}})
        ifd.append({'name':'acceptBut',
            'widgetType':Tkinter.Button,
            'wcfg': { 'text':'Delete',
                'command': self.Delete_cb},
            'gridcfg':{'sticky':'wens'}})
        ifd.append({'name':'closeBut',
            'widgetType':Tkinter.Button,
            'wcfg': { 'text':'Dismiss',
                'command': self.dismiss_cb},
            'gridcfg':{'sticky':'wens', 'row':-1, 'column':1}})

        self.form2  = self.vf.getUserInput(ifd, modal=0, blocking=0,
                 width=350, height=330)
        self.form2.root.protocol('WM_DELETE_WINDOW',self.dismiss_cb)
        self.lb = self.ifd.entryByName['atsLC']['widget'].lb
        self.energyWid = self.ifd.entryByName['energy']['widget']
        self.hbondLabel = self.ifd.entryByName['hbondLabel']['widget']
        self.update()


    def Delete_cb(self, event=None):
        bl = []
        for b in self.bondsToDelete:
            for at in [b.donAt, b.hAt, b.accAt]:
                at.hbonds.remove(b)
                if not len(at.hbonds):
                    delattr(at, 'hbonds')
            bl.append(b)
        self.bondsToDelete = []
        for b in bl:
            del b
        #also reset self.hbondLabel, self.lb
        self.reset()
        #also need to reset showHBonds so it will recalc everything
        self.vf.showHBonds.reset()
        self.vf.showHBonds.lines.Set(vertices=[], tagModified=False)
        self.vf.showHBonds.labels.Set(vertices=[], tagModified=False)
        self.vf.showHBonds.engLabs.Set(vertices=[], tagModified=False)
        self.vf.showHBonds.dispEnergy.set(0)
        self.vf.GUI.VIEWER.Redraw()
        self.everts = []
        self.dismiss_cb()


    def resetDisplay(self, event=None):
        self.lines.Set(vertices=[], tagModified=False)
        self.labels.Set(vertices=[], labels=[], tagModified=False)


    def updateDisplay(self, event=None):
        #this draws red lines along bondToDelete
        lineVerts = []
        faces = []
        self.everts = []
        labs = []
        ctr = 0
        n = len(self.bondsToDelete)
        self.lb.delete(0, 'end')
        if n:
            for b in self.bondsToDelete:
                hAtCoords = getTransformedCoords(b.hAt)
                accAtCoords = getTransformedCoords(b.accAt)
                lineVerts.append(hAtCoords)
                lineVerts.append(accAtCoords)
                #lineVerts.append(b.hAt.coords)
                #lineVerts.append(b.accAt.coords)
                faces.append((ctr, ctr+1))
                pt = (numpy.array(hAtCoords)+numpy.array(accAtCoords))/2.0
                self.everts.append(pt.tolist())
                labs.append(str(b.energy))
                ctr = ctr + 2
                self.lb.insert('end', b.hAt.full_name())
            self.lines.Set(vertices = lineVerts, faces=faces,
                           tagModified=False)
            self.labels.Set(vertices = self.everts, labels = labs,
                            tagModified=False)
            msg = str(n) + ' hbonds to delete:'
            self.hbondLabel.config(text=msg)
        else:
            msg = '0 hbonds to delete:'
            self.hbondLabel.config(text=msg)
            self.resetDisplay()
        self.vf.GUI.VIEWER.Redraw()
                

    def reset(self):
        self.lb.select_clear(0, 'end')
        self.lb.delete(0, 'end')
        self.hbondLabel.config(text=self.origLabel)
        self.energyWid.set(-2.0)
        #also change bonds to delete
        self.bondsToDelete = []
        self.everts = []
        self.resetDisplay()
        self.update()


    def update(self, event=None):
        energy = self.energyWid.get()
        self.bondsToDelete = []
        for b in self.hbonds:
            if b.energy > energy and not b in self.bondsToDelete:
                self.bondsToDelete.append(b)
        self.updateDisplay()
            

    def showHBondLC(self, hats, event=None):
        if not hasattr(self, 'ifd'):
            self.doit(hats)
        if self.lb.curselection() == (): return
        self.showAll.set(0)
        #THIS COULD BE >1
        labs = []
        verts = []
        lineVerts = []
        faces = []
        ctr = 0
        for n in self.lb.curselection():
            #n in ('0','1','2')
            name = self.lb.get(n)
            at = self.vf.Mols.NodesFromName(name)[0]
            b = at.hbonds[0]
            #NB THESE MUST HAVE hydrogens
            hAtCoords = getTransformedCoords(b.hAt)
            accAtCoords = getTransformedCoords(b.accAt)
            lineVerts.append(hAtCoords) 
            lineVerts.append(accAtCoords) 
            #lineVerts.append(b.hAt.coords) 
            #lineVerts.append(b.accAt.coords) 
            faces = ((ctr,ctr+1),)
            if self.hasEnergies:
                labs.append(self.e_labs[ind])
                verts.append(self.everts[ind])
            ctr = ctr + 2
        self.labels.Set(vertices=verts,labels=labs,\
                        visible=self.disp_energy.get(), tagModified=False)
        self.lines.Set(vertices=lineVerts, type=GL.GL_LINE_STRIP,
                           faces=faces, freshape=1, tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def dismiss_cb(self, event=None, **kw):
        #limitHbonds
        self.lines.Set(vertices=[], tagModified=False)
        self.labels.Set(vertices=[], tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        delattr(self, 'ifd')
        self.form2.destroy()
        self.hbonds = []
        self.everts = []
        self.vf.showHBonds.dismiss_cb()


    def showEnergies(self, hats):
        if not self.showAll.get():
            self.showHBondLC(hats)
        else:
            self.labels.Set(labels = self.e_labs, vertices = self.everts,
                visible=self.disp_energy.get(), tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        

    def buildEnergies(self, hats):
        #FIX THIS: for some reason, eList is disordered...???
        eList = self.vf.getHBondEnergies(hats, topCommand=0)
        for hat in hats:
            self.e_labs.append(str(hat.hbonds[0].energy))


    def hideSelector(self, event=None):
        e = self.ifd.entryByName['keySelector']
        if self.hideSel.get():
            e['widget'].grid_forget()
        else:
            e['widget'].grid(e['gridcfg'])
        self.form.autoSize() 


    def cleanUpCrossSet(self):
        chL = []
        for item in self.vf.GUI.VIEWER.rootObject.children:
            if isinstance(item, CrossSet) and item.name[:8]=='strSelCr':
                chL.append(item)
        if len(chL):
            for item in chL: 
                item.Set(vertices=[], tagModified=False)
                self.vf.GUI.VIEWER.Redraw()
                self.vf.GUI.VIEWER.RemoveObject(item)


    def Close_cb(self, event=None):
        self.cleanUpCrossSet()
        self.form.destroy()


    def Ok_cb(self, event=None):
        if self.hideSel.get():
            nodes = self.vf.getSelection()
        else:
            nodes = self.ifd.entryByName['keySelector']['widget'].get()
        if not len(nodes):
            self.warningMsg('no nodes specified')
            return
        ats = nodes.findType(Atom)
        self.form.withdraw()
        apply(self.doitWrapper, (ats,), {})


    def guiCallback(self):
        #limitHbonds
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        sel =  self.vf.getSelection()
        ifd = self.ifd = InputFormDescr(title = "Select nodes + energy limit")
        ifd.append({'name':'DkeyLab',
                    'widgetType':Tkinter.Label,
                    'text':'For Hydrogen Bonds:',
                    'gridcfg':{'sticky':Tkinter.W,'columnspan':3}})
        ifd.append({'name':'selDRB0',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Use all atoms',
                'variable': self.hideSel,
                'value':1,
                'command':self.hideSelector
                },
            'gridcfg':{'sticky':'w'}})
        ifd.append({'name':'selDRB1',
            'widgetType':Tkinter.Radiobutton,
            'wcfg':{'text':'Set atoms to use',
                'variable': self.hideSel,
                'value':0,
                'command':self.hideSelector
                },
            'gridcfg':{'sticky':'w', 'row':-1, 'column':1}})
        ifd.append({'name': 'keySelector',
            'wtype':StringSelectorGUI,
            'widgetType':StringSelectorGUI,
            'wcfg':{ 'molSet': self.vf.Mols,
                    'vf': self.vf,
                    'all':1,
                    'crColor':(0.,1.,.2),
            },
            'gridcfg':{'sticky':'we', 'columnspan':2 }})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Ok',
            'wcfg':{'bd':6,
                    'command':self.Ok_cb,
                    },
            'gridcfg':{'sticky':'ew', 'columnspan':2 }})
        ifd.append({'widgetType': Tkinter.Button,
            'text':'Cancel',
            'wcfg':{'bd':6},
            'gridcfg':{'sticky':'ew', 'column':2,'row':-1},
            'command':self.Close_cb})
        self.form = self.vf.getUserInput(self.ifd, modal=0, blocking=0)
        self.form.root.protocol('WM_DELETE_WINDOW',self.Close_cb)
        self.hideSelector()


LimitHydrogenBondsGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'Limit Existing Hbonds',
                                          'menuCascadeName': 'Edit'}


LimitHydrogenBondsGUI = CommandGUI()
LimitHydrogenBondsGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'Limit Existing Hbonds by Energy', cascadeName='Edit')



class DisplayHBonds(MVCommand):
    """Base class to allow user to visualize pre-existing HydrogenBonds between 
atoms in viewer 
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : DisplayHBonds
   \nCommand : displayHBonds
   \nSynopsis:\n
        None <--- displayHBonds(ats)
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly

    def initGeom(self):
        #initialize:
        #   self.geom
        #   self.hasGeom
        pass


    def onAddCmdToViewer(self):
        self.initGeom()
        #from DejaVu.Labels import Labels
        self.verts = []
        self.showAll = Tkinter.IntVar()
        self.showAll.set(1)
        self.vf.loadModule('labelCommands', 'Pmv', log=0)
        # Create a ColorChooser.
        self.palette = ColorChooser(commands=self.color_cb,
                    exitFunction = self.hidePalette_cb)
        # pack
        self.palette.pack(fill='both', expand=1)
        # hide it 
        self.palette.hide()
        self.width = 100
        self.height = 100
        self.winfo_x = None
        self.winfo_y = None


    def hidePalette_cb(self, event=None):
        self.palette.hide()


    def reset(self):
        self.geom.Set(vertices=[], tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        self.hasGeom = 0


    def update(self, event=None):
        self.radii = self.radii_esw.get()
        self.hasGeom =0
        self.showAllHBonds()
        

    def showHBondLC(self, atName, event=None):
        #perhaps toggle on and off displaying the hbond with this???
        #find the bond and change its visible field
        for n in self.lb.curselection():
            #n in ('0','1','2')
            entry = self.lb.get(n)
            name = entry[:-2]
            #at = self.vf.Mols.NodesFromName(name)[0]
            ats = self.vf.Mols.NodesFromName(name)
            at = ats[0]
            #to deal with two atoms with the exact same name:
            if len(ats)>1:
                for a in ats:
                    if hasattr(a, 'hbonds'):
                        at = a
                        break
            b = at.hbonds[0]
            #b.visible = not(b.visible)
            if b.visible==0: b.visible = 1
            else: b.visible = 0
            entry = name + ' ' + str(b.visible)
            self.lb.delete(n)
            self.lb.insert(n, entry)
        self.hasGeom = 0
        self.showAllHBonds()


    def color_cb(self, colors):
        from DejaVu import colorTool
        self.geom.Set(materials = (colors[:3],), tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def changeColor(self, event=None):
        #col = self.palette.display(callback=self.color_cb, modal=1)
        #self.palette.pack(fill='both', expand=1)
        # Create a ColorChooser.
        if not hasattr(self, 'palette') or not self.palette.master.winfo_exists():
            self.palette = ColorChooser(commands=self.color_cb,
                        exitFunction = self.hidePalette_cb)
            # pack
            self.palette.pack(fill='both', expand=1)
            #??rh WHY hide it??
            #self.palette.hide()
        if not self.palette.master.winfo_ismapped():
            self.palette.master.deiconify()


    def getIfd(self, atNames):
        #base class
        pass

#        print 'in spheres getIfd'
#        if not hasattr(self, 'ifd'):
#            ifd = self.ifd = InputFormDescr(title = 'Show Hydrogen Bonds as Spheres')
#            ifd.append({'name': 'hbondLabel',
#                'widgetType': Tkinter.Label,
#                'wcfg':{'text': str(len(atNames)) + ' Atoms in hbonds:'},
#                'gridcfg':{'sticky': 'wens', 'columnspan':2}})
#            ifd.append({'name': 'atsLC',
#                'widgetType':ListChooser,
#                'wcfg':{
#                    'entries': atNames,
#                    'mode': 'multiple',
#                    'title': '',
#                    'command': CallBackFunction(self.showHBondLC, atNames),
#                    'lbwcfg':{'height':5, 
#                        'selectforeground': 'red',
#                        'exportselection': 0,
#                        'width': 30},
#                },
#                'gridcfg':{'sticky':'wens', 'row':2,'column':0, 'columnspan':2}})
#            ifd.append( {'name':'spacing',
#                'widgetType': ExtendedSliderWidget,
#                'wcfg':{'label':'spacing',
#                     'minval':.01, 'maxval':1.0,
#                    'immediate':1,
#                    'init':.4,
#                    'width':150,
#                    'command':self.update,
#                    'sliderType':'float',
#                    'entrypackcfg':{'side':'bottom'}},
#                'gridcfg':{'sticky':'wens'}})
#            ifd.append( {'name':'radii',
#                'widgetType': ExtendedSliderWidget,
#                'wcfg':{'label':'radii',
#                     'minval':.01, 'maxval':1.0,
#                    'immediate':1,
#                    'init':.1,
#                    'width':150,
#                    'command':self.update,
#                    'sliderType':'float',
#                    'entrypackcfg':{'side':'bottom'}},
#                'gridcfg':{'sticky':'wens','row':-1, 'column':1}})
#            ifd.append( {'name':'quality',
#                'widgetType': Tkinter.Scale,
#                'wcfg':{'label':'quality',
#                    'troughcolor':'green',
#                     'from_':2, 'to_':20,
#                    'orient':'horizontal',
#                    'length':'2i',
#                    'command':self.updateQuality },
#                'gridcfg':{'sticky':'wens'}})
#            ifd.append({'name':'changeColorBut',
#                'widgetType':Tkinter.Button,
#                'wcfg': { 'text':'Choose color',
#                    'relief':'flat',
#                    'command': self.changeColor},
#                'gridcfg':{'sticky':'wes', 'row':-1, 'column':1}})
#            ifd.append({'name':'changeVertsBut',
#                'widgetType':Tkinter.Button,
#                'wcfg': { 'text':'Set anchors',
#                    'command': self.changeDVerts},
#                'gridcfg':{'sticky':'wens'}})
#            ifd.append({'name':'closeBut',
#                'widgetType':Tkinter.Button,
#                'wcfg': { 'text':'Dismiss',
#                    'command': self.dismiss_cb},
#                'gridcfg':{'sticky':'wens', 'row':-1, 'column':1}})


    def setUpWidgets(self, atNames):
        if not hasattr(self, 'ifd'):
            self.getIfd(atNames)
        self.radii_esw = self.ifd.entryByName['radii']['widget']
        self.lb = self.ifd.entryByName['atsLC']['widget'].lb


    def doit(self, ats):
        #DisplayHBonds base class
        self.reset()
        hbats = AtomSet(ats.get(lambda x: hasattr(x, 'hbonds')))
        if not hbats:
            self.warningMsg('no hydrogen bonds found')
            return 'ERROR'
        hats = AtomSet(hbats.get(lambda x: x.element=='H'))
        atNames = []
        if hats:
            for h in hats:
                nStr = h.full_name() + ' 1'
                atNames.append((nStr, None))
                #h.hbonds[0].visible = 1
                for b in h.hbonds:
                    b.visible = 1
        else:
            hats = AtomSet([])
            bnds = []
            for at in hbats:
                bnds.extend(at.hbonds)
            bndD = {}
            for b in bnds:
                bndD[b] = 0
            hbnds2 = bndD.keys()
            hbnds2.sort()
            for b in hbnds2:
                b.visible = 1
                nStr = b.donAt.full_name() + ' 1'
                atNames.append((nStr, None))
                hats.append(b.donAt)
        self.hats = hats
        self.showAllHBonds()
        #print "hasattr(self, form)==", hasattr(self, 'form')
        self.getIfd(atNames)
        if not hasattr(self, 'form'):
            #print "building form"
            self.form  = self.vf.getUserInput(self.ifd, modal=0, blocking=0,
                 width=380, height=390)
            self.form.root.protocol('WM_DELETE_WINDOW',self.dismiss_cb)
            self.toplevel = self.form.f.master.master
            self.toplevel.positionfrom(who='user')
            if self.winfo_x is not None and self.winfo_y is not None:
                geom = self.width +'x'+ self.height +'+'
                geom += self.winfo_x + '+' + self.winfo_y
                self.toplevel.geometry(newGeometry=geom)
        #else:
            #print "already has form"
        self.setUpWidgets(atNames)


    def updateQuality(self, event=None):
        # asSpheres
        self.quality = self.quality_sl.get()
        self.geom.Set(quality=quality, tagModified=False)
        self.showAllHBonds()


    def dismiss_cb(self, event=None, **kw):
        # base class
        self.reset()
        try:
            self.width = str(self.toplevel.winfo_width())
            self.height = str(self.toplevel.winfo_height())
            self.winfo_x = str(self.toplevel.winfo_x())
            self.winfo_y = str(self.toplevel.winfo_y())
        except:
            pass
        self.vf.GUI.VIEWER.Redraw()
        if hasattr(self, 'ifd'):
            delattr(self, 'ifd')
        if hasattr(self, 'form2'):
            self.form2.destroy()
            delattr(self, 'form2')
        if hasattr(self, 'form'):
            self.form.destroy()
            delattr(self, 'form')
        if hasattr(self, 'palette'):
            #self.palette.exit()
            self.palette.hide()


    def setupDisplay(self):
        #draw geom between hAts OR donAts and accAts
        self.geom.Set(vertices = self.verts, radius = self.radius,
                      tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def interpolate(self, pt1, pt2):
        # self.spacing = .4
        length = dist(pt1, pt2)
        c1 = numpy.array(pt1)
        c2 = numpy.array(pt2)
        n = length/self.spacing
        npts = int(math.floor(n))
        # use floor of npts to set distance between > spacing
        delta = (c2-c1)/(1.0*npts)
        ##spacing = length/floor(npts)
        vertList = []
        for i in range(npts):
            vertList.append((c1+i*delta).tolist())
        vertList.append(pt2.tolist())
        return vertList


    def setDVerts(self, entries, event=None):
        if not hasattr(self, 'ifd2'):
            self.changeDVerts()
        lb = self.ifd2.entryByName['datsLC']['widget'].lb
        if lb.curselection() == (): return
        atName = lb.get(lb.curselection())
        ind = int(lb.curselection()[0])
        for h in self.hats:
            for b in h.hbonds:
                ats = b.donAt.parent.atoms.get(lambda x, atName=atName: x.name==atName)
                if ats is None or len(ats) == 0:
                    if b.hAt is not None: at = b.hAt
                    else: at = b.donAt
                else:
                    at = ats[0]
                b.spVert1 = at
        self.hasGeom = 0
        self.showAllHBonds()
        

    def setAVerts(self, entries, event=None):
        if not hasattr(self, 'ifd2'):
            self.changeDVerts()
        lb = self.ifd2.entryByName['aatsLC']['widget'].lb
        if lb.curselection() == (): return
        atName = lb.get(lb.curselection())
        ind = int(lb.curselection()[0])
        for h in self.hats:
            for b in h.hbonds:
                ats = b.accAt.parent.atoms.get(lambda x, atName=atName: x.name==atName)
                if ats is None or len(ats) == 0:
                    at = b.accAt
                else:
                    at = ats[0]
                b.spVert2 = at
        self.hasGeom = 0
        self.showAllHBonds()


    def changeDVerts(self, event=None):
        #for all residues in hbonds, pick new donorAttachment
        # and new acceptorAttachment
        entries = []
        ns = ['N','C','O','CA','reset']
        for n in ns:
            entries.append((n, None))

        if hasattr(self, 'form2'):
            self.form2.root.tkraise()
            return
        ifd2 = self.ifd2=InputFormDescr(title = 'Set Anchor Atoms')
        ifd2.append({'name': 'datsLC',
            'widgetType':ListChooser,
            'wcfg':{
                'entries': entries,
                'mode': 'single',
                'title': 'Donor Anchor',
                'command': CallBackFunction(self.setDVerts, entries),
                'lbwcfg':{'height':5, 
                    'selectforeground': 'red',
                    'exportselection': 0,
                    #'lbpackcfg':{'fill':'both', 'expand':1},
                    'width': 30},
            },
            'gridcfg':{'sticky':'wens', 'columnspan':2}})
        ifd2.append({'name': 'aatsLC',
            'widgetType':ListChooser,
            'wcfg':{
                'entries': entries,
                'mode': 'single',
                'title': 'Acceptor Anchor',
                'command': CallBackFunction(self.setAVerts, entries),
                'lbwcfg':{'height':5, 
                    'selectforeground': 'red',
                    #'lbpackcfg':{'fill':'both', 'expand':1},
                    'exportselection': 0,
                    'width': 30},
            },
            'gridcfg':{'sticky':'wens', 'columnspan':2}})
        ifd2.append({'name':'doneBut',
            'widgetType':Tkinter.Button,
            'wcfg': { 'text':'Done',
                'command': self.closeChangeDVertLC},
            'gridcfg':{'sticky':'wens'}})
        self.form2 = self.vf.getUserInput(self.ifd2, modal=0, blocking=0)
        self.form2.root.protocol('WM_DELETE_WINDOW',self.closeChangeDVertLC)


    def closeChangeDVertLC(self, event=None):
        if hasattr(self, 'ifd2'):
            delattr(self, 'ifd2')
        if hasattr(self, 'form2'):
            self.form2.destroy()
            delattr(self, 'form2')


    def showAllHBonds(self, event=None):
        if not self.hasGeom:
            verts = []
            for at in self.hats:
                for b in at.hbonds:
                    if hasattr(b, 'visible') and not b.visible: continue
                    if hasattr(b, 'spVert1'):
                        pt1 = getTransformedCoords(b.spVert1)
                        #verts.append(getTransformedCoords(b.spVert1))
                        #verts.append(b.spVert1.coords)
                    elif b.hAt is not None:
                        pt1 = getTransformedCoords(b.hAt)
                        #verts.append(getTransformedCoords(b.hAt))
                        #verts.append(b.hAt.coords) 
                    else:
                        pt1 = getTransformedCoords(b.donAt)
                        #verts.append(getTransformedCoords(b.donAt))
                        #verts.append(b.donAt.coords)
                    if hasattr(b, 'spVert2'):
                        #verts.extend(self.interpolate(verts[-1],
                        verts.extend(self.interpolate(pt1,
                                getTransformedCoords(b.spVert2)))
                    else:
                        #verts.extend(self.interpolate(verts[-1],
                        verts.extend(self.interpolate(pt1,
                                getTransformedCoords(b.accAt)))
            self.verts = verts
            self.hasGeom = 1
        self.geom.Set(vertices = self.verts, radii = self.radii,
                      tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def guiCallback(self):
        #showHbonds
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        sel =  self.vf.getSelection()
        #put a selector here
        if len(sel):
            ats = sel.findType(Atom)
            apply(self.doitWrapper, (ats,), {})



class DisplayHBondsAsSpheres(DisplayHBonds):
    """This class allows user to visualize pre-existing HydrogenBonds between 
atoms in viewer as small spheres
    \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : DisplayHBondsAsSpheres
   \nCommand : displayHBSpheres
   \nSynopsis:\n
        None <--- displayHBSpheres(nodes, **kw)
    """

    def initGeom(self):
        from DejaVu.IndexedPolylines import IndexedPolylines
        #from DejaVu.Labels import Labels
        self.quality = 4
        self.spheres = self.geom = Spheres('hbondSpheres', 
                    materials=((0,1,0),), shape=(0,3),
                    radii=0.1, quality=4, pickable=0, inheritMaterial=0, protected=True)
        geoms = [self.spheres]
        self.masterGeom = Geom('HbondsAsSpheresGeoms',shape=(0,0), 
                                pickable=0, protected=True)
        self.masterGeom.isScalable = 0
        if self.vf.hasGui:
            miscGeom = self.vf.GUI.miscGeom
            hbond_geoms = check_hbond_geoms(self.vf.GUI)
        
            self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=hbond_geoms)
            for item in geoms:
                self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom)
        self.hasGeom = 0
        self.spacing = .40
        self.radii = 0.1
        self.verts = []
        


    def __call__(self, nodes, **kw):
        """None <- displayHBSpheres(nodes, **kw) """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes = self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        ats = nodes.findType(Atom)
        apply(self.doitWrapper, (ats,), kw)



    def update(self, event=None):
        #as spheres
        self.radii = self.radii_esw.get()
        self.spacing = self.spacing_esw.get()
        self.hasGeom = 0
        self.showAllHBonds()
        

    def color_cb(self, colors):
        from DejaVu import colorTool
        self.geom.Set(materials = (colors[:3],), tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        col = colorTool.TkColor(colors[:3])
        self.quality_sl.config(troughcolor = col)


    def getIfd(self, atNames):
        #print 'in spheres getIfd'
        if not hasattr(self, 'ifd'):
            ifd = self.ifd=InputFormDescr(title = 'Show Hydrogen Bonds as Spheres')
            ifd.append({'name': 'hbondLabel',
                'widgetType': Tkinter.Label,
                'wcfg':{'text': str(len(atNames)) + ' Atoms in hbonds:\n(1=visible, 0 not visible)'},
                'gridcfg':{'sticky': 'wens', 'columnspan':2}})
            ifd.append({'name': 'atsLC',
                'widgetType':ListChooser,
                'wcfg':{
                    'entries': atNames,
                    'mode': 'multiple',
                    'title': '',
                    'command': CallBackFunction(self.showHBondLC, atNames),
                    'lbwcfg':{'height':5, 
                        'selectforeground': 'red',
                        'exportselection': 0,
                        'width': 30},
                },
                'gridcfg':{'sticky':'wens', 'row':2,'column':0, 'columnspan':2}})
            ifd.append( {'name':'spacing',
                'widgetType': ExtendedSliderWidget,
                'wcfg':{'label':'spacing',
                     'minval':.01, 'maxval':1.0,
                    'immediate':1,
                    'init':.4,
                    'width':150,
                    'command':self.update,
                    'sliderType':'float',
                    'entrypackcfg':{'side':'bottom'}},
                'gridcfg':{'sticky':'wens'}})
            ifd.append( {'name':'radii',
                'widgetType': ExtendedSliderWidget,
                'wcfg':{'label':'radii',
                     'minval':.01, 'maxval':1.0,
                    'immediate':1,
                    'init':.1,
                    'width':150,
                    'command':self.update,
                    'sliderType':'float',
                    'entrypackcfg':{'side':'bottom'}},
                'gridcfg':{'sticky':'wens','row':-1, 'column':1}})
            ifd.append( {'name':'quality',
                'widgetType': Tkinter.Scale,
                'wcfg':{'label':'quality',
                    'troughcolor':'green',
                     'from_':2, 'to_':20,
                    'orient':'horizontal',
                    'length':'2i',
                    'command':self.updateQuality },
                'gridcfg':{'sticky':'wens'}})
            ifd.append({'name':'changeColorBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Choose color',
                    'relief':'flat',
                    'command': self.changeColor},
                'gridcfg':{'sticky':'wes', 'row':-1, 'column':1}})
            ifd.append({'name':'changeVertsBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Set anchors',
                    'command': self.changeDVerts},
                'gridcfg':{'sticky':'wens'}})
            ifd.append({'name':'closeBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Dismiss',
                    'command': self.dismiss_cb},
                'gridcfg':{'sticky':'wens', 'row':-1, 'column':1}})


    def setUpWidgets(self, atNames):
        if not hasattr(self, 'ifd'):
            self.getIfd(atNames)
        self.radii_esw = self.ifd.entryByName['radii']['widget']
        self.spacing_esw = self.ifd.entryByName['spacing']['widget']
        self.quality_sl= self.ifd.entryByName['quality']['widget']
        self.quality_sl.set(self.quality)
        self.lb = self.ifd.entryByName['atsLC']['widget'].lb


    def updateQuality(self, event=None):
        self.quality = self.quality_sl.get()
        self.geom.Set(quality=self.quality, tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


DisplayHBondsAsSpheresGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'As Spheres',
                                          'menuCascadeName': 'Display'}


DisplayHBondsAsSpheresGUI = CommandGUI()
DisplayHBondsAsSpheresGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'As Spheres', cascadeName = 'Display')



class DisplayHBondsAsCylinders(DisplayHBonds):
    """This class allows user to visualize pre-existing HydrogenBonds between 
atoms in viewer as cylinders
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : DisplayHBondsAsCylinders
   \nCommand : displayHBCylinders
   \nSynopsis:\n
        None <- displayHBCylinders(nodes, **kw)
    """


    def initGeom(self):
        self.cylinders = self.geom = Cylinders('hbondCylinders', 
                    quality=40, culling=GL.GL_NONE, radii=(0.2),
                    materials=((0,1,0),), pickable=0, inheritMaterial=0)
        #DejaVu.Cylinders overwrites kw cull so have to reset it here
        self.cylinders.culling = GL.GL_NONE
        geoms = [self.cylinders]
        miscGeom = self.vf.GUI.miscGeom
        hbond_geoms = check_hbond_geoms(self.vf.GUI)
        self.masterGeom = Geom('HbondsAsCylindersGeoms',shape=(0,0), 
                                pickable=0, protected=True)
        self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=hbond_geoms)
        for item in geoms:
            self.vf.GUI.VIEWER.AddObject(item, parent=self.masterGeom)
        self.hasGeom = 0
        self.radii = 0.2
        self.length = 1.0
        self.oldlength = 1.0
        self.verts = []
        self.faces = []


    def __call__(self, nodes, **kw):
        """None <- displayHBCylinders(nodes, **kw) """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        nodes =self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        ats = nodes.findType(Atom)
        apply(self.doitWrapper, (ats,), kw)



    def getIfd(self, atNames):
        #cylinders
        if not hasattr(self, 'ifd'):
            ifd = self.ifd = InputFormDescr(title='Show Hydrogen Bonds as Cylinders')
            ifd.append({'name': 'hbondLabel',
                'widgetType': Tkinter.Label,
                'wcfg':{'text': str(len(atNames)) + ' Atoms in hbonds:\n(1=visible, 0 not visible)'},
                'gridcfg':{'sticky': 'wens', 'columnspan':2}})
            ifd.append({'name': 'atsLC',
                'widgetType':ListChooser,
                'wcfg':{
                    'entries': atNames,
                    'mode': 'multiple',
                    'title': '',
                    'command': CallBackFunction(self.showHBondLC, atNames),
                    'lbwcfg':{'height':5, 
                        'selectforeground': 'red',
                        'exportselection': 0,
                        'width': 30},
                },
                'gridcfg':{'sticky':'wens', 'row':2,'column':0, 'columnspan':2}})
            ifd.append( {'name':'radii',
                'widgetType': ExtendedSliderWidget,
                'wcfg':{'label':'radii',
                     'minval':.01, 'maxval':1.0,
                    'immediate':1,
                    'init':.2,
                    'width':250,
                    'command':self.update,
                    'sliderType':'float',
                    'entrypackcfg':{'side':'right'}},
                'gridcfg':{'sticky':'wens', 'columnspan':2}})
            ifd.append( {'name':'length',
                'widgetType': ExtendedSliderWidget,
                'wcfg':{'label':'length',
                     'minval':.01, 'maxval':5.0,
                    'immediate':1,
                    'init':1.0,
                    'width':250,
                    'command':self.update,
                    'sliderType':'float',
                    'entrypackcfg':{'side':'right'}},
                'gridcfg':{'sticky':'wens', 'columnspan':2}})
            ifd.append({'name':'changeVertsBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Set anchors',
                    'command': self.changeDVerts},
                'gridcfg':{'sticky':'wens'}})
            ifd.append({'name':'changeColorBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Change color',
                    'command': self.changeColor},
                'gridcfg':{'sticky':'wes', 'row':-1, 'column':1}})
            ifd.append({'name':'closeBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Dismiss',
                    'command': self.dismiss_cb},
                'gridcfg':{'sticky':'wens','columnspan':2 }})
            #ifd.append({'name':'changeVertsBut',
                #'widgetType':Tkinter.Button,
                #'wcfg': { 'text':'Set anchors',
                    #'command': self.changeDVerts},
                #'gridcfg':{'sticky':'wens'}})


    def setUpWidgets(self, atNames):
        if not hasattr(self, 'ifd'):
            self.getIfd(atNames)
        self.radii_esw = self.ifd.entryByName['radii']['widget']
        self.length_esw = self.ifd.entryByName['length']['widget']
        self.lb = self.ifd.entryByName['atsLC']['widget'].lb



    def update(self, event=None):
        #as cylinders
        self.radii = self.radii_esw.get()
        self.oldlength = self.length
        self.length = self.length_esw.get()
        self.hasGeom = 0
        self.showAllHBonds()
        

    def adjustVerts(self, v1, v2, length):
        #each end needs to be changed by length/2.
        #eg if length = 1.1, change each end by .05
        if length==self.oldlength:
            return (v1, v2)
        vec = numpy.array(v2) - numpy.array(v1)
        vec_len = math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2) 
        alpha = (length-1.)/2.  #(.5-1)/2.-> -.25%  or (1.5 -1.)/2. ->+.25%
        #delta v1 is v1 - alpha*vec_len
        #delta v2 is v2 + alpha*vec_len
        delta = alpha*vec_len
        return numpy.array(v1-delta*vec).astype('f'), numpy.array(v2+delta*vec).astype('f')

        



    def showAllHBonds(self, event=None):
        if not self.hasGeom:
            verts = []
            faces = []
            ct = 0
            for at in self.hats:
                for b in at.hbonds:
                    if hasattr(b, 'visible') and not b.visible: continue
                    if hasattr(b, 'spVert1'):
                        pt1 = getTransformedCoords(b.spVert1)
                    elif b.hAt is not None:
                        pt1 = getTransformedCoords(b.hAt)
                    else:
                        pt1 = getTransformedCoords(b.donAt)
                    if hasattr(b, 'spVert2'):
                        #verts.extend(self.interpolate(verts[-1],
                        at2 = b.spVert2
                    else:
                        at2 = b.accAt
                    pt2 = getTransformedCoords(at2)
                    #verts.extend([pt1, pt2])
                    verts.extend(self.adjustVerts(pt1, pt2, self.length))
                    faces.append((ct, ct+1))
                    ct = ct + 2
            #reset oldlength here, after recalc all hbond verts
            self.oldlength = self.length
            self.verts = verts
            self.faces = faces
            self.hasGeom  = 1

        self.cylinders.Set(vertices=self.verts, radii=self.radii,
                           faces=self.faces, tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


DisplayHBondsAsCylindersGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'As Cylinders',
                                          'menuCascadeName': 'Display'}


DisplayHBondsAsCylindersGUI = CommandGUI()
DisplayHBondsAsCylindersGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'As Cylinders', cascadeName = 'Display')



class ExtrudeHydrogenBonds(MVCommand):
    """This class allows user to visualize pre-existing HydrogenBonds between 
atoms in viewer using extruded geometries
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : ExtrudeHydrogenBonds
   \nCommand : extrudeHBonds
   \nSynopsis:\n
        None <- extrudeHBonds(nodes, shape2D=None, capsFlag=0, **kw)
    """

    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        self.flag = self.flag | self.objArgOnly

    def onAddCmdToViewer(self):
        self.spacing = 0.4
        self.geoms = []
        self.color = (0,1,1)
        # Create a ColorChooser.
        self.palette = ColorChooser(commands=self.color_cb,
                    exitFunction = self.hidePalette_cb)
        # pack
        self.palette.pack(fill='both', expand=1)
        # hide it 
        self.palette.hide()
        #self.palette.exit()

    def hidePalette_cb(self, event=None):
        self.palette.hide()

    def __call__(self, nodes, shape2D=None, capsFlag=0,**kw):
        """
        None <- extrudeHBonds(nodes, shape2D=None, capsFlag=0, **kw)
        """
        if type(nodes) is StringType:
            self.nodeLogString = "'"+nodes+"'"
        if shape2D==None:
            shape2D = Circle2D(radius=0.1)
        nodes = self.vf.expandNodes(nodes)
        if not len(nodes): return 'ERROR'
        nodes = nodes.findType(Atom)
        kw['capsFlag'] = capsFlag
        apply(self.doitWrapper, (nodes, shape2D), kw)


    def interpolate(self, pt1, pt2):
        length = dist(pt1, pt2)
        c1 = numpy.array(pt1)
        c2 = numpy.array(pt2)
        delta = (c2-c1)/(3.0)
        vertList = []
        for i in range(3):
            vertList.append((c1+i*delta).tolist())
        vertList.append(pt2)
        return vertList


    def updateDisplay(self):
        self.geoms = []
        atNames = []
        for b in self.hbonds:
            accAtCoords = getTransformedCoords(b.accAt)
            dAtCoords = getTransformedCoords(b.donAt)
            if b.hAt is not None:
                hAtCoords = getTransformedCoords(b.hAt)
                name = b.hAt.full_name()
            else:
                name = b.donAt.full_name()
            if not hasattr(b, 'spline'):
                #make one
                if b.hAt is not None:
                    #name = b.hAt.full_name()
                    coords = self.interpolate(hAtCoords, accAtCoords)
                    #coords = self.interpolate(b.hAt.coords, b.accAt.coords)
                else:
                    #name = b.donAt.full_name()
                    coords = self.interpolate(dAtCoords, accAtCoords)
                    #coords = self.interpolate(b.donAt.coords, b.accAt.coords)
                nStr = name + ' 1'
                atNames.append((nStr,None))
                b.spline = SplineObject(coords, name = name,
                                    nbchords = 4, interp = 'interpolation',
                                    continuity= 2, closed=0)
                smooth = b.spline.smooth
            else:
                nStr = name + ' %d'%b.g.visible
                atNames.append((nStr,None))
            if hasattr(b,'exVert1'):
                name = b.exVert1.full_name()
                coords1= b.exVert1.coords
            elif b.hAt is not None:
                name = b.hAt.full_name()
                coords1= hAtCoords
            else:
                name = b.donAt.full_name()
                coords1= dAtCoords
            if hasattr(b,'exVert2'):
                coords2= b.exVert2.coords
            else:
                coords2= accAtCoords

            coords = self.interpolate(coords1, coords2)
            b.spline = SplineObject(coords, name = name,
                                nbchords = 4, interp = 'interpolation',
                                continuity= 2, closed=0)
            smooth = b.spline.smooth
            if hasattr(b, 'g'):
                self.vf.GUI.VIEWER.RemoveObject(b.g)
            name = b.spline.name[-8:]+'_hbond'
            b.g = GleExtrude(name=name, visible=1, 
                             inheritMaterial=0,
                             culling=GL.GL_NONE,
                             materials = (self.color,))
            self.vf.GUI.VIEWER.AddObject(b.g)
            b.g.culling = GL.GL_NONE
            self.geoms.append(b.g)
            ctlAtmsInSel = AtomSet([b.donAt, b.accAt])
            ctlAtms = AtomSet([b.donAt, b.accAt])
            b.g.spline = (b.spline, ctlAtmsInSel, ctlAtms)

            # need to get new contourPoints and new contourNormals
            b.g.shape2D = self.shape
            contpts = numpy.array(b.g.shape2D.contpts)
            contourPoints = contpts[:,:2]
            contnorm = numpy.array(b.g.shape2D.contnorm)
            contourNormals = contnorm[:,:2]
            b.g.Set(trace3D = numpy.array((b.spline.smooth),'f'),
                    shape2D = self.shape,
                    contourUp = (0.,0.,1.),
                    inheritMaterial=0,
                    materials = (self.color,), visible=1,
                    capsFlag=self.capsFlag, tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        return atNames


    def doit(self, nodes, shape2D, capsFlag = 0):
        #extrude
        self.shape = shape2D
        self.capsFlag = capsFlag
        hats = AtomSet(nodes.get(lambda x: hasattr(x, 'hbonds')))
        hbonds = []
        for h in hats:
            for b in h.hbonds:
                if h == b.donAt:
                    b.visible = 1
                    hbonds.append(b)
        self.hbonds = hbonds
        atNames = self.updateDisplay()
        if not hasattr(self, 'ifd2'):
            ifd = self.ifd2=InputFormDescr(title = 'Show Extruded Hydrogen Bonds')
            ifd.append({'name': 'hbondLabel',
                'widgetType': Tkinter.Label,
                'wcfg':{'text': str(len(atNames)) + ' Atoms in hbonds:\n(1=visible, 0 not visible)'},
                'gridcfg':{'sticky': 'wens', 'columnspan':2}})

            ifd.append({'name': 'atsLC',
                'widgetType':ListChooser,
                'wcfg':{
                    'entries': atNames,
                    'mode': 'single',
                    'title': '',
                    'command': CallBackFunction(self.showHBondLC, atNames),
                    'lbwcfg':{'height':5, 
                        'selectforeground': 'red',
                        'exportselection': 0,
                        'width': 30},
                },
                'gridcfg':{'sticky':'wens', 'row':2,'column':0, 'columnspan':2}})
            ifd.append({'name':'changeColorBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Choose color',
                    'command': self.changeColor},
                'gridcfg':{'sticky':'wes'}})
            ifd.append({'name':'changeVertsBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Set anchors',
                    'command': self.changeDVerts},
                'gridcfg':{'sticky':'wens','row':-1,'column':1}})
            ifd.append({'name':'closeBut',
                'widgetType':Tkinter.Button,
                'wcfg': { 'text':'Dismiss',
                    'command': self.dismiss_cb},
                'gridcfg':{'sticky':'wens','columnspan':2}})
            self.form  = self.vf.getUserInput(ifd, modal=0, blocking=0,
                                              width=350, height=290)
            self.form.root.protocol('WM_DELETE_WINDOW',self.dismiss_cb)
            self.lb = self.ifd2.entryByName['atsLC']['widget'].lb


    def updateExtrusion_cb(self, event=1):
        #REDO DISPLAY HERE!!
        self.updateDisplay()


    def changeDVerts(self, event=None):
        #for all residues in hbonds, pick new donorAttachment
        # and new acceptorAttachment
        entries = []
        ns = ['N','C','O','CA','reset']
        for n in ns:
            entries.append((n, None))
        ifd3 = self.ifd3=InputFormDescr(title = 'Set Anchor Atoms')
        ifd3.append({'name': 'datsLC',
            'widgetType':ListChooser,
            'wcfg':{
                'entries': entries,
                'mode': 'single',
                'title': 'Donor Anchor',
                'command': CallBackFunction(self.setDVerts, entries),
                'lbwcfg':{'height':5, 
                    'selectforeground': 'red',
                    'exportselection': 0,
                    'width': 30},
            },
            'gridcfg':{'sticky':'wens', 'columnspan':2}})
        ifd3.append({'name': 'aatsLC',
            'widgetType':ListChooser,
            'wcfg':{
                'entries': entries,
                'mode': 'single',
                'title': 'Acceptor Anchor',
                'command': CallBackFunction(self.setAVerts, entries),
                'lbwcfg':{'height':5, 
                    'selectforeground': 'red',
                    'exportselection': 0,
                    'width': 30},
            },
            'gridcfg':{'sticky':'wens', 'columnspan':2}})
        ifd3.append({'name':'acceptBut',
            'widgetType':Tkinter.Button,
            'wcfg': { 'text':'Update extrusion',
                'command': self.updateExtrusion_cb},
            'gridcfg':{'sticky':'wens'}})
        ifd3.append({'name':'doneBut',
            'widgetType':Tkinter.Button,
            'wcfg': { 'text':'Done',
                'command': self.closeChangeDVertLC},
            'gridcfg':{'sticky':'wens'}})
        self.form3 = self.vf.getUserInput(self.ifd3, modal=0, blocking=0)
        self.form3.root.protocol('WM_DELETE_WINDOW',self.closeChangeDVertLC)


    def setDVerts(self, entries, event=None):
        if not hasattr(self, 'ifd3'):
            self.changeDVerts()
        lb = self.ifd3.entryByName['datsLC']['widget'].lb
        if lb.curselection() == (): return
        atName = lb.get(lb.curselection())
        ind = int(lb.curselection()[0])
        for b in self.hbonds:
            ats = b.donAt.parent.atoms.get(lambda x, atName=atName: x.name==atName)
            if ats is None:
                if b.hAt is not None: at = b.hAt
                else: at = b.donAt
            else:
                at = ats[0]
            b.exVert1 = at
        #REDO DISPLAY HERE!!
        #self.updateDisplay()
        

    def setAVerts(self, entries, event=None):
        if not hasattr(self, 'ifd3'):
            self.changeDVerts()
        lb = self.ifd3.entryByName['aatsLC']['widget'].lb
        if lb.curselection() == (): return
        atName = lb.get(lb.curselection())
        ind = int(lb.curselection()[0])
        for b in self.hbonds:
            ats = b.accAt.parent.atoms.get(lambda x, atName=atName: x.name==atName)
            if ats is None:
                at = b.accAt
            else:
                at = ats[0]
            b.exVert2 = at
        #REDO DISPLAY HERE!!
        #self.updateDisplay()


    def closeChangeDVertLC(self, event=None):
        if hasattr(self, 'ifd3'):
            delattr(self, 'ifd3')
        if hasattr(self, 'form3'):
            self.form3.destroy()


    def dismiss_cb(self, event=None, **kw):
        #extrudeHbonds
        for g in self.geoms:
            g.Set(visible=0, tagModified=False)
        self.vf.GUI.VIEWER.Redraw()
        if hasattr(self, 'ifd2'):
            delattr(self, 'ifd2')
        if hasattr(self, 'form3'):
            self.form3.destroy()
            delattr(self, 'form3')
        if hasattr(self, 'form'):
            self.form.destroy()
            delattr(self, 'form')
        if hasattr(self, 'palette'):
            self.palette.hide()
            #self.palette.exit()


    def color_cb(self, colors):
        self.color = colors[:3]
        for g in self.geoms:
            g.Set(materials = (self.color,), inheritMaterial=0,
                  tagModified=False)
        self.vf.GUI.VIEWER.Redraw()


    def changeColor(self, event=None):
        #col = self.palette.display(callback=self.color_cb, modal=1)
        if not hasattr(self, 'palette') or not self.palette.master.winfo_exists():
            self.palette = ColorChooser(commands=self.color_cb,
                        exitFunction = self.hidePalette_cb)
            # pack
            self.palette.pack(fill='both', expand=1)
            #??rh WHY hide it??
            #self.palette.hide()
        if not self.palette.master.winfo_ismapped():
            self.palette.master.deiconify()


    def showHBondLC(self, atName, event=None):
        #toggle on and off displaying the hbond
        #find the bond and Set its visible field
        #CHANGE ENTRY HERE!!!!
        for n in self.lb.curselection():
            #n in ('0','1','2')
            entry = self.lb.get(n)
            name = entry[:-2]
            #at = self.vf.Mols.NodesFromName(name)[0]
            #at = ats[0]
            ats = self.vf.Mols.NodesFromName(name)
            #to deal with two atoms with the exact same name:
            if len(ats)>1:
                for a in ats:
                    if hasattr(a, 'hbonds'):
                        at = a
                        break
            b = at.hbonds[0]
            b.g.Set(visible = not(b.g.visible), tagModified=False)
            if b.g.visible:
                entry = name + ' 1'
            else:
                entry = name + ' 0'
            self.lb.delete(n)
            self.lb.insert(n, entry)
            self.vf.GUI.VIEWER.Redraw()


    def guiCallback(self):
        # Write a shape chooser method that should be reused in
        # secondaryStructureCommands, CATrace commands, and splineCommands.
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        idf = InputFormDescr(title='Extrude Spline')
        # List Chooser for the shape to extrude
        entries = [('rectangle',None),('circle',None),
                   ('ellipse',None),
                   ('square',None),('triangle',None)
                   ]

        idf.append({'name':'shape',
                    'widgetType':ListChooser,
                    'defaultValue':'circle',
                    'wcfg':{'entries': entries,
                            'lbwcfg':{'height':5,'exportselection':0},
                            'title':'Choose a shape to extrude'},
                    'gridcfg':{'sticky':'wens'}
                    })
        typeshape = self.vf.getUserInput(idf)
        if typeshape == {} or typeshape['shape'] == []: return
        
 
        ########################################################
        
        # Shape is a Rectangle
        if typeshape['shape'][0]=='rectangle':
            idf = InputFormDescr(title ="Rectangle size :")
            idf.append( {'name': 'width',
                         'widgetType':ExtendedSliderWidget,
                         'wcfg':{'label': 'Width',
                                 'minval':0.05,'maxval':3.0 ,
                                 'init':1.2,
                                 'labelsCursorFormat':'%1.2f',
                                 'sliderType':'float',
                                 'entrywcfg':{'width':4},
                                 'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}
                     })

            idf.append( {'name': 'height',
                         'widgetType':ExtendedSliderWidget,
                         'wcfg':{'label': 'Height',
                                 'minval':0.05,'maxval':3.0 ,
                                 'init':0.4,
                                 'labelsCursorFormat':'%1.2f',
                                 'sliderType':'float',
                                 'entrywcfg':{'width':4},
                                 'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}
                         })

            #idf.append({'name':'frontcap',
                        #'widgetType':Tkinter.Checkbutton,
                        #'defaultValue':1,'type':float,
                        #'wcfg':{'text':'front cap',
                                #'state': 'disabled',
                                #'variable': Tkinter.IntVar()},
                        #'gridcfg':{'sticky':'we'}})

            idf.append({'name':'caps',
                        'widgetType':Tkinter.Checkbutton,
                        'defaultValue':1,
                        'wcfg':{'text':'Caps',
                                'variable': Tkinter.IntVar()},
                        'gridcfg':{'sticky':'we','row':-1}})

            val = self.vf.getUserInput(idf)
            if val:
                shape = Rectangle2DDupRectangle2D(width = val['width'],
                                      height = val['height'],
                                      vertDup=1, firstDup=1)
                capsFlag = val['caps']

        # Shape is a Circle:
        elif typeshape['shape'][0]=='circle':
            idf = InputFormDescr(title="Circle size :")

            idf.append( {'name': 'radius',
                         'widgetType':ExtendedSliderWidget,
                         'wcfg':{'label': 'Radius',
                                 'minval':0.05,'maxval':3.0 ,
                                 'init':0.2,
                                 'labelsCursorFormat':'%1.2f',
                                 'sliderType':'float',
                                 'entrywcfg':{'width':4},
                                 'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}
                     })

            idf.append({'name':'caps',
                        'widgetType':Tkinter.Checkbutton,
                        'defaultValue':1,
                        'wcfg':{'text':'Caps',
                                'variable': Tkinter.IntVar()},
                        'gridcfg':{'sticky':'we'}})

            val = self.vf.getUserInput(idf)
            if val:
                shape = Circle2D(radius=val['radius'], firstDup = 1)
                capsFlag = val['caps']

        # Shape is an Ellipse
        elif typeshape['shape'][0]=='ellipse':
            idf = InputFormDescr(title="Ellipse demi grand Axis and demi small axis :")

            idf.append( {'name': 'grand',
                         'widgetType':ExtendedSliderWidget,
                         'wcfg':{'label': 'demiGrandAxis',
                                 'minval':0.05,'maxval':3.0 ,
                                 'init':0.5,
                                 'labelsCursorFormat':'%1.2f',
                                 'sliderType':'float',
                                 'entrywcfg':{'width':4},
                                 'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}
                     })
            idf.append( {'name': 'small',
                         'widgetType':ExtendedSliderWidget,
                         'wcfg':{'label': 'demiSmallAxis',
                                 'minval':0.05,'maxval':3.0 ,
                                 'init':0.2,
                                 'labelsCursorFormat':'%1.2f',
                                 'sliderType':'float',
                                 'entrywcfg':{'width':4},
                                 'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}
                     })

            idf.append({'name':'caps',
                        'widgetType':Tkinter.Checkbutton,
                        'defaultValue':1,
                        'wcfg':{'text':'Caps ',
                                'variable': Tkinter.IntVar()},
                        'gridcfg':{'sticky':'we','row':-1}})

            val = self.vf.getUserInput(idf)
            if val:
                shape = Ellipse2D(demiGrandAxis= val['grand'],
                                  demiSmallAxis=val['small'], firstDup=1)
                capsFlag = val['caps']
                
        # Shape is a Square:
        elif typeshape['shape'][0]=='square':

            idf = InputFormDescr(title="Square size :")
            idf.append( {'name': 'sidelength',
                         'widgetType':ExtendedSliderWidget,
                         'wcfg':{'label': 'Length of side:',
                                 'minval':0.05,'maxval':3.0 ,
                                 'init':0.5,
                                 'labelsCursorFormat':'%1.2f',
                                 'sliderType':'float',
                                 'entrywcfg':{'width':4},
                                 'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}
                     })

            idf.append({'name':'caps',
                        'widgetType':Tkinter.Checkbutton,
                        'defaultValue':1,
                         'wcfg':{'text':'Caps',
                                 'variable': Tkinter.IntVar()},
                        'gridcfg':{'sticky':'we'}})

            val = self.vf.getUserInput(idf)
            
            if val:
                shape = Square2D(side=val['sidelength'], firstDup=1, vertDup=1)
                capsFlag = val['caps']

        # Shape is a Triangle:
        elif typeshape['shape'][0]=='triangle':

            idf = InputFormDescr(title="Triangle size :")
            idf.append( {'name': 'sidelength',
                         'widgetType':ExtendedSliderWidget,
                         'wcfg':{'label': 'Length of side:    ',
                                 'minval':0.05,'maxval':3.0 ,
                                 'init':0.5,
                                 'labelsCursorFormat':'%1.2f',
                                 'sliderType':'float',
                                 'entrywcfg':{'width':4},
                                 'entrypackcfg':{'side':'right'}},
                     'gridcfg':{'columnspan':2,'sticky':'we'}
                     })

            idf.append({'name':'caps',
                        'widgetType':Tkinter.Checkbutton,
                        'defaultValue':1,
                        'wcfg':{'text':'Caps',
                                'variable': Tkinter.IntVar()},
                        'gridcfg':{'sticky':'we'}})


            val = self.vf.getUserInput(idf)
            if val:
                shape = Triangle2D(side=val['sidelength'], firstDup=1,
                                   vertDup=1)
                capsFlag = val['caps']

        else: return

        if val:
            nodes = self.vf.getSelection()
            if not len(nodes): return 'ERROR'
            nodes = nodes.findType(Atom)
            self.doitWrapper(nodes, shape, capsFlag = capsFlag)

ExtrudeHydrogenBondsGuiDescr = {'widgetType':'Menu',
                                'menuBarName':'menuRoot',
                                'menuButtonName':'Hydrogen Bonds',
                                'menuEntryLabel':'As Extruded Shapes',
                                'menuCascadeName': 'Display'}

ExtrudeHydrogenBondsGUI = CommandGUI()
ExtrudeHydrogenBondsGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'As Extruded Shapes', cascadeName = 'Display')



class WriteAssemblyHBonds(MVCommand):
    """Command to write a file specifying >1 chain with hydrogen bonds. Chains which are hydrogen-bonded are written in one pdb file.  They may or may not be part of the same molecule.
    \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : WriteAssemblyHBonds
   \nCommand : writeAssemblyHB
   \nSynopsis:\n
        None <--- writeAssemblyHB(filename, nodes, **kw)
   \nRequired Arguments:\n     
        filename --- name of the hbond file\n
        nodes --- TreeNodeSet holding the current selection
    """


    def onAddCmdToViewer(self):
        self.PdbWriter = PdbWriter()


    def __call__(self, filename, nodes, **kw):
        """None <--- writeAssemblyHB(filename, nodes, **kw)
        \nfilename ---  name of the hbond file
        \nnodes --- TreeNodeSet holding the current selection """
        apply(self.doitWrapper, (filename, nodes,), kw)


    def fixIDS(self, chains):
        idList = chains.id
        ifd = self.ifd = InputFormDescr(title = "Set unique chain ids")
        ent_varDict = self.ent_varDict = {}
        cb_varDict = self.cb_varDict = {}
        self.nStrs = []
        self.entStrs = []
        self.chains = chains
        ifd.append({'name':'Lab1',
                    'widgetType':Tkinter.Label,
                    'text':'Current',
                    'gridcfg':{'sticky':'we'}})
        ifd.append({'name':'Lab2',
                    'widgetType':Tkinter.Label,
                    'text':'New',
                    'gridcfg':{'sticky':'we', 'row':-1, 'column':1}})
        for ch in chains:
            idList = chains.id
            ct = idList.count(ch.id)
            if ct!=1:
                for i in range(ct-1):
                    ind = idList.index(ch.id)
                    idList[ind] = ch.id+str(i)
        for i in range(len(chains)):
            chid = idList[i]
            ch = chains[i]
            nStr = chid+'_lab'
            self.nStrs.append(nStr)
            entStr = chid+'_ent'
            self.entStrs.append(entStr)
            newEntVar = ent_varDict[ch] = Tkinter.StringVar()
            newEntVar.set(ch.id)
            newCbVar = cb_varDict[ch] = Tkinter.IntVar()
            ifd.append({'name':nStr,
                    'widgetType':Tkinter.Label,
                    'text':ch.full_name(),
                    'gridcfg':{'sticky':'we'}})
            ifd.append({'name':entStr,
                'widgetType':Tkinter.Entry,
                'wcfg':{ 'textvariable': newEntVar, },
                'gridcfg':{'sticky':'we', 'row':-1, 'column':1}})
        vals = self.vf.getUserInput(self.ifd, modal=0, blocking=1, okcancel=1)
        if vals:
            for i in range(len(chains)):
                #CHECK for changes in id/name
                ch = chains[i]
                ll = ch.id
                wl = strip(vals[self.entStrs[i]])
                if ll!=wl:
                    print 'changed chain ', ch.full_name(), ' to ',  
                    ch.id = wl[0]
                    ch.name = wl[0]
                    print  ch.full_name()


    def writeHYDBNDRecords(self, atoms, fptr):
        for a in atoms:
            if not hasattr(a, 'hbonds'): continue 
            for b in a.hbonds:
                #only write a record if a is the donor 
                if b.donAt!=a: continue
                #columns 1-6 + spaces for columns 7-12
                s = 'HYDBND      '
                #columns 13-16 donor name
                #strip off altloc + save it
                nameStr, altLoc = self.PdbWriter.formatName(a)
                s = s + nameStr
                #column 17 donor altLoc indicator
                if altLoc: s = s + altLoc
                else: s = s + ' '
                #columns 18-20 donor parent name (residue type)
                #column 21 space + column 22 chain id
                s = s + a.parent.type + ' ' + a.parent.parent.id 
                #columns 23-27 donor parent number 
                #column 27 insertion code
                #column 28 space
                s = s + '%5d' %int(a.parent.number) + '  '

                # write the OPTIONAL hydrogen atom info
                hAt = b.hAt
                if hAt is None:
                    s = s + "              "
                else:
                    #columns 30-33 hydrogen atom name
                    nameStr, altLoc = self.PdbWriter.formatName(hAt)
                    s = s + nameStr
                    #column 34 hydrogen atom  altLoc indicator
                    if altLoc: s = s + altLoc
                    else: s = s + ' '
                    #column 35 space + column 36 chain id
                    s = s + ' ' + hAt.parent.parent.id
                    #columns 37-41 hydrogen atom parent number 
                    # nb: 42 would be insertion code then 43 a space
                    s = s + '%5d' %int(hAt.parent.number) + '  '

                # write the acceptor atom info
                acc = b.accAt 
                #columns 44-47 acceptor name
                nameStr, altLoc = self.PdbWriter.formatName(acc)
                s = s + nameStr
                #column 48 acceptor altLoc indicator
                if altLoc: s = s + altLoc
                else: s = s + ' '
                #columns 49-51 acceptor parent name (residue type)
                #column 52 space + column 53 chain id
                s = s + acc.parent.type + ' ' + acc.parent.parent.id
                #columns 54-58 acceptor parent number 
                # nb: 59 would be insertion code 
                # ???? non implemented->
                # 60-65 symmetry operator for 1st non-hyd. atom
                s = s + '%5d' %int(acc.parent.number) + '   \n'
                fptr.write(s)
        


    def doit(self, filename, nodes):
        #writeAssembly
        nodes = self.vf.expandNodes(nodes)
        if len(nodes)==0: return 'ERROR'
        nodes = nodes.findType(Atom)
        hbats = nodes.get(lambda x: hasattr(x, 'hbonds'))
        if hbats is None:
            self.warningMsg('no atoms with hbonds specified')
            return 'ERROR'
        #CHECK FOR presence and uniqueness of chain ids
        chains = nodes.parent.uniq().parent.uniq()
        fptr = open(filename, 'w')
        #to write:
        self.writeHYDBNDRecords(nodes, fptr)
        for ch in chains:
            for at in ch.residues.atoms:
                self.PdbWriter.write_atom(fptr, at)
            try:
                self.PdbWriter.write_TER(fptr, at)
            except:
                ostr =self.PdbWriter.defineTERRecord(at)
                fptr.write(ostr)
        fptr.close()

    
    def guiCallback(self):
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        nodes = self.vf.getSelection()
        if not len(nodes): return None
        nodes = nodes.findType(Atom)
        hbats = AtomSet(nodes.get(lambda x: hasattr(x, 'hbonds')))
        if not hbats:
            self.warningMsg('no atoms with hbonds specified')
            return 'ERROR'
        chains = nodes.parent.uniq().parent.uniq()
        self.fixIDS(chains)
        file = self.vf.askFileSave(types=[('pdb files', '*.pdb'), 
                ('any', '*.*')], title="file to write hbonded chains\n(could be >1 molecule):")
        if file is None: return
        self.doitWrapper(file, nodes)


WriteAssemblyHBondsGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'Write Assembly with Hbonds',
                                          'menuCascadeName': 'Assemblies'}

WriteAssemblyHBondsGUI = CommandGUI()
WriteAssemblyHBondsGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'Write Hbonded Chains as 1 Mol', cascadeName='Assemblies')



class WriteIntermolHBonds(MVCommand):
    """Command to write files specifying intermolecular hydrogen bonds
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : WriteIntermolHBonds
   \nCommand : writeIntermolHB
   \nSynopsis:\n
        None <- writeIntermolHB(filename, nodes, **kw)
   \nRequired Arguments:\n       
        filename --- name of the hbond file\n
        nodes --- TreeNodeSet holding the current selection
    """


    def __call__(self, filename, nodes, **kw):
        """None <- writeIntermolHB(filename, nodes, **kw)
        \nfilename --- name of the hbond file
        \nnodes --- TreeNodeSet holding the current selection """
        apply(self.doitWrapper, (filename, nodes,), kw)


    def doit(self, filename, nodes):
        #writeIntermolHBonds
        nodes = self.vf.expandNodes(nodes)
        if len(nodes)==0: return 'ERROR'
        nodes = nodes.findType(Atom)
        hbats = AtomSet(nodes.get(lambda x: hasattr(x, 'hbonds')))
        if not hbats:
            self.warningMsg('no atoms with hbonds specified')
            return 'ERROR'
        bnds = []
        for at in hbats:
            for b in at.hbonds:
                if b.donAt.top!=b.accAt.top and b not in bnds:
                    bnds.append(b)
        if not len(bnds):
            self.warningMsg('no intermolecular hydrogen bonds in specified atoms')
            return 'ERROR'

        fptr = open(filename, 'w')
        for b in bnds:
            outstring = ''+b.donAt.full_name() + ',' + b.accAt.full_name()
            if b.hAt is not None:
                outstring = outstring + ',' + b.hAt.full_name()
            outstring = outstring + '\n'
            fptr.write(outstring)
        fptr.close()

    
    def guiCallback(self):
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        file = self.vf.askFileSave(types=[('hbnd files', '*.hbnd'), 
                ('any', '*.*')], title="file to write intermolecular hbonds:")
        if file is None: return
        nodes = self.vf.getSelection()
        if not len(nodes): return None
        self.doitWrapper(file, nodes)

WriteIntermolHBondsGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'Write Intermolecular Hbonds',
                                          'menuCascadeName': 'Assemblies'}

WriteIntermolHBondsGUI = CommandGUI()
WriteIntermolHBondsGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'Write Intermolecular Hbonds', cascadeName='Assemblies')



class ReadIntermolHBonds(MVCommand):
    """Command to read files specifying intermolecular hydrogen bonds
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : ReadIntermolHBonds
   \nCommand : readIntermolHB
   \nSynopsis:\n
        None <--- readIntermolHB(filename, nodes, **kw)
   \nRequired Arguments:\n
        filename --- name of the hbond file
    """


    def __call__(self, filename, **kw):
        """None <--- readIntermolHB(filename, nodes, **kw)
        \nfilename --- name of the hbond file"""
        if not os.path.exists(filename):
            raise IOError
        apply(self.doitWrapper, (filename,), kw)


    def doit(self, filename):
        #read IntermolHBonds
        fptr = open(filename, 'r')
        allLines = fptr.readlines()
        fptr.close()
        ctr = 0
        for line in allLines:
            lList = split(line,',')
            donAt = self.vf.Mols.NodesFromName(lList[0])
            if donAt is None:
                msg = donAt + ' not in mv.allAtoms'
                self.warningMsg(msg)
                return 'ERROR'
            else:
                donAt = donAt[0]
            if len(lList)==2:
                accAt = self.vf.Mols.NodesFromName(lList[1][:-1])
            else:
                accAt = self.vf.Mols.NodesFromName(lList[1])
            if accAt is None:
                msg = accAt + ' not in mv.allAtoms'
                self.warningMsg(msg)
                return 'ERROR'
            else:
                accAt = accAt[0]
            if len(lList)==3:
                #there is a newlinecharacter
                hAt = self.vf.Mols.NodesFromName(lList[2][:-1])
                if hAt is None:
                    msg = hAt + ' not in mv.allAtoms'
                    self.warningMsg(msg)
                    return 'ERROR'
                else:
                    hAt = hAt[0]
            else: hAt = None

            #at this point build a hbond
            newHB = HydrogenBond(donAt, accAt, hAt)
            for at in [donAt, accAt]:
                if hasattr(at, 'hbonds'):
                    at.hbonds.append(newHB)
                else:
                    at.hbonds = [newHB]
            if hAt is not None:
                hAt.hbonds = [newHB]
            ctr = ctr + 1
        msg = 'built '+str(ctr) + ' intermolecular hydrogen bonds'
        self.warningMsg(msg)

    
    def guiCallback(self):
        if not len(self.vf.Mols):
            self.warningMsg('no molecules in viewer')
            return 
        file = self.vf.askFileOpen(types=[('hbnd files', '*.hbnd'), 
                ('any', '*.*')], title="intermolecular hbonds file:")
        if file is None: return
        apply(self.doitWrapper, (file,), {})


ReadIntermolHBondsGuiDescr = {'widgetType':'Menu',
                                          'menuBarName':'menuRoot',
                                          'menuButtonName':'Hydrogen Bonds',
                                          'menuEntryLabel':'Read Intermolecular Hbonds',
                                          'menuCascadeName': 'Assemblies'}

ReadIntermolHBondsGUI = CommandGUI()
ReadIntermolHBondsGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds',
        'Read Intermolecular Hbonds', cascadeName='Assemblies')



class AddHBondCommandGUICommand(MVCommand, MVAtomICOM):
    """GUI command wrapper for AddHBondCommand which allows user to add hbonds between selected atoms. Hydrogen bonds are built assuming user picked hydrogen atom (or donor atom if there is no hydrogen atom) first followed by the acceptor atom.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : AddHBondCommandGUICommand
   \nCommand : addHBondGC
   \nSynopsis:\n 
        None<---addHBondGC(atoms)
   \nRequired Arguments:\n     
        atoms ---  atom(s)
    """
    
    def __init__(self, func=None):
        MVCommand.__init__(self, func)
        MVAtomICOM.__init__(self)
        self.atomList = AtomSet([])
        self.undoAtList = AtomSet([])
        self.save = None


    def onRemoveObjectFromViewer(self, obj):
        removeAts = AtomSet([])
        for at in self.atomList:
            if at in obj.allAtoms:
                removeAts.append(at)
        self.atomList = self.atomList - removeAts
        removeAts = AtomSet([])
        for at in self.undoAtList:
            if at in obj.allAtoms:
                removeAts.append(at)
        self.undoAtList = self.undoAtList - removeAts
        self.update()


    def onAddCmdToViewer(self):
        if not self.vf.commands.has_key('setICOM'):
            self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv',
                                topCommand=0) 
        miscGeom = self.vf.GUI.miscGeom
        hbond_geoms = check_hbond_geoms(self.vf.GUI)
        self.masterGeom = Geom('addHBondGeoms',shape=(0,0), 
                                pickable=0, protected=True)
        self.masterGeom.isScalable = 0
        self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=hbond_geoms)
        self.spheres = Spheres(name='addHBondSpheres', shape=(0,3),
                               inheritMaterial=0,
                               radii=0.2, quality=15,
                               materials = ((1.,1.,0.),), protected=True) 
        self.vf.GUI.VIEWER.AddObject(self.spheres, parent=self.masterGeom)


    def __call__(self, atoms, **kw):
        """None<-addHBondGC(atoms)
        \natoms  : atom(s)"""
        #print "addHBondGC"
        if type(atoms) is StringType:
            self.nodeLogString = "'"+atoms+"'"
        ats = self.vf.expandNodes(atoms)
        if not len(ats): return 'ERROR'
        return apply(self.doitWrapper, (ats,), kw)


    def doit(self, ats):
        #addHBondGC
        #can only do it on first two picked
        if len(ats)>2:
            ats = ats[:2]
        for at in ats:
            #check for repeats of same atom
            lenAts = len(self.atomList)
            if lenAts and at==self.atomList[-1]:
                continue
            self.atomList.append(at)
            self.undoAtList.append(at)
            lenAts = len(self.atomList)
        #self.update()
        #if only have one atom, there is nothing else to do
        if lenAts<2: return
        #now build hbonds between pairs of atoms
        atSet = self.atomList
        if lenAts%2!=0:
            atSet = atSet[:-1]
            #all pairs of atoms will be bonded
            #so keep only the last one
            atomList = atSet[-1:]
            lenAts = lenAts -1
        else:
            atomList = AtomSet([])
        newHB = self.vf.addHBond(atSet[0], atSet[1], topCommand=0)
        self.update()
        self.atomList = atomList
        #to update display, need to call appropriate commands
        geomList = [self.vf.showHBonds.lines, self.vf.hbondsAsSpheres.spheres]
        cmdList = [self.vf.showHBonds, self.vf.hbondsAsSpheres]
        #FIX THIS: extrude geoms are too slow
        #if len(self.vf.extrudeHBonds.geoms):
            #geomList.append(self.vf.extrudeHBonds.geoms[0])
            #cmdList.append(self.vf.extrudeHBonds)
        for i in range(len(geomList)):
            geom = geomList[i]
            cmd = cmdList[i]
            hats = AtomSet(self.vf.allAtoms.get(lambda x: hasattr(x,'hbonds')))
            if len(geom.vertexSet):
                #first call dismiss_cb???
                apply(cmd.dismiss_cb,(),{})
                d = {}
                d['topCommand']=0
                #update a visible geometry here
                apply(cmd,(hats,), d)
        self.update()


##     def negateCmdAfter(self, ats, **kw):
##         return
##         lenUndoAts = len(self.undoAtList)
##         lenAts = len(ats)
##         if lenAts==1:
##             #after adding 1 self.atomList would be 1 or 0
##             if len(self.atomList)==1:
##                 s = '0'
##                 ustr='"c=self.addHBondGC;c.atomList=c.atomList[:'+s+'];c.undoAtList=c.undoAtList[:-1];c.update()"'
##             else:
##                 ind = str(lenUndoAts - 2)
##                 ustr = '"c=self.addHBondGC;nodes=self.expandNodes('+'\''+self.undoAtList[-2:].full_name()+'\''+'); self.removeHBondGC(nodes, topCommand=0);c.atomList=nodes[:1];c.undoAtList=c.undoAtList[:'+ind+'];c.update()"'
##         elif lenUndoAts>lenAts:
##             ustr='"c=self.addHBondGC;nodes=self.expandNodes('+'\''+ats.full_name()+'\''+');self.removeHBondGC(nodes, topCommand=0);c.undoAtList=c.undoAtList[:'+str(lenAts)+']"'
##         else:
##             ustr='"c=self.addHBondGC;nodes=self.expandNodes('+'\''+ats.full_name()+'\''+');self.removeHBondGC(nodes, topCommand=0);c.undoAtList=c.undoAtList[:0]"'
##         if len(self.atomList) and lenAts>1:
##             atStr = self.atomList.full_name()
##             estr = ';nodes=self.expandNodes('+'\''+self.atomList.full_name()+'\''+');c.atomList=nodes;c.update()"'
##             ustr = ustr + estr
##         self.undoCmds = "exec("+ustr+")"


    def update(self, event=None):
        if not len(self.atomList):
            self.spheres.Set(vertices=[], tagModified=False)
            self.vf.labelByExpression(self.atomList, negate=1, topCommand=0, setupNegate=False)
            self.vf.GUI.VIEWER.Redraw()
            return
        self.lineVertices=[]
        #each time have to recalculate lineVertices
        for at in self.atomList:
            c1 = getTransformedCoords(at)
            self.lineVertices.append(tuple(c1))
        self.spheres.Set(vertices=self.lineVertices, tagModified=False)
        self.vf.labelByExpression(self.atomList,
                                  function = 'lambda x: x.full_name()',
                                  lambdaFunc = 1,
                                  textcolor = 'yellow',
                                  format = '', negate = 0,
                                  location = 'Last', log = 0,
                                  font = 'arial1.glf', only = 1,
                                  topCommand=0)
        #setting spheres doesn't trigger redraw so do it explicitly
        self.vf.GUI.VIEWER.Redraw()


    def guiCallback(self, event=None):
        self.save = self.vf.ICmdCaller.commands.value["Shift_L"]
        self.vf.setICOM(self, modifier="Shift_L", topCommand=0, setupNegate=False)


    def startICOM(self):
        self.vf.setIcomLevel( Atom , topCommand=0)




AddHBondGuiDescr = {'widgetType':'Menu',
                              'menuBarName':'menuRoot',
                              'menuButtonName':'Hydrogen Bonds',
                              'menuEntryLabel':'Add Hydrogen Bonds',
                              'menuCascadeName': 'Edit'}

AddHBondCommandGUICommandGUI=CommandGUI()
AddHBondCommandGUICommandGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds', 'Add Hydrogen Bonds', cascadeName='Edit')



class AddHBondCommand(MVCommand):
    """AddHBondCommand allows user to add hydrogen bonds between pairs of atoms.
    \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : AddHBondCommand
   \nCommand : addHBond
   \nSynopsis:\n
        None<---addHBond(atom1, atom2)
   \nRequired Arguments:\n       
        atom1 --- first atom\n
        atom2 --- second atom
    """

    def negateCmdBefore(self, atom1, atom2):
        
        return ([(self.vf.removeHBond, (atom1.full_name(),atom2.full_name()),{})], self.vf.removeHBond.name)


    def __call__(self, atom1, atom2, **kw):
        """None<-addHBond(atom1, atom2)
        \natom1 --- first atom
        \natom2  --- second atom """
        ats = self.vf.expandNodes(atom1)
        if not len(ats): return 'ERROR'
        at1 = ats[0]
        ats = self.vf.expandNodes(atom2)
        if not len(ats): return 'ERROR'
        at2 = ats[0]
        #check that at1 and at2 are not already in an hbond
        if hasattr(at1, 'hbonds') and hasattr(at2, 'hbonds'):
            for b in at1.hbonds:
                if b.donAt==at2 or b.hAt==at2 or b.accAt==at2:
                    return 'ERROR'
            for b in at2.hbonds:
                if b.donAt==at2 or b.hAt==at2 or b.accAt==at2:
                    return 'ERROR'
        return apply(self.doitWrapper, (at1, at2), kw)


    def doit(self, at1, at2):
        #addHBond
        #this assumes atoms are picked in order
        #from donor to acceptor
        donAt = at1
        accAt = at1
        if at1.element=='H':
            hAt = at1
            accAt = at2
        elif at2.element=='H':
            hAt = at2
            accAt = at1
        else:
            hAt = None
        if hAt is not None :
            b = hAt.bonds[0]
            donAt = b.atom1
            if id(donAt)==id(hAt):
                donAt = b.atom2
        newHB = HydrogenBond( donAt, accAt, hAt=hAt)
        if not hasattr(accAt, 'hbonds'):
            accAt.hbonds=[]
        if not hasattr(donAt, 'hbonds'):
            donAt.hbonds=[]
        accAt.hbonds.append(newHB)
        donAt.hbonds.append(newHB)
        #hydrogens can have only 1 hbond
        if hAt is not None :
            hAt.hbonds = [newHB]
        return newHB

    



class RemoveHBondCommandGUICommand(MVCommand, MVBondICOM):
    """RemoveHBondCommandGUICommand allows user to remove hbonds between
    picked atoms.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : RemoveHBondCommandGUICommand
   \nCommand : removeHBondGC
   \nSynopsis:\n
        None <- removeHBondGC(atoms, **kw)
   \nRequired Arguments:\n
        atoms: atoms to remove hbonds between
    """
       

    def onAddCmdToViewer(self):
        if not self.vf.commands.has_key('setICOM'):
            self.vf.loadCommand('interactiveCommands', 'setICOM', 'Pmv',
                                topCommand=0) 
        hbond_geoms = check_hbond_geoms(self.vf.GUI)
        self.masterGeom = Geom('removeBondGeoms',shape=(0,0), 
                                pickable=0, protected=True)
        self.masterGeom.isScalable = 0
        self.vf.GUI.VIEWER.AddObject(self.masterGeom, parent=hbond_geoms)
        self.spheres = Spheres(name='removeBondSpheres', shape=(0,3),
                               inheritMaterial=0,
                               radii=0.2, quality=15,
                               materials = ((1.,0.,0.),), protected=True) 
        self.vf.GUI.VIEWER.AddObject(self.spheres, parent=self.masterGeom)


    def __init__(self, func = None):
        MVCommand.__init__(self, func)
        MVBondICOM.__init__(self)
        self.pickLevel = 'parts'
        self.undoBondList = []
        self.save = None


    def guiCallback(self):
        self.save = self.vf.ICmdCaller.commands.value["Shift_L"]
        self.vf.setICOM(self, modifier="Shift_L", topCommand = 0)
        self.vf.setIcomLevel(Atom, topCommand=0)

    def stop(self):
        self.done_cb()


    def getObjects(self, pick):
        for o, val in pick.hits.items(): ###loop over geometries
            primInd = map(lambda x: x[0], val)
            g = o.mol.geomContainer
            if g.geomPickToBonds.has_key(o.name):
                func = g.geomPickToBonds[o.name]
                if func: return func(o, primInd)
            else:
                l = []
                bonds = g.atoms[o.name].bonds[0]
                if not len(bonds): return BondSet()
                for i in range(len(primInd)):
                    l.append(bonds[int(primInd[i])])
                return BondSet(l)


    def dismiss(self):
        self.vf.setICOM(self.save, modifier="Shift_L", topCommand = 0)
        self.save = None
        self.done_cb()


    def done_cb(self):
        pass
        

    def __call__(self, atoms, **kw):
        """None <- removeHBondGC(atoms, **kw)
           atoms: atoms to remove hbonds between
           """
        if type(atoms) is StringType:
            self.nodeLogString = "'"+atoms+"'"
        ats = self.vf.expandNodes(atoms)
        if not len(ats): return 'ERROR'
        apply(self.doitWrapper, (ats,), kw)


    def doit(self, ats):
        #removeHBondGC
        hats = AtomSet(ats.get(lambda x: hasattr(x, 'hbonds')))
        if not len(hats): return 'ERROR'
        for at in hats:
            if not hasattr(at, 'hbonds'):
                continue
            if not len(at.hbonds):
                delattr(at, 'hbonds')
                continue
            for hbnd in at.hbonds:
                if at==hbnd.donAt:
                    if hbnd.accAt in hats:
                        self.vf.removeHBond(at, hbnd.accAt, topCommand=0)
                elif at==hbnd.accAt:
                    if hbnd.donAt in hats:
                        self.vf.removeHBond(hbnd.donAt, at, topCommand=0)
                    elif hbnd.hAt in hats:
                        self.vf.removeHBond(hbnd.hAt, at, topCommand=0)
                else:
                    #in this case at is hydrogen atom of hbond
                    if hbnd.accAt in hats:
                        self.vf.removeHBond(at, hbnd.accAt)
        #FIX THIS: update the geoms as above in addHBonds
        #to update display, need to call appropriate commands
        geomList = [self.vf.showHBonds.lines, self.vf.hbondsAsSpheres.spheres]
        cmdList = [self.vf.showHBonds, self.vf.hbondsAsSpheres]
        #FIX THIS: extrude is too slow
        #if len(self.vf.extrudeHBonds.geoms):
            #geomList.append(self.vf.extrudeHBonds.geoms[0])
            #cmdList.append(self.vf.extrudeHBonds)
        for i in range(len(geomList)):
            geom = geomList[i]
            cmd = cmdList[i]
            hats = AtomSet(self.vf.allAtoms.get(lambda x: hasattr(x,'hbonds')))
            if len(geom.vertexSet):
                #first call dismiss_cb???
                apply(cmd.dismiss_cb,(),{})
                d = {}
                d['topCommand']=0
                #update visible geometry here
                apply(cmd,(hats,),d)


##     def negateCmdAfter(self, ats, **kw):
##         return
##         lenUndoAts = len(self.undoAtList)
##         lenAts = len(ats)
##         if lenAts==1:
##             #after removing 1 self.atomList would be 1 or 0
##             if len(self.atomList)==1:
##                 s = '0'
##                 ustr='"c=self.removeHBondGC;c.atomList=c.atomList[:'+s+'];c.undoAtList=c.undoAtList[:-1];c.update()"'
##             else:
##                 ind = str(lenUndoAts - 2)
##                 ustr = '"c=self.removeHBondGC;nodes=self.expandNodes('+'\''+self.undoAtList[-2:].full_name()+'\''+'); self.addHBondGC(nodes, topCommand=0);c.atomList=nodes[:1];c.undoAtList=c.undoAtList[:'+ind+'];c.update()"'
##         elif lenUndoAts>lenAts:
##             ustr='"c=self.removeHBondGC;nodes=self.expandNodes('+'\''+ats.full_name()+'\''+');self.addHBondGC(nodes, topCommand=0);c.undoAtList=c.undoAtList[:'+str(lenAts)+']"'
##         else:
##             ustr='"c=self.removeHBondGC;nodes=self.expandNodes('+'\''+ats.full_name()+'\''+');self.addHBondGC(nodes, topCommand=0);c.undoAtList=c.undoAtList[:0]"'
##         if len(self.atomList) and lenAts>1:
##             atStr = self.atomList.full_name()
##             estr = ';nodes=self.expandNodes('+'\''+self.atomList.full_name()+'\''+');c.atomList=nodes;c.update()"'
##             ustr = ustr + estr
##         self.undoCmds = "exec("+ustr+")"


RemoveHBondGuiDescr = {'widgetType':'Menu',
                              'menuBarName':'menuRoot',
                              'menuButtonName':'Hydrogen Bonds',
                              'menuEntryLabel':'Remove Hydrogen Bond',
                              'menuCascadeName': 'Edit'}


RemoveHBondCommandGUICommandGUI=CommandGUI()
RemoveHBondCommandGUICommandGUI.addMenuCommand('menuRoot', 'Hydrogen Bonds', 
        'Remove Hydrogen Bonds', cascadeName='Edit')



class RemoveHBondCommand(MVCommand):
    """RemoveHBondCommand allows user to remove hydrogen bonds between specified
    atoms.
   \nPackage : Pmv
   \nModule  : hbondCommands
   \nClass   : RemoveHBondCommand
   \nCommand : removeHBond
   \nSynopsis:\n
        None<-removeHBond(atom1, accAt)
   \nRequired Arguments:\n        
        atom1 --- donAt or hAt of bond\n
        accAt --- accAt of bond 
    """


    def negateCmdBefore(self, atom1, atom2):

        return ([(self.vf.addHBond, (atom1.full_name(),atom2.full_name()),{'topCommand':0})], self.vf.addHBond.name)
        


    def __call__(self, atom1, accAt, **kw):
        """None<-removeHBond(atom1, accAt)
        \natom1  --- donAt or hAt of bond
        \naccAt --- accAt of bond"""
        ats = self.vf.expandNodes(atom1)
        if not len(ats): return 'ERROR'
        at1 = ats[0]
        ats = self.vf.expandNodes(accAt)
        if not len(ats): return 'ERROR'
        accAt = ats[0]
        return apply(self.doitWrapper, (at1, accAt), kw)


    def doit(self, atom1, accAt):
        b = None
        for hb in atom1.hbonds:
            if hb.accAt == accAt:
                #found the bond to remove
                b = hb
        #if no bond exists between these atoms, return error
        if b is None:
            return 'ERROR'

        #only remove one bond here
        accAt.hbonds.remove(b)
        if not len(accAt.hbonds):
            delattr(accAt,'hbonds')
        hAt = b.hAt
        if hAt is not None:
            donAt = b.donAt
            hAt.hbonds.remove(b)
            delattr(hAt,'hbonds')
            donAt.hbonds.remove(b)
            if not len(donAt.hbonds):
                delattr(donAt,'hbonds')
        else:
            #in this case, atom1 was the donAt
            atom1.hbonds.remove(b)
            if not len(atom1.hbonds):
                delattr(atom1,'hbonds')
        del b
        return



commandList=[
    {'name':'getHBDonors','cmd':GetHydrogenBondDonors(), 'gui':None},
    {'name':'getHBAcceptors','cmd':GetHydrogenBondAcceptors(), 'gui':None},
    {'name':'getHBondEnergies','cmd':GetHydrogenBondEnergies(), 'gui': None},
    {'name':'showHBDA','cmd':ShowHBDonorsAcceptors(), 
            'gui':ShowHBDonorsAcceptorsGUI},
    {'name':'showHBonds','cmd':ShowHydrogenBonds(), 'gui':ShowHydrogenBondsGUI},
    {'name':'buildHBondsGC','cmd':BuildHydrogenBondsGUICommand(),
            'gui': BuildHydrogenBondsGUICommandGUI},
    {'name':'buildHBonds','cmd':BuildHydrogenBonds(), 'gui': None},
    {'name':'addHBondHsGC','cmd':AddHBondHydrogensGUICommand(),
            'gui': AddHBondHydrogensGUICommandGUI},
    {'name':'addHBondHs','cmd':AddHBondHydrogens(), 'gui': None},
    {'name':'hbondsAsSpheres','cmd':DisplayHBondsAsSpheres(),
            'gui':DisplayHBondsAsSpheresGUI},
    {'name':'hbondsAsCylinders','cmd':DisplayHBondsAsCylinders(),
            'gui':DisplayHBondsAsCylindersGUI},
    {'name':'extrudeHBonds','cmd':ExtrudeHydrogenBonds(), 'gui':ExtrudeHydrogenBondsGUI},
    {'name':'addHBondGC','cmd':AddHBondCommandGUICommand(),
     'gui': AddHBondCommandGUICommandGUI},
    {'name':'addHBond','cmd':AddHBondCommand(), 'gui': None},
    {'name':'removeHBondGC','cmd':RemoveHBondCommandGUICommand(),
     'gui': RemoveHBondCommandGUICommandGUI},
    {'name':'removeHBond','cmd':RemoveHBondCommand(), 'gui': None},
    {'name':'limitHBonds','cmd':LimitHydrogenBonds(), 'gui':LimitHydrogenBondsGUI},
    {'name':'readIntermolHBonds','cmd':ReadIntermolHBonds(), 
        'gui':ReadIntermolHBondsGUI},
    {'name':'writeIntermolHBonds','cmd':WriteIntermolHBonds(), 
        'gui':WriteIntermolHBondsGUI},
    {'name':'writeHBondAssembly','cmd':WriteAssemblyHBonds(), 
        'gui':WriteAssemblyHBondsGUI},

    ]



def initModule(viewer):
    for dict in commandList:
        viewer.addCommand(dict['cmd'], dict['name'], dict['gui'])
