"""
     python/ui/model.py: CCP4MG Molecular Graphics Program
     Copyright (C) 2001-2008 University of York, CCLRC
     Copyright (C) 2009-2011 University of York
     Copyright (C) 2012 STFC

     This library is free software: you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public License
     version 3, modified in accordance with the provisions of the 
     license to address the requirements of UK law.
 
     You should have received a copy of the modified GNU Lesser General 
     Public License along with this library.  If not, copies may be 
     downloaded from http://www.ccp4.ac.uk/ccp4license.php
 
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU Lesser General Public License for more details.
"""
from point_funcs import *
from utils import *
import utils
import os
import string
import re

from mgapp import *
from mmdb2 import *
from mmut import *

from dataobj import *
from model_selection import *
from moleculargraphicsmodel import *
from resources import *
import build
from MolLabel import *
from EditHistory import *
import pygl_coord
import mmut
from global_definitions import *
import model_colour

import time
from PyQt4 import QtCore

#-----------------------------------------------------------------------
def openpdbfile (filename='',name='',drawing_style='bonds:all_atoms',customResCIFFiles={},  **keywords):
#-----------------------------------------------------------------------
  import re
  
  f = openFile(filename,'r')
  if not f: 
    MGGUI().WarningMessage("Error opening file " + filename)
    return [1,'Error opening file '+filename]
        
  l = f.read()
  f.close()
  if re.search('html',l) and (filename.lower().endswith('pdb') or filename.lower().endswith('pdb.gz')):
    print "found html"
    if not re.search('HEADER',l):
      return [1, 'Error reading file: '+filename + \
                   '\n the file appears to be an html file' ]
    else:
      rv = strip_html_from_PDB(filename=filename)
      if rv[0]:  return [1,rv[1]]

  m = MolData (filename=filename,name=name)
  m.setCustomResCIFFiles(customResCIFFiles)
  rv = m.loaddata(warnings=1)
  print "rv",rv
  if rv[0]==1 or rv[0]==MolData.NO_ATOMS_IN_CIF_FILE:
      m.delete()
      if rv[0]!=MolData.NO_ATOMS_IN_CIF_FILE:
        MGGUI().WarningMessage("Error opening file " + filename + '\n'+rv[1])
      return [rv[0],'Error loading file: '+filename+'\n'+rv[1]]

  return [0,m]
  


#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------
# The Model class
#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------

class MolData(DataObj,ModelAnalysis,EditHistory):

  NO_ATOMS_IN_CIF_FILE = -9999
  class_dependents = {}
  last_MolData_number = 0

  # save the 'last' molecule/model/chain colour
  colour_counter = {}
  colour_counter['bymol']=''


  restype_menu = ['amino acid','nucleic acid','saccharide','solvent','solute','monomer','metal']
  restype_alias =  ['amino_acid','nucleic_acid','saccharide','solvent','solute','monomer','metal']
  restype_code = [RESTYPE_PEPTIDE,RESTYPE_NUCL,RESTYPE_SACH,RESTYPE_SOLVENT,RESTYPE_SOLUTE,RESTYPE_NONPOLY,RESTYPE_METAL]

  secstr_code = ['N','S','B','3','4','5','H']
  secstr_code_dssp = ['N','E','B','T 3','T 4','T 5','H']
  secstr_enum_dssp = [SSE_None, SSE_Strand, SSE_Strand, SSE_3Turn,SSE_4Turn,SSE_5Turn,SSE_Helix ]
  secstr_menu = ['No structure','Beta strand','Beta bulge','3-turn', 
                 '4-turn','5-turn','Alpha helix']
  secstr_alias = ['none', 'strand', 'bulge', '3turn', 
                  '4turn',  '5turn', 'helix']
  secstr_enum = [SSE_None, SSE_Strand, SSE_Bulge,
          SSE_3Turn,SSE_4Turn,SSE_5Turn,SSE_Helix ]
  
#------------------------------------------------------------------
  def __init__(self,filename=[],name='',dataparams={},restore_from_version=0,**kw):
#------------------------------------------------------------------
    #print "model.MolData.__init__",name,filename,dataparams
    # Initialise data that will be over-written by restore

    self.customResCIFFiles = {}
    self.show_aromatic_rings = 1
    if hasattr(dataparams,"has_key") and dataparams.has_key("name"):
      name = dataparams["name"]
    DataObj.__init__(self,'MolData',name=name,module='model', \
                     filename=filename,dataparams=dataparams)
    ModelAnalysis.__init__(self)
    EditHistory.__init__(self)
    self.molHnd = ''
    self.spline_selHnd = 0
    self.worm_selHnd = 0
    self.splineinfo_ribbon = SplineInfo()
    self.splineinfo_worm = SplineInfo()
    # Selection handle to all atoms selected, visible and drawn
    # in a 'bonding' style
    self.do_update_all_bonded_atoms = 0  # Flag change in data object that requires total redraw
    self.changedSplines = []        # Change in visibiilty/selection of spline object
    self.changedConnected = []      # Change in visibiilty/selection of connected object (bonds/cylinders etc)
    self.all_bonded_atoms = 0
    self.all_bonded_atoms_spline = 0

    
    MolData.last_MolData_number =  MolData.last_MolData_number+1
    import copy
    self.MolData_number = copy.deepcopy(MolData.last_MolData_number)
    
    self.charge_loaded = 0  # ?Charge loaded?  1=derived from atom energy type
                            #                     (in MolDisp:apply_colour)
    self.moving = 0
    self.masterCrystal = ''
    self.treeModel = None

    #print 'MolData.init dataparams',dataparams
    self.saved_dataparams = dataparams
    import model_symmetry
    self.model_symmetry = model_symmetry.model_symmetry(self.name)
    if dataparams.has_key('model_symmetry'):
      self.model_symmetry.setParams(dataparams['model_symmetry'])
    
    #print MolData.colour_counter['bymol']
    #MolData.colour_counter['bymol'] = MGCOLOUR().next(MolData.colour_counter['bymol'],exclude=['black','white'])
    if not hasattr(self,"mol_colour") or not self.mol_colour:
      #self.mol_colour = MolData.colour_counter['bymol']
      self.mol_colour = ''
    #print 'MolData.mol_colour',self.mol_colour

      
#------------------------------------------------------------------
  def toggleAromaticity(self):
#------------------------------------------------------------------
    self.show_aromatic_rings = not self.show_aromatic_rings
    self.GetMolBonds()
    self.redraw_all_objects()
    import rebuild
    rebuild.UpdateDisplay()

#------------------------------------------------------------------
  def setCustomResCIFFiles(self,customResCIFFiles):
#------------------------------------------------------------------
    self.customResCIFFiles = customResCIFFiles

#------------------------------------------------------------------
  def deleteAll(self):
#------------------------------------------------------------------
       if self:
         # Are there any other objects that should be deleted
         # along with this one (eg Crystal gets deleted with map)
         dependents = self.getDependentsForDeletion()
         #print 'displayTable deleteDataObj dependents',dependents
         self.delete()
         if dependents:
           # In principle there could be a recursion here.
           for dep in dependents:
             dep_target = get_dataobj(name=dep)
             if dep_target:
               dep_target[0].delete()
             else:
               dep_target = get_dispobj(name=dep)
               if dep_target:
                 dep_target.delete()

#------------------------------------------------------------------
  def delete(self,delete='',**keywords):
#------------------------------------------------------------------
    # Save user data
    if self.moving: self.move()
    MODELINFO().save(set=self.name_label,filename=self.filename)
    #If this MolData object does not have a clone then close
    #the MolInfo data set
    duplicate = 0
    for obj in get_dataobj(object_type='MolData'):
      if obj != self and obj.filename == self.filename: duplicate=1
    if not duplicate:
      MODELINFO().close(set=self.name_label)

    DataObj.delete(self)
    molHnd = getattr(self,'molHnd',None)
    if molHnd:
      molHnd.DeleteAllSelections()
      molHnd.FreeFileMemory()
      if os.environ.has_key("CCP4MG_DEBUG_MEMORY"):
        print "del",molHnd
      molHnd.thisown = 0
      import _mmut
      _mmut.delete_CMMANManager(molHnd)
    del self

    
  def __del__(self):
      print "MolData.__del__ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

#------------------------------------------------------------------
  def getDependentsForDeletion(self):
#------------------------------------------------------------------
    # This method return any other objects that should be deleted along with
    # this one.  We need to delete the crystal object
    xtl_list = []
    xtl = CRYSTALMANAGER().getCrystal(self.name)
    if xtl: xtl_list.append(xtl)
    return xtl_list

#----------------------------------------------------------------------
  def get_label(self):
#----------------------------------------------------------------------
    if PM('atom_label_params').get('show_number_label'):
      return  str(self.MolData_number)+') '+self.name_label
    else:
      return self.name_label


#----------------------------------------------------------------------
  def applyDrawingStyle(self,drawing_style='',keep_display_objects=-1,chosen={},mgpic_file='',force_recolour_bymol=False):
#----------------------------------------------------------------------
    if not drawing_style and not mgpic_file:
      drawing_style = PICTUREWIZARD().default_drawing_style
    if drawing_style == 'all_atoms' and self.ca_only:
      drawing_style = 'bonds:trace_and_ligands'
          
    if keep_display_objects == 0:
      togo = []
      for obj in self.objinsts:
        if not obj.master_application: togo.append(obj)
      #print "MolData.applyDrawingStyle togo",togo
      
    dobj = { 'MolData1' : self.name }
    rv = PICTUREWIZARD().create_picture(drawing_style=drawing_style,chosen_dataobj = dobj ,chosen=chosen, keep_display_objects=keep_display_objects,mgpic_file=mgpic_file,force_recolour_bymol=force_recolour_bymol)
    #print "applyDrawingStyle",rv
    if rv[0]:
      if not  self.objinsts:
      # Picture wizard has failed and not current display objects so just draw one object
        self.add_object(object_type='MolDisp',visible=1)
    else:
      # Created new objects so delete old
      if keep_display_objects == 0:
        for obj in togo: obj.delete()
        del togo

    self.lastWizardRun = drawing_style
    return rv      

#----------------------------------------------------------------------
  def get_attribute ( self, attb ='',parent_selection='',qualifier='',PAtom=None,chains=[],force_recolour_bymol=False):
#-----------------------------------------------------------------------
    selindexp = pygl_coord.intp()
    #print "get_attribute",attb,qualifier
    # beware picture wizard scripts expect model_list to be ['0'] or a list of models
    if ['peptide_list','strand_list'].count(attb):
      import copy
      parent_selection = copy.deepcopy(qualifier)
      if attb == 'peptide_list':
        qualifier = 'peptide'
      elif attb == 'strand_list':
        qualifier = 'nucleic'
      attb = 'chain_list'
      
    if attb == 'model_list':

      mm = []
      if  hasattr(self,'nmr_models') and self.nmr_models:
        for mod in range(self.first_nmr_model,self.nmr_models+1): mm.append ('/'+str(mod))
      else:
        mm = ['/1']
      sym_models = self.model_symmetry.get_symmetry_models()
      # Add the symids
      for item in sym_models:
        mm.append(item+' '+self.model_symmetry.modelSymid(item[1:]))
      return mm
    
    if attb == 'model_number_list':

      mm = []
      if  hasattr(self,'nmr_models') and self.nmr_models:
        for mod in range(self.first_nmr_model,self.nmr_models+1): mm.append ('/'+str(mod))
      else:
        mm = ['/1']
      sym_models = self.model_symmetry.get_symmetry_models()
      # Add the symids
      for item in sym_models:
        mm.append(item)
      return mm

    elif attb == 'chain_list':
      mm = []
      if qualifier == 'peptide':
        source = self.peptides
      elif qualifier == 'nucleic':
        source =self.nucleics
      elif qualifier == 'solvent':
        source =self.solvent_chains
      elif qualifier == '':
        source = self.chains
      else:
        pass
      #pModel = self.molHnd.GetModel(1)
      for chain in source:
        #mm.append(CModelPtr(self.molHnd.GetChain(1,chain)))
        if chain:
          #mm.append (chain+'/')
          mm.append (chain)
        else:
          mm.append("// /")
      
      return mm

      
    elif attb == 'secstr_list':
      mask = inta(7)
      for i in range(0,7): mask[i] = 0
      mask[1]=1
      mask[6]=1
      output = self.molHnd.ListSecStructure(mask)
      #print "ListSecStructure output",output
      mm = string.split(output,'\n')
      if not mm[-1]: mm.pop()
      #print "mm",mm
      return mm

    
    elif attb == 'monomer_list':
      mm = []
      if len(self.monomers) > 0:
        if chains and len(chains)>0 and len(chains[0])>0:
          chainSel = self.molHnd.NewSelection()
          neighbSel = self.molHnd.NewSelection()
          self.molHnd.Select(chainSel,STYPE_ATOM,0,chains[0].rstrip('/').strip(),ANY_RES,"*",ANY_RES,"*","*","*","*","*",SKEY_NEW )
          selindexp = pygl_coord.intp()
          selAtoms = mmut.GetAtomSelIndex(self.molHnd,chainSel,selindexp)
          nSelAtoms = selindexp.value()
          self.molHnd.SelectNeighbours(neighbSel,STYPE_ATOM,selAtoms,nSelAtoms,0.0,4.0,SKEY_NEW)
          for pp in self.monomers:
            monSel = self.molHnd.NewSelection()
            self.molHnd.Select(monSel,STYPE_ATOM,pp,SKEY_NEW)
            self.molHnd.Select(monSel,STYPE_ATOM,neighbSel,SKEY_AND)
            selindexp = pygl_coord.intp()
            selAtoms = mmut.GetAtomSelIndex(self.molHnd,monSel,selindexp)
            nSelAtoms = selindexp.value()
            if nSelAtoms>0:
              print nSelAtoms,"in chain",chains[0].rstrip('/').strip(), " and monomer",pp
              mm.append(pp)
            self.molHnd.DeleteSelection(monSel)
          self.molHnd.DeleteSelection(chainSel)
          self.molHnd.DeleteSelection(neighbSel)
        else:
          for pp in self.monomers:
            mm.append (pp)
      return mm
    
    elif attb == 'res_list':
      import copy
      res_list = []
      qualifer_selHnd = 0
      if ["' '",' ','UNNAMED'].count(parent_selection):
        parent_selection = '/1//'
      else:
        parent_selection = re.sub('/0','/1',parent_selection)

      if qualifier == 'monomers':
        mm = []
        for pp in self.monomers: mm.append (pp)
        return mm
    
      elif qualifier == '':
        sel = copy.deepcopy(parent_selection)
      elif qualifier == 'peptide':
        sel = parent_selection+'('+MODELINFO().get_names_string(key='amino_acid',set=self.name_label)+')'
        
      elif qualifier == 'nucleic':
        sel = parent_selection+'('+MODELINFO().get_names_string(key='nucleic_acid',set=self.name_label)+')'

      elif qualifier == 'solvent':
         sel = copy.deepcopy(parent_selection)

      else:
        self.parse_selection(qualifier)
        if not rv[0]: qualifer_selHnd = rv[1]


      #print "res_list qualifier,sel",qualifier,sel
      selHnd = self.molHnd.NewSelection()
      self.molHnd.Select(selHnd,STYPE_RESIDUE,sel,SKEY_NEW)

      if qualifer_selHnd:
        self.molHnd.Select(selHnd,STYPE_RESIDUE,qualifer_selHnd,SKEY_AND)
        self.molHnd.DeleteSelection(qualifier_selHnd)

      for ch in chains:
        chselHnd = self.molHnd.NewSelection()
        self.molHnd.Select(chselHnd,STYPE_RESIDUE,0,ch,ANY_RES,"*",ANY_RES,"*","*","*","*","*",SKEY_NEW )
        self.molHnd.Select(selHnd,STYPE_RESIDUE,chselHnd,SKEY_AND)
        self.molHnd.DeleteSelection(chselHnd)

        
      selindexp = pygl_coord.intp()
      selResidues = newPPCResidue()
      #nselRes =self.molHnd.GetSelIndex(selHnd,selResidues)
      selResidues = mmut.GetResidueSelIndex(self.molHnd,selHnd,selindexp)
      nselRes = selindexp.value()
      
      if nselRes > 0:
        for ir in range(0,nselRes):
          pcres = getPCResidue(selResidues,ir)
          #chnid = str(pcres.GetChainID())
          #if not chnid or chnid == ' ':  chnid = '// '
          #resname = chnid + '/' + str(pcres.GetSeqNum()) 
          #inscode = pcres.GetInsCode()
          #if inscode: resname = resname  + '.' + inscode
          #resname = resname +  "(" + pcres.GetResName() + ')' 
          #res_list.append(resname)
      self.molHnd.DeleteSelection(selHnd)
      #delPPCResidue( selResidues)
      return res_list
    
    elif ['atom_list','first_atom'].count(attb):
      atom_list = []
      #print "atom_list qualifier",qualifier
      selHnd = self.molHnd.NewSelection()
      #If the selection is over all models then limit to model 1
      parent_selection = re.sub('/0','/1',parent_selection)
      self.molHnd.Select(selHnd,STYPE_ATOM,parent_selection,SKEY_NEW)
      selAtoms = newPPCAtom()        
      #nselAtom =self.molHnd.GetSelIndex(selHnd,selAtoms)
      selAtoms = mmut.GetAtomSelIndex(self.molHnd,selHnd,selindexp)
      nselAtom = selindexp.value()
      if attb == 'first_atom': nselAtom = min(nselAtom,1)
      if nselAtom > 0:
        pcatm = getPCAtom(selAtoms,0)
        chnid = str(pcatm.GetChainID())
        if not chnid or chnid == ' ':  chnid = '// '
        resname = chnid + '/' + str(pcatm.GetSeqNum()) 
        inscode = pcatm.GetInsCode()
        if inscode: resname = resname  + '.' + inscode
        resname = resname  + "(" + pcatm.GetResName() + ')' 
        for ia in range(0,nselAtom):
          pcatm = getPCAtom(selAtoms,ia)
          name = string.strip(str( pcatm.name))
          alt = pcatm.GetAltLoc()
          if alt: name = name + ':' + alt
          atom_list.append(resname+'/'+name)
          
      self.molHnd.DeleteSelection(selHnd)
      #print "get_attribute atom_list",atom_list
      return atom_list

    elif attb == 'selobj_list':
      retval,user_selection= MODELINFO().get_info(
               set=[self.name_label],info='user_selection')
      selobj_labels = user_selection.keys()
      i = 0
      for obj in self.get_dispobj(object_type='MolDisp'):
        i = i+1
        selobj_labels.append(str(i)+':'+obj.selection_label)
      selobj_list = user_selection.keys()
      selobj_list.extend(self.objnames)
      ret = []
      ret.append(selobj_list)
      ret.append(selobj_labels)
      #print "ret",ret
      return ret

    elif attb == 'molecule_colour':
      #print force_recolour_bymol
      if not self.mol_colour or force_recolour_bymol:
        #print "Need to get a colour"
        MolData.colour_counter['bymol'] = MGCOLOUR().next('',exclude=['black','white'])
        self.mol_colour =  MolData.colour_counter['bymol']
        dobjs = get_dataobj(object_type='MolData')

        """
        for dobj in dobjs:
          print dobj.name, self.name
          if force_recolour_bymol and dobj.name == self.name:
            break # We are hoping that force_recolour_bymol is being used on ALL objects. Otherwise this is hopeless.
          if dobj.name != self.name and dobj.mol_colour == self.mol_colour:
            print "I am",self.name,". ",dobj.name," is already using",self.mol_colour
            MolData.colour_counter['bymol'] = MGCOLOUR().next(dobj.mol_colour,exclude=['black','white'])
            self.mol_colour =  MolData.colour_counter['bymol']
            print "Now try",self.mol_colour
        """

        i = 0
        inUse = True
        while inUse and i<20:
          inUse = False
          for dobj in dobjs:
            if force_recolour_bymol and dobj.name == self.name:
              break # We are hoping that force_recolour_bymol is being used on ALL objects. Otherwise this is hopeless.
            if dobj.name != self.name and dobj.mol_colour == self.mol_colour:
               #print "I am",self.name,". ",dobj.name," is already using",self.mol_colour
               inUse = True
               MolData.colour_counter['bymol'] = MGCOLOUR().next(dobj.mol_colour,exclude=['black','white'])
               self.mol_colour =  MolData.colour_counter['bymol']
               #print "Now try",self.mol_colour
               i = i + 1
               break

        #print
      return self.mol_colour

    elif attb == 'ca_only':
      return self.ca_only


    elif attb == 'element_list':
      status,protocols = MODELINFO().get_info( 
        set=self.name_label,info='selection_protocols',key='element')
      if not status and protocols.has_key('element') and protocols['element'].has_key('ORDER'):
        return protocols['element']['ORDER']
      else:
        return []

    elif attb == 'energy_type':
      if PAtom:
        mask = pygl_coord.inta(20)
        for i in range(20): mask[i] = 0
        mask[10] = 1
        text_label = self.molHnd.AtomLabel(PAtom,mask)
        #print 'MolData.get_attribute',text_label
        return text_label
        
    return ''
  
#------------------------------------------------------------------
  def dataparams(self,all=1):
#------------------------------------------------------------------
    # save the key data which is necessary when restoring the
    # object on restart or 'restore status'
    datapar = DataObj.dataparams(self)

    sym_mates = self.model_symmetry.getSymmetryMates()    
    if sym_mates: datapar['symmetry_mates']=sym_mates

    datapar['model_symmetry']=self.model_symmetry.getParams()

    if self.molHnd.GetIsTransformed():
      tmat = self.molHnd.GetTransform()
      datapar['transform_matrix']=tmat

    if hasattr(self,'animation') and self.animation:
      datapar['load_animation'] = self.animation.params()
      
    if hasattr(self,'DSSP_file'):
      datapar['DSSP_file'] = self.DSSP_file

    if hasattr(self,'mol_colour'):
      datapar['mol_colour'] = self.mol_colour

    if hasattr(self,'lastWizardRun'):
      datapar['lastWizardRun'] = self.lastWizardRun

    if hasattr(self,'show_aromatic_rings'):
      datapar['show_aromatic_rings'] = self.show_aromatic_rings

    return datapar

#--------------------------------------------------------------------------
  def  update_data_status(self,shadowdisp):
#--------------------------------------------------------------------------
    import utils
    if isinstance(shadowdisp,MolData):
      other_datapars = shadowdisp.dataparams()
    else:
      other_datapars = shadowdisp.dataparams
    rv,diffs,undiffs = utils.dict_diffs(self.dataparams(),other_datapars)
    #print "MolData diffs",diffs
    #print "MolData undiffs",undiffs
    if diffs.has_key('load_animation'):
      import Animation
      if hasattr(self,'animation'):
        if not other_datapars.has_key('load_animation') or \
               not other_datapars['load_animation']:
           self.animation.delete()
        elif self.animation.filename == other_datapars['load_animation']['filename']:
          self.animation.update_data_status(other_datapars['load_animation'])
        else:
          self.animation.delete()
          self.animation = Animation.Animation(parent=self, \
             params=other_datapars['load_animation'])
          
      elif other_datapars.has_key('load_animation') and \
               other_datapars['load_animation']:
        self.animation = Animation.Animation(parent=self, \
             params=other_datapars['load_animation'])
    elif undiffs.has_key('load_animation'):
      if hasattr(self,'animation'):
        self.animation.delete()
    if diffs.has_key('symmetry_mates'):
      self.model_symmetry.update_data_status(other_datapars['symmetry_mates'])
    elif undiffs.has_key('symmetry_mates'):
      self.model_symmetry.update_data_status( [] )
    

    if diffs.has_key('transform_matrix'):
      vtmat = []
      vtran = []
      ii = 0
      for i in range(0,3):
        for j in range(0,3):
          vtmat.append(diffs['transform_matrix'][ii])
          ii = ii + 1
        vtran.append(diffs['transform_matrix'][ii])
        ii = ii + 1
      self.molHnd.SetTransform(vtmat,vtran,True)
      self.update_dependents('transform')
    elif undiffs.has_key('transform_matrix'):
      self.molHnd.UnSetTransform(True)
      self.update_dependents('transform')

    rv = DataObj.update_data_status(self,shadowdisp)
    if diffs or undiffs:
      self.update_gui.append(['update'])
      rv = 1

    return rv
      
#-----------------------------------------------------------------------
  def update_matrix(self,input_matrix):
#-----------------------------------------------------------------------
    
      vtmat = []
      vtran = []
      ii = 0
      for i in range(0,3):
        for j in range(0,3):
          vtmat.append(input_matrix[ii])
          ii = ii + 1
        vtran.append(input_matrix[ii])
        ii = ii + 1
      self.molHnd.SetTransform(vtmat,vtran,True)
      self.update_dependents('transform')

#-----------------------------------------------------------------------
  def update_all_bonded_atoms(self):
#-----------------------------------------------------------------------
    '''
    Update the selection handle which flags all selected atoms in all
    visible bonded MolDisp objects. Unset for only one visible MolDisp
    '''
    
    # Note: excluding objects with master_application set (ie those
    # owned by an application)  This gets round problems with coming
    # out of interactive selection with unwanted bonds drawn.  But it
    # may not be a good long term solution
    if (not self.changedSplines) and (not self.changedConnected) and (not self.do_update_all_bonded_atoms) : return
    #print 'update_all_bonded_atoms',self.changedSplines,self.changedConnected
    #start_time = time.clock()
    changed_externally_bonded = 0
    if self.changedSplines or self.do_update_all_bonded_atoms:
      changed_externally_bonded = 1
    else:
      for objname in self.changedConnected:
        obj = self.get_dispobj(name=objname)
        if obj: changed_externally_bonded = obj.Connectivity.GetNumberOfExternalBonds()

    # Delete old selection handle for all splined atoms
    if  hasattr(self,'worm_selHnd') and self.worm_selHnd>= 0:
      self.molHnd.DeleteSelection(self.worm_selHnd)
    self.worm_selHnd = -1
    if  hasattr(self,'spline_selHnd') and self.spline_selHnd>= 0:
      self.molHnd.DeleteSelection(self.spline_selHnd)
    self.spline_selHnd = -1
      
    # How many visible bonded objects?
    nvis = 0
    spline_objects = []  # list of visible objects in spline style
    worm_objects = []  # list of visible objects in worm style
    for obj in self.get_dispobj(object_type='MolDisp'):
      if obj.visible and not obj.master_application and getattr(obj,'SelHandle',None):
        if not MolDisp.no_connectivity.count(obj.style): nvis = nvis + 1
        if MolDisp.spline_styles.count(obj.style) or obj.style.startswith(MolDisp.custom_spline_style_prefix): 
          if obj.style == "WORM" or obj.style.startswith(MolDisp.custom_spline_style_prefix):
            worm_objects.append(obj)
          else:
            spline_objects.append(obj)

    #print "worm_objects",worm_objects
    #print "spline_objects",spline_objects
    #print "nvis",nvis

    if ( nvis>1 ):
      if spline_objects:
      # Update the self.spline_selHnd for all splined atoms
        self.spline_selHnd = self.molHnd.NewSelection()
        for spline_obj in spline_objects:
          #print "spline_obj",spline_obj.name
          self.molHnd.Select(self.spline_selHnd,STYPE_ATOM,spline_obj.SelHandle.getSelHnd(),SKEY_OR)
        #self.molHnd.Select(self.spline_selHnd,STYPE_ATOM,"/*/*/*.*/CA:*",SKEY_AND)
      if worm_objects:
        self.worm_selHnd = self.molHnd.NewSelection()
        for spline_obj in worm_objects:
          #print "spline_obj",spline_obj.name
          self.molHnd.Select(self.worm_selHnd,STYPE_ATOM,spline_obj.SelHandle.getSelHnd(),SKEY_OR)
        #self.molHnd.Select(self.worm_selHnd,STYPE_ATOM,"/*/*/*.*/CA:*",SKEY_AND)

          
    if not self.all_bonded_atoms:
      self.all_bonded_atoms = self.molHnd.NewSelection()
    if not self.all_bonded_atoms_spline:
      self.all_bonded_atoms_spline = self.molHnd.NewSelection()
      
    mode = SKEY_NEW
    for obj in self.get_dispobj(object_type='MolDisp'):
      if obj.visible and not obj.master_application and getattr(obj,'SelHandle',None):
        if MolDisp.connected_styles.count(obj.style) :
            #print "add to all_bonded_atoms",obj.SelHandle.getLabel()
            self.molHnd.Select(self.all_bonded_atoms,STYPE_ATOM, \
                           obj.SelHandle.getSelHnd(),mode)          
            mode = SKEY_OR
            #print 'ExternalBonds',obj.name,obj.Connectivity.GetNumberOfExternalBonds()
            if changed_externally_bonded and obj.Connectivity.GetNumberOfExternalBonds()!=0:
              obj.do_redraw_external = 1
              obj.do_update_connectivity = 1


    if self.worm_selHnd>0 or self.spline_selHnd>0:
      tmp_selHnd = self.molHnd.NewSelection()
      if self.worm_selHnd>0:
        self.molHnd.Select(tmp_selHnd,STYPE_ATOM,self.worm_selHnd,SKEY_NEW)
        self.molHnd.Select(tmp_selHnd,STYPE_ATOM,"/*/*/*.*/CA:*",SKEY_AND)
        self.molHnd.Select(self.all_bonded_atoms_spline,STYPE_ATOM,tmp_selHnd,SKEY_OR)
        self.splineinfo_worm = GetRibbonOrWormSplineInfo(self.molHnd,self.worm_selHnd,False)
      if self.spline_selHnd>0:
        self.molHnd.Select(tmp_selHnd,STYPE_ATOM,self.spline_selHnd,SKEY_NEW)
        self.molHnd.Select(tmp_selHnd,STYPE_ATOM,"/*/*/*.*/CA:*",SKEY_AND)
        self.molHnd.Select(self.all_bonded_atoms_spline,STYPE_ATOM,tmp_selHnd,SKEY_OR)
        self.splineinfo_ribbon = GetRibbonOrWormSplineInfo(self.molHnd,self.spline_selHnd,True)
      self.molHnd.DeleteSelection(tmp_selHnd)

            
    self.changedSplines = []
    self.changedConnected = []
    self.do_update_all_bonded_atoms = 0
    #print 'done update_all_bonded_atoms',time.clock()-start_time

#---------------------------------------------------------------------
  def update_dependents(self,attribute=''):
#---------------------------------------------------------------------
    '''
    Intercept Dependency.update_dependents in order to prompt
    all display objects to handle moving
    '''
    #print "MolData update_dependents",attribute
    if ['reconfigure','edit'].count(attribute):
      self.apply_edit_secstr()
      # Do we need to update SAS
      for obj in self.objinsts:
        if obj.object_type == 'MolDisp' and \
            ['atom_sas','res_sas','atom_buried','res_buried'].count(obj.colour):
          self.update_sasarea(reapply=1)
          break
    
    if ['transform','reconfigure','edit'].count(attribute):
      for obj in self.objinsts:
        obj.handle_moving_data(attribute=attribute,master=self)
        if obj.object_type == 'MolDisp':   
          obj.label.handle_moving_data(attribute=attribute,master=self)
          obj.set_extent()
          obj.do_redraw = 1
        #reset the centre/extent
    Dependency.update_dependents(self,attribute)

#----------------------------------------------------------------------
  def initialise_MoleculeSelection ( self, CID='',pAtom=None, \
                                     if_monomer=0,if_atom=1):
#----------------------------------------------------------------------
    import utils
    #print "initialise_MoleculeSelection CID",CID
    # Set up the configure statement for a MoleculeSelection widget
    # assuming it should be initiallised to CID
    if pAtom:
      CID =  self.molHnd.AtomLabel_atom1(pAtom)
      #CID = pAtom.GetAtomID()[0]
      #print "initialise_MoleculeSelection CID",CID
    if CID:
      id = self.splitAtomID(CID)
      #print "id",id
      if id:
        # Beware the input id might have a null chain identifier
        if id[1]:
          chain = id[1]
        elif chain_list.count(' '):
          chain = ' '
        #print "chain",chain,"list count",chain_list.count(' ')
        if if_monomer and self.get_attribute('monomer_list').count(CID):
          monomer = CID
          #print "monomer",monomer
          if if_atom:
            atom_list =  self.get_attribute('atom_list',monomer)
            atom = id[3]
        elif chain_list.count(chain):
          #print "got chain"
          res_list = self.get_attribute('res_list',chain)
          if res_list.count(id[2]):
            res = id[2]
            #print "res",res
            if if_atom:
              atom_list =  self.get_attribute('atom_list',res)
              atom = id[3]

    config = ['CONFIGURE',
            ['mol',self.name],
            ['selection',utils.tclSafe(CID)],
            ['chain',chain],
            ['res',res]]
    if if_atom:  config.append( ['atom',atom] )
    if if_monomer: config.append( ['monomer',monomer])
    return config
        
#------------------------------------------------------------------
  def loaddata(self,recentre=1,warnings=1,find_bonds=1,**keywords):
#------------------------------------------------------------------
    '''
    Load data from PDB file and analyse structure
    '''

    errors = []
    self.load_warnings = 'Loading file: '+self.filename[2]+'\n'
    self.molHnd = CMMANManager(RESOURCES().SRS,RESOURCES().CMolBondParams)

    nChains1 = 0
    nChains2 = 0
    test1 = Manager()
    test1.SetFlag(MMDBF_AutoSerials)
    test1.SetFlag(MMDBF_IgnoreRemarks)
    test1.SetFlag(MMDBF_IgnoreBlankLines)
    test1.SetFlag(MMDBF_IgnoreHash)
    test1.SetFlag(MMDBF_EnforceUniqueChainID)
    RC = utils.ReadCoorFile(test1,self.filename[2])
    if RC == 0:
      nChains1 = test1.GetNumberOfChains(1)
    test2 = Manager()
    test2.SetFlag(MMDBF_AutoSerials)
    test2.SetFlag(MMDBF_IgnoreRemarks)
    test2.SetFlag(MMDBF_IgnoreBlankLines)
    test2.SetFlag(MMDBF_IgnoreHash)
    RC = utils.ReadCoorFile(test2,self.filename[2])
    if RC == 0:
      nChains2 = test2.GetNumberOfChains(1)

    if(nChains1!=nChains2):
       self.molHnd.duplicateChainIDS = True
    else:
       self.molHnd.duplicateChainIDS = False

    self.molHnd.SetFlag(MMDBF_AutoSerials)
    #self.molHnd.SetFlag(MMDBF_IgnoreRemarks)
    self.molHnd.SetFlag(MMDBF_IgnoreBlankLines)
    self.molHnd.SetFlag(MMDBF_IgnoreHash)
    #self.molHnd.SetFlag(MMDBF_EnforceUniqueChainID)
    #self.molHnd.SetFlag(MMDBF_AllowDuplChainID)
    #print "loaddata filename",self.filename[2]
    #RC1 = self.molHnd.ReadCoorFile(self.filename[2])
    RC1 = utils.ReadCoorFile(self.molHnd,self.filename[2])
    if RC1 > 0:
      errors.append(RC1)
      n_err_line = intp()
      err_line = self.molHnd.GetInputBuffer (n_err_line )
      self.load_errors = GetErrorDescriptionChar ( RC1 ) + \
          '\nAt line number' +  str(n_err_line.value())+':\n'+err_line + \
          '\n ***This may lead to problems with atom selection***'
      self.load_warnings = self.load_warnings + \
      '\n*****Error loading coordinate file******\n' + \
       self.load_errors + \
      '\n****************************************\n'
      self.molHnd.SetFlag(MMDBF_IgnoreNonCoorPDBErrors)
      self.molHnd.SetFlag(MMDBF_IgnoreDuplSeqNum)
      self.molHnd.SetFlag(MMDBF_IgnoreSegID)
      self.molHnd.SetFlag(MMDBF_IgnoreElement)
      self.molHnd.SetFlag(MMDBF_IgnoreCharge)
      self.molHnd.SetFlag(MMDBF_IgnoreUnmatch)
      #self.molHnd.SetFlag(MMDBF_IgnoreHash)
      #self.molHnd.SetFlag(MMDBF_IgnoreRemarks)
      #self.molHnd.SetFlag(MMDBF_IgnoreBlankLines)
      #RC2 = self.molHnd.ReadCoorFile(self.filename[2])
      RC2 = utils.ReadCoorFile(self.molHnd,self.filename[2])
      '''
      syminfo = os.path.join(os.environ['CLIBD'],'syminfo.lib')
      if os.path.exists(syminfo):
        print 'Loading syminfo'
        self.molHnd.SetSyminfoLib(syminfo)
      '''
      if RC2 > 0:
        self.load_warnings = self.load_warnings+ '\nFAILED to load file'
        return [1,self.load_warnings]        
    else:
      nat = self.molHnd.GetNumberOfAtoms()
      if nat <= 0:
        self.load_warnings = self.load_warnings + '\nNo atoms in the file'
        if self.filename[2].lower().endswith('.cif') or self.filename[2].lower().endswith('.cif.gz'):
          return [self.NO_ATOMS_IN_CIF_FILE,self.load_warnings]        
        errors.append(201)

    #self.molHnd.PDBCleanup ( PDBCLEAN_SERIAL )

    udd_duplchids = self.molHnd.GetUDDHandle ( UDR_HIERARCHY,"duplicate_chain_ids" );
    if udd_duplchids<=0:
       udd_duplchids = self.molHnd.RegisterUDInteger(UDR_HIERARCHY,"duplicate_chain_ids" );
    if(nChains1!=nChains2):
       self.molHnd.PutUDData(udd_duplchids,1)
    else:
       self.molHnd.PutUDData(udd_duplchids,0)
    
    #Cleanup the element names
    # restore from .ccp4mg - or create new internal data structure
    MODELINFO().restore(set=self.name_label,filename=self.filename,
                            override=0,create=1)
    
    # Apply residue typing
    self.updateCustomResTypes()
    self.updateCustomLinks()

    if find_bonds:
      #print "Setting customResCIFFiles",self.customResCIFFiles
      self.molHnd.setCustomResCIFFiles(mmut.map_string_string(self.customResCIFFiles))
      output = self.GetMolBonds()
      if output: self.load_warnings = self.load_warnings + \
       '\nMatching residues to library\n\n' + output

    if not self.molHnd.isSpaceGroup():
      self.molHnd.SetSpaceGroup("P1")
      self.load_warnings = self.load_warnings + '\nNo space group in file - setting to P1'
    else:
      spgp = self.molHnd.GetSpaceGroup()
      if ['R3','R 3'].count(spgp):
        cell_info = self.molHnd.GetCellInfo()
        if abs(cell_info[3]-90)<1e-6 and abs(cell_info[4]-90)<1e-6 and abs(cell_info[5]-120)<1e-6:
          self.molHnd.SetSpaceGroup('H 3')
          self.load_warnings = self.load_warnings + '\n***Space group R 3 reset to H 3***'

    rv = self.model_symmetry.check_symmetry(self.filename[2])
    if rv[0]: self.load_warnings = self.load_warnings + '\n'+rv[1]
    if len(rv)>2: errors.append(rv[2])

    if not hasattr(self,'originalSymmetry'):
      self.originalSymmetry = self.getMolHndCell()

    #self.model_symmetry.load_biomolecule()
    
    status,data= MODELINFO().get_info(set=self.name_label, \
               info='selection_protocols')

    
    #analyse content of molecule
    self.analyse()
    
    # Calculate SS for all models
    PM('SecStr_param').add_dependent(self.apply_edit_secstr)
    self.update_ModelInfo_secstr()
    rv = self.apply_edit_secstr(apply=1)
    if rv[0]:  self.load_warnings = self.load_warnings + '\n' + rv[1]

    self.CSASArea = None
    # Recentre if this is the only loaded model
    if recentre and len(get_dataobj(object_type='MolData'))==1: VW('mainview').recentre = 1

    # If we has restored a transform matrix then
    # apply it now
    if hasattr(self,'transform_matrix'):
      vtmat = []
      vtran = []
      ii = 0
      for i in range(0,3):
        for j in range(0,3):
          vtmat.append(self.transform_matrix[ii])
          ii = ii + 1
        vtran.append(self.transform_matrix[ii])
        ii = ii + 1
      self.molHnd.SetTransform(vtmat,vtran,False)
      del self.transform_matrix

    if hasattr(self,'load_animation') and self.load_animation:
      #print "init MolData load_animation",self.load_animation
      import Animation
      #for item in self.load_animation:
      self.animation = Animation.Animation(parent=self, \
             params=self.load_animation)
      del self.load_animation

    self.loadTreeModel()

    self.update_dependents('create')
    #List warnings if we are not restoring on restart


    if self.saved_dataparams.has_key('model_symmetry'):
      self.model_symmetry.setParams(self.saved_dataparams['model_symmetry'])
    if self.saved_dataparams.has_key('symmetry_mates'):
      self.model_symmetry.setSymmetryMates(self.saved_dataparams['symmetry_mates'])

    
    if self.load_warnings:
      return [2,self.load_warnings,errors]

    else:
      return [0,""]

#---------------------------------------------------------------------
  def GetMolBonds(self):
#---------------------------------------------------------------------
    
    output = self.molHnd.GetMolBonds(bool(self.show_aromatic_rings))
    #print 'GetMolBonds',output
    
    self.apply_edit_bonds()

    return output
  

#---------------------------------------------------------------------
  def loadTreeModel(self):
#---------------------------------------------------------------------
    
    import mgTreeView
    self.treeModel =  mgTreeView.AbstractMolData(self.molHnd,self)


#---------------------------------------------------------------------
  def getMolHndCell(self):
#---------------------------------------------------------------------
    '''
    Return list of reciprocal cell length, angles, volume and orthog code
    '''
    import math
    import mmdb2 as mmdb
    import pygl_coord
    status = 0
    sg = self.molHnd.GetSpaceGroup()
    if not sg: return dict(status=1)
    xptr=pygl_coord.doublep()
    yptr=pygl_coord.doublep()
    zptr=pygl_coord.doublep()
    aptr=pygl_coord.doublep()
    bptr=pygl_coord.doublep()
    gptr=pygl_coord.doublep()
    volptr=pygl_coord.doublep()
    codeptr=pygl_coord.intp()
    rv = self.molHnd.GetCell(xptr,yptr,zptr,aptr,bptr,gptr,volptr,codeptr)
    # NB GetCell returns 1=OK
    if not rv: return dict(status=1)

    xp = xptr.value()
    yp = yptr.value()
    zp = zptr.value()
    ap = aptr.value() * math.pi / 180.0
    bp = bptr.value() * math.pi / 180.0
    gp = gptr.value() * math.pi / 180.0
    volp =  volptr.value()
    codep = codeptr.value()
    

    a = ap * 180.0 / math.pi
    b = bp * 180.0 / math.pi
    g = gp * 180.0 / math.pi
    
    return dict(status=0,cell=[ xp,yp,zp,a,b,g],sg=sg,volume=volp,code=codep)

#---------------------------------------------------------------------
  def setMolHndSymmetry(self,cell=[],sg=''):
#---------------------------------------------------------------------
    # Save the symmetry in the PDB. The PBD symmetry may be changed if this data
    # object becomes part of a crystal
    if not hasattr(self,'originalSymmetry'):
      self.originalSymmetry = self.getMolHndCell()

    if cell: self.molHnd.SetCell( \
        cell[0],cell[1],cell[2],cell[3],cell[4],cell[5],0)
    if sg: self.molHnd.SetSpaceGroup(sg)

#---------------------------------------------------------------------
  def setMasterCrystal(self,crystal='',force=0):
#---------------------------------------------------------------------
    if crystal != self.masterCrystal or force:
      self.masterCrystal = crystal
      if crystal:
        xtl = get_dispobj(name = crystal)
        self.setMolHndSymmetry(xtl.cell,xtl.sg)
      else:
        #print "setMasterCrystal reverting to",self.originalSymmetry
        if not self.originalSymmetry.get('status',1):
          self.setMolHndSymmetry(self.originalSymmetry.get('cell'),self.originalSymmetry.get('sg'))
      
      for obj in self.objinsts:
        obj.setMasterCrystal(crystal)




#--------------------------------------------------------------------------
  def getSASAreaPM(self):
#--------------------------------------------------------------------------
    if hasattr(self,'SASAreaPM'):
      return self.SASAreaPM
    else:
      return PM('SASArea_param')
        
#--------------------------------------------------------------------------
  def update_sasarea(self,reapply=1,**keywords):
#--------------------------------------------------------------------------
    if not self.CSASArea:
      selHnd = -1
      sele=self.getSASAreaPM().get('limit_selection')
      #print "update_sasarea sele",sele
      if sele != 'all':
        rv = self.parse_selection(command=sele)
        #print "update_sasarea parse_selection",rv
        if rv[0] == 0 and rv[1] > 0:  selHnd = rv[1]
      self.CSASArea = self.molHnd.GetSASArea(selHnd)
      self.getSASAreaPM().add_dependent(self.update_sasarea)
      reapply=1

      #print "update_sasarea selHnd",selHnd

    if reapply and self.CSASArea:
      self.getSASAreaPM().update( self.CSASArea.SetParams )
      #MGGUI().StartInterrupt(gui='Stop SAS calculation')
      #print "update_sasarea",self.nmr_models
      if self.nmr_models:
        rv = self.CSASArea.Calculate(0,True)
      else:
        rv = self.CSASArea.Calculate(0,False)
      #MGGUI().EndInterrupt(gui='Stop SAS calculation')

      if rv:
        del self.CSASArea
        self.CSASArea = None
      else:
        for obj in self.get_dispobj(object_type='MolDisp'):
          if ['res_sas','atom_sas','res_buried','atom_buried'].count(obj.colour):
            obj.do_recolour = 2

      return rv    
      

#-------------------------------------------------------------------------
  def clone(self,**keywords):
#-------------------------------------------------------------------------
    if not os.path.exists(self.filename[2]) \
          or not os.path.isfile(self.filename[2]):
      MGGUI().WarningMessage('File no longer exists '+self.filename[2])
      return None
    
    if keywords and hasattr(keywords,"has_key") and keywords.has_key("name"):
      m = MolData (filename=self.filename,dataparams=self.dataparams(),name=keywords["name"])
    else:
      m = MolData (filename=self.filename,dataparams=self.dataparams())
    rv = m.loaddata()
    if rv[0]==1:
      MGGUI().WarningMessage("ERROR loading data from file "+self.filename[2])
      m.delete()
      return None
    return m
  

    
#-----------------------------------------------------------------
  def add_object(self,object_type='MolDisp',visible=1,name='',animation='',params={},selparams={},colparams={},styleparams={},**keywords):
#-----------------------------------------------------------------
    dobj = None
    if object_type == 'MolDisp':
      dobj = MolDisp(self.name,visible=visible,params=params,selparams=selparams,colparams=colparams,styleparams=styleparams)
      
    if object_type == 'Annotation':
      import Annotation
      dobj = Annotation.Annotation(self.name,visible=1,params=params)
    elif object_type == 'HBonds':
      import HBonds
      dobj = HBonds.HBonds(self.name,params=params,selparams=selparams,colparams=colparams,styleparams=styleparams)
    elif object_type == 'Contacts':
      import Contacts
      dobj = Contacts.Contacts(self.name,params=params,selparams=selparams,colparams=colparams,styleparams=styleparams)
    elif object_type == 'SurfaceDispobj':
      import SurfaceDispobj
      dobj = SurfaceDispobj.SurfaceDispobj(self.name,params=params,selparams=selparams,colparams=colparams,styleparams=styleparams)

    return dobj


#-----------------------------------------------------------------
  def add_animation(self,filelist=[]):
#-----------------------------------------------------------------
    if self.animation:
      self.animation.delete()

    import Animation
    self.animation = Animation.Animation(parent=self,filelist = filelist)
    return self.animation

#-----------------------------------------------------------------
  def handle_program_exit(self):
#-----------------------------------------------------------------
    '''
    Tidy up on  program exit
    '''

    # save .ccp4mg file
    MODELINFO().save(set=self.name_label,filename=self.filename)

    #Remove a animation legend
    if hasattr(self,'animation') and self.animation.legend_obj:
      self.animation.legend_obj.delete()
    

#---------------------------------------------------------------------
  def copy_display(self,copy_from='',filename='', clear=0,**keywords):
#---------------------------------------------------------------------
    #print 'MolData.copy_display',copy_from
    '''
    This is left in as reminder of v1.0 option to load picture style from
    pickle file
    if filename:
      status = HISTORY().load_pickle_file(filename=filename,DIR_filename=DIR_filename)
      #print "load_pickle_file status",status
      if not status or not status.has_key('Data_list') :
        MGGUI().WarningMessage('No model status found in the file')
        return 1
      if len(status['Data_list'])==1:
        copy_dataobj = status[status['Data_list'][0]]
        shadow = 1
      else:
        model_menu = []
        for item in status['Data_list']:
          if status.has_key(item) and status[item].object_type == 'MolData':
            model_menu.append(item)
        MGGUI().CreateGUIWindow ('copy_from_file_model',self, [
            ['TITLE','Copy display from status file'],
            ['OFFSET','display_table'],
            ['RETURN_METHOD','copy_display',
               ['RETURN_LIST','file_model']],
            ['PARAM','file_model',
               ['TYPEDEF','menu',model_menu,model_menu]],
            ['DISPLAY','WINDOW',       
               ['LINE',
                 ['LABEL','Copy from model'],
                 ['WIDGET','file_model']],
               ['BUTTONS',['ok',['LABEL','Apply']],['dismiss','CLOSE']]
            ] ])
        self.copy_from_status = status
        return
    '''

    if copy_from:
      copy_dataobj = data(name=copy_from)
      if not copy_dataobj: return [1,'Error finding data object '+copy_from]
      copy_dataobj_shadow = dataobj.DataObjShadow()
      copy_dataobj_shadow.save(copy_dataobj)
      #Zap current display objects
      if clear:
        del_list=[]
        for obj in self.objinsts: del_list.append(obj)
        for obj in del_list:  obj.delete()

      # Use the base class update_data_status which only updates
      # the display objects and not any other 
      dataobj.DataObj.update_data_status(self,copy_dataobj_shadow,clear=clear)
      return [0,'']

    return [1,'']
  
#----------------------------------------------------------------------
  def save_file(self,filename=''):
#----------------------------------------------------------------------
    if os.path.splitext(filename)[1] == '.cif':
      rv = self.molHnd.WriteCIFASCII(filename)
    else:
      filename = os.path.splitext(filename)[0]+'.pdb'
      rv = self.molHnd.WritePDBASCII(filename)
    #print 'MolData.save_file',filename,rv
    self.setFilename(filename)
    # Unset any transform - do not apply an undo transformation
    self.molHnd.UnSetTransform(False)
    
    return rv

#----------------------------------------------------------------------
  def list_data_file(self,list_data='all',filename='',DIR_filename='',
                     file_format='PDB',**keywords):
#----------------------------------------------------------------------
    '''
    list_data = 'visible' => list only the atoms in visible display objects
    list_data = MolDisp.name => list only the atoms MolDisp
    or if filename is set then list the data in that file
    or list self.filename
    '''
    if file_format == 'PDB':
      save_ext = '.pdb'
    else:
      save_ext = '.cif'

    #print "list_data_file list_data",list_data
    if list_data == 'all' and os.path.splitext(self.filename[1]) == save_ext:
        DataObj.list_data_file(self,filename=self.filename[2])
    else:
      if ['visible','all'].count(list_data):
        selHnd = self.molHnd.NewSelection()
        if list_data == 'visible':
          dispobj_list = self.get_dispobj(object_type='MolDisp')
          for dispobj in dispobj_list:
            if dispobj.visible:
              self.molHnd.Select(selHnd,STYPE_ATOM,dispobj.selHnd,SKEY_OR)
        else:
          self.molHnd.SelectAtoms(selHnd, 0,"*",ANY_RES,"*",ANY_RES,"*", \
                               "*","*","*","*",SKEY_OR )
      else:
        dispobj = self.get_dispobj(name=list_data)
        if not dispobj: return [1,'ERROR in MolData.list_data_file']
        selHnd = dispobj.SelHandle.getSelHnd()

      #Keep unique index for each selection list for this model in
      # this session
      #(DatObj.list_data_file() has default index 0 for all data i.e. displaying the whole file)
      if not hasattr(self,'list_data_file_index'): self.list_data_file_index = 0
      self.list_data_file_index = self.list_data_file_index + 1

      # Get a name for temporary PDB/CIF file
      import utils
      if filename:
        filename = utils.get_filename(DIR_filename,filename)
      else:
        filename = os.path.join(utils.environScratch(), \
          utils.fileRoot(self.filename[1])+'_sele_'+
            str(self.list_data_file_index)+save_ext)
      #print "list_data_file filename",filename
      if os.path.exists(filename): os.remove(filename)

      RC = self.molHnd.WriteSelection(selHnd,filename,file_format)
        
      if ['visible','all'].count(list_data): self.molHnd.DeleteSelection(selHnd)
      
      if RC:
        return [1,'ERROR writing temporary file '+filename]
      else:
        return [0,filename]

      

#-------------------------------------------------------------------
  def transform(self,glwidget):
#-------------------------------------------------------------------
    '''
    Handle mouse input to translate objects
    '''
    import pygl_coord
    import utils

    #print 'MolData.transform', glwidget.transformDeltaMode()

    if  glwidget.transformDeltaMode() == 'translate':

      vtrans = glwidget.worldTranslate()
      #print "MolData.transform",vtrans
      vrot=[1.0,0.0,0.0,
            0.0,1.0,0.0,
            0.0,0.0,1.0]
      self.molHnd.SetTransform(vrot,vtrans,False)

    else:
      #Handle rotation not sorted
      vrotmat = glwidget.worldRotate().matrixVector()
      vtrans = [0.0,0.0,0.0]
      vrot = []
      ii = -1
      for i in [0,1,2]:
        for j in [0,1,2]:
          ii = ii + 1
          vrot.append(vrotmat[ii])
        ii = ii + 1
      self.molHnd.SetTransform(vrot,vtrans,False)
      
  
    # Moving cursor in x-direction - expect rot about y axis

    '''
    axes = [ [0.0,1.0,0.0], [1.0,0.0,0.0], [0.0,0.0,1.0] ]
    i = -1
    for rot in [rotx,roty,rotz]:
      i = i + 1
      if rot:
        frot = utils.safeFloat(rot,0.0)
        cart = pygl_coord.Cartesian(axes[i])
        cartnew = qmat * cart
        axis = [ cartnew.get_x(),cartnew.get_y(), cartnew.get_z() ]
        #print "axis",axis
        
        self.molHnd.SetTransform(frot,axis,-1)
    '''

    self.update_dependents(attribute='transform')

#--------------------------------------------------------------------
  def draw(self):
#--------------------------------------------------------------------
    for obj in self.objinsts:
      obj.do_redraw = 1
      obj.draw()

#--------------------------------------------------------------------
  def handle_transform_popup(self,transform_select='',**keywords):
#--------------------------------------------------------------------
    #Atom popup for the 'live' moving model
    #Enables setting the centre of rotation
    #print "handle_transform_popup",transform_select
    
    if transform_select:

      from atom_util import inta
      molobj,dispobj,atomptr = ATOMPICKING().get_last_picked('atom')

      if transform_select == 'SSE':
        mask = inta(7)
        for i in range(0,7): mask[i] = 1
        import string
        sub = string.strip(self.molHnd.ListSecStructure(mask,atomptr))
        if sub:
          sel = string.split(sub)[1]
        else:
          sel = ''

      else:
        mask = inta(9)
        py_mask = [0,1,1,1,1,1,1,1,0]
        for i in range(0,9): mask[i]=py_mask[i]
        selected_atom = molobj.molHnd.AtomLabel_mask(atomptr,mask)
        del mask    
        sel = self.convertAtomID ( out =transform_select , \
                                   atomID = selected_atom)
      #print "sel",sel
      if not sel: return
      selHnd = self.molHnd.NewSelection()
      self.molHnd.Select  (selHnd,STYPE_ATOM,sel,SKEY_NEW)
      self.transform_centre = sel
      self.molHnd.SetTransform(0.0,[1.0,0.0,0.0],selHnd)
      self.molHnd.DeleteSelection(selHnd)
      return

    else:
      # Define a portion of the pop-up menu to appear when
      # the user picks an atom
      molobj,dispobj,atomptr = ATOMPICKING().get_last_picked('atom')
      if molobj != self: return ['','','']
      params = [
           ['RETURN',self.name_string()+'.handle_transform_popup'], 
           ['PARAM','transform_select',
             ['LABEL','Rotate around this'],
             ['TYPEDEF','menu',['atom','residue','SSE','chain','all'],
                             ['atom','residue','SSE','chain','all']],
             ['INITIAL','']]
        ]

      display = [ ['pr','transform_select']]
    
      return ['',params,display]


      

#---------------------------------------------------------------------
  def findDispobjContainingAtom(self,atm_ptr=None,object_type='MolDisp'):
#---------------------------------------------------------------------
    '''
    Find the first MolDisp containing the atom atm_ptr
    Used when need to stick a label on the display
    '''
    #print "atm_ptr",atm_ptr
    dispobj = None
    ii = 0
    dobj_list = self.get_dispobj(object_type=object_type)
    #print "dobj_list",dobj_list
    while not dispobj and ii < len(dobj_list):
      selHnd = dobj_list[ii].SelHandle.getSelHnd()
      #print "ii,selHnd",ii,selHnd
      if selHnd >0 and atm_ptr.isInSelection(selHnd): dispobj = dobj_list[ii]
      ii = ii + 1

    return dispobj

            
   

            
#------------------------------------------------------------------
  def handle_edit(self,_edit='', exit='', nsecstr='',\
     edit_secstr_secstr='',edit_secstr_first='',edit_secstr_last='', \
     restype_res='',restype_type='',restype_alias='', \
     monlib='',DIR_monlib='', dsspfile='',DIR_dsspfile='',
     secstrfile='',**keywords):
#------------------------------------------------------------------
    import types
    #print "handle_edit restype_res",restype_res,restype_type,exit
    # Draw the GUI


    if _edit == 'restype':
      #print "status,sel_prot",status,sel_prot
      length = 0
      restype_res = []
      restype_alias = []
      restype_type = []

      #Get the residue name aliases
      status,alias= MODELINFO().get_info(set=self.name_label, \
               info='residue_aliases')
      #print "status,alias",status,alias
      if not status:
        for key,value in alias.items():
          restype_res.append(key)
          restype_alias.append(value)
          restype_type.append('')
          
      status,sel_prot= MODELINFO().get_info(set=self.name_label, \
               info='selection_protocols')
      #print "sel_prot",sel_prot
      if not status:
        for type in MolData.restype_alias:
          if sel_prot.has_key(type) and sel_prot[type].has_key('ORDER'):
            for item in sel_prot[type]['ORDER']:
              if restype_res.count(item):
                ii = restype_res.index(item)
                restype_type[ii] = type
              else:
                restype_type.append(type)
                restype_res.append(item)
                restype_alias.append('')
        length = len(restype_res)
      if status or length == 0:
        restype_res = ['']
        restype_alias = ['']
        restype_type=['']
        length=1

    
      
      MGGUI().CreateGUIWindow('edit_restype_'+self.name,self, [
        ['TITLE','Edit residue type assignment for '+self.name_label],
        ['ICON','Edit'],
        ['HELP','atom_typing#residue_types'],
        ['RETURN_METHOD','handle_edit',
          ['RETURN_LIST',
            ['edit_restype::res',['ALIAS','restype_res']],
            ['edit_restype::alias',['ALIAS','restype_alias']],
            ['edit_restype::type',['ALIAS','restype_type']]]],
        ['DISPLAY','WINDOW',
          ['LINE',['LABEL','For residue types not recognised by the monomer','-italic']],
          ['LINE',['LABEL','library enter the name of residue and either','-italic']],
          ['LINE',['LABEL','the equivalent library name and/or the residue type','-italic']],
          ['EXTENDING_FRAME','edit_restype',
             ['TITLE_LINE',
              ['LABEL','Name'],
              ['LABEL','library name'],
              ['LABEL','type']],
             ['LENGTH',length],
             ['CONFIGURE',['gui_label','residue type'],
                          ['res',restype_res],
                          ['alias',restype_alias],
                          ['type',restype_type]],
             ['PARAM','type',
              ['TYPEDEF','menu',MolData.restype_menu,MolData.restype_alias],
              ['INITIAL',MolData.restype_alias[0]]],
             ['PARAM','res',
              ['TYPE','_text'],
              ['INITIAL','']],
             ['PARAM','alias',
              ['TYPE','_text'],
              ['INITIAL','']],
             ['DISPLAY',
               ['LINE',
                 ['WIDGET','res'],
                 ['WIDGET','alias'],
                 ['WIDGET','type']]]  ],
           ['BUTTONS',['ok',['LABEL','Apply']],
                      ['dismiss','CLOSE']] ] ] )


   
    elif isinstance(restype_res,types.ListType) and exit == 'ok':
      # Set up sel_prot dictionary with items for the
      # 'standard' residue types
      sel_prot = {}
      for type in Selection.restype_alias:
        sel_prot[type]={}
        sel_prot[type]['ORDER']=[]
        
      # Load sel_prot with the residue type info from the GUI
      for ii in range(len(restype_res)):
        if not sel_prot[restype_type[ii]]['ORDER'].count(restype_res[ii]):
          sel_prot[restype_type[ii]]['ORDER'].append(restype_res[ii])
      # For each 'standard' type either add or delete from ModelInfo
      # dependent on whether there is an info in the sel_prot item
      for type in MolData.restype_alias:
        if sel_prot[type]['ORDER']:
         ret = MODELINFO().add_info(set=self.name_label, \
            info='selection_protocols',key=type,value=sel_prot[type])
        else:
          MODELINFO().delete_info(set=self.name_label, \
             info='selection_protocols',key=type)

      # Clear out any saved residue aliases
      MODELINFO().delete_info(set=self.name_label, \
             info='residue_aliases',key='*')
      # Save the residue name alias info
      for ii in range(len(restype_res)):
        if restype_res[ii] and restype_alias[ii]:
          #print "saving alias",restype_res[ii],restype_alias[ii]
          #aliases[restype_res[ii]] = restype_alias[ii]
          ret = MODELINFO().add_info(set=self.name_label, \
            info='residue_aliases',key=restype_res[ii],value=restype_alias[ii])
  
      
      # Apply residue typing
      self.updateCustomResTypes()
      # update secondary structure
      self.apply_edit_secstr()

      #redo bonding
      # as per loaddata
      # Get bonds - use any monomer library associated with this PDB 
      status,data= MODELINFO().get_info(set=self.name_label, \
               info='monomer_library')
    
      if data.has_key('filename'):
        import utils
        monlib = utils.get_filename(data['filename'][0],data['filename'][1])
      else:
        monlib = ''
      output = self.molHnd.GetMolBonds(monlib)
      #print "output from GetMolBonds",output
      self.load_warnings =  self.load_warnings + \
      '\nReassigning atom types and bonding..\n\n' + output
      # Redo analyse assuming res types have been changes
      self.analyse()
      
      # Redo selection of all display objects
      obj_list = self.get_dispobj(object_type='MolDisp')
      #print "reapply_selection obj_list",obj_list
      for obj in self.get_dispobj(object_type='MolDisp'):
        obj.reapply_selection = 1
        

    elif _edit == 'monlib':
      status,data= MODELINFO().get_info(set=self.name_label, \
               info='monomer_library')
      if data.has_key('filename'):
        import utils
        monlib = utils.get_filename(data['filename'][0],data['filename'][1])
      else:
        monlib = ''
      MGGUI().CreateGUIWindow ('monlib',self, [
        ['RETURN_METHOD','handle_edit'],
        ['TITLE','Select monomer library file'],
        ['OFFSET','display_table'],
        ['PARAM','monlib',
           ['TYPE','_cif_file'],
           ['INITIAL',monlib]],
        ['DISPLAY','SELECTFILE',
          ['FILE','monlib','-format','CIF','-filter','*.cif']]
        ] )

    elif monlib and exit == '1':
      import utils
      ret = MODELINFO().add_info(set=self.name_label, \
            info='monomer_library',key='filename',value=[DIR_monlib,monlib])

      filn=utils.get_filename(DIR_monlib,monlib)
      output = self.molHnd.GetMolBonds(filn)
      self.load_warnings = self.load_warnings + \
                           '\n\nRevised atom typing...\n\n' + output

    elif _edit == 'list_charges':
      self.molHnd.LoadCharge('surface_potential_charge')
      text = self.molHnd.PrintCharges()
      import string
      textlist = string.split(text,"\n")
      MGGUI().CreateGUIWindow ('list_charges',self,[
        ['TITLE',"Assigned charges"],
        ['HELP','surfaces#charge_assignment'],
        ['ICON','Data'],
        ['RETURN_METHOD','handle_edit',
           ['MODE','GUI']],
         ['OBJECT','data',
           ['TYPE','ListData'],
           ['RETURN_METHOD','handle_edit',
             ['MODE','GUI']],
           ['CONFIGURE',['height','20'],
                        ['width','60'],
                        ['pick_dataobj',self.name],
                        ['font','fixed'],
                        ['text_list',textlist]]],
        ['DISPLAY','WINDOW',
            ['MESSAGE'],
            ['SUBFRAME', [] , [
             ['LINE',
              ['LABEL','Double click on atom name to centre on atom','-italic']]]],
            ['DRAW','data'],
            ['BUTTONS',['dismiss0','CLOSE',['LABEL','Close']]] ]
       ])




 
#------------------------------------------------------------------
  def edit_secstr_popup(self,**keywords):
#------------------------------------------------------------------
    #print "edit_secstr_popup",keywords
    at_pick =  ATOMPICKING()
    molobj,dispobj,atomptr = at_pick.get_last_picked('atom')
    #print "molobj,dispobj",molobj.name,dispobj.name,molobj,self
    if molobj != self: return ['','','']
    #mask = inta(7)
    #for i in range(0,7): mask[i] = 1
    #sub = string.strip(molobj.molHnd.ListSecStructure(mask,atomptr))
    #  if onelet.count(sub[0]):
    #    sse = sse_name[onelet.index(sub[0])]+sub[1:]
    #else:
    #  sse = ''
   
    if self.interactive_edit_secstr:
      range = self.getResidueID(self.interactive_edit_secstr[0],
                                     atomptr)
      last_resid = range[2]
      
    else:  
      last_resid = self.getResidueID(atomptr)
      
    params = [
           ['RETURN',self.name_string()+'.handle_edit_secstr_popup'], 
           ['PARAM','start_range',
             ['TYPEDEF','menu',MolData.secstr_menu,MolData.secstr_alias],
             ['INITIAL',''],
             ['LABEL','Set sec structure for '+last_resid+' to']]
        ]

    display = [ ['pr','start_range'] ]

    if self.interactive_edit_secstr:
      sse = MolData.secstr_menu[MolData.secstr_alias.index(self.interactive_edit_secstr[1])]
      params.append( 
           ['PARAM','end_range',
             ['TYPE','_logical'],
             ['INITIAL',''],
             ['LABEL','Set '+range[0]+' '+sse ]])
      display.append(['com','end_range'])
      
    return ['',params,display]

#-----------------------------------------------------------------
  def handle_edit_secstr_popup (self,start_range='',end_range='',**keywords):
#-----------------------------------------------------------------
    #print "handle_edit_secstr_popup",start_range,'*',end_range
    #print "interactive_edit_secstr",self.interactive_edit_secstr
    at_pick =  ATOMPICKING()
    molobj,dispobj,atomptr = at_pick.get_last_picked('atom')
    resid = self.getResidueID(atomptr)

    #print "molobj,dispobj",molobj.name,dispobj.name,molobj,self
    if molobj != self: return ['','','']

    add = 0
    edit_secstr_first=[]; edit_secstr_last=[];edit_secstr_secstr=[]
    for item in self.edit_secstr:
      edit_secstr_first.append(item[0])
      edit_secstr_last.append(item[1])
      edit_secstr_secstr.append(item[2])
    
    if start_range:
      self.interactive_edit_secstr=[atomptr,start_range]
      if not edit_secstr_first[-1] and not edit_secstr_last[-1]:
         edit_secstr_first[-1]= resid
         edit_secstr_secstr[-1] = start_range
      else:
        edit_secstr_first.append(resid)
        edit_secstr_last.append('')
        edit_secstr_secstr.append(start_range)
        add = 1
      
    elif end_range:
      if edit_secstr_first[-1] == self.getResidueID(self.interactive_edit_secstr[0]) and edit_secstr_last[-1] == '':
        edit_secstr_last[-1] = resid
      self.interactive_edit_secstr=[]
       

    self.edit_secstr = []
    for i in range(len(edit_secstr_secstr)):
      if edit_secstr_first[i] or edit_secstr_last[i]:
         self.edit_secstr.append([edit_secstr_first[i], \
           edit_secstr_last[i],edit_secstr_secstr[i]])
    #ModelInfo.insts.add_info(set=self.name_label,info='edit_secstr',
    #                   key='edit_secstr',value=self.edit_secstr)
    self.apply_edit_secstr(apply=1)
    
    MGGUI().UpdateGUIWindow('edit_secstr_'+self.name, [
        ['OBJECT','edit_secstr',
              ['CONFIGURE',['add',str(add)],
                           ['secstr',edit_secstr_secstr],
                           ['first::selection',edit_secstr_first],
                           ['last::selection',edit_secstr_last]] ] ])

#------------------------------------------------------------------
  def update_ModelInfo_secstr(self):
#------------------------------------------------------------------
     # Test for pre-2.0 style secstr info - convert to new style and update the ModelInfo
     status,data = MODELINFO().get_info(set=self.name_label,info= 'edit_secstr',key='edit_secstr')
     if not status and data.has_key('edit_secstr'):
       seli_command = []
       secstr= []
       import model_selection
       for item in data:
         seli_list = self.interpretRanges(item[0],item[1])
         seli_command.append(seli_list[1])
         if not seli_command or not MolData.secstr_alias.count(item[2]): break
         secstr.append(item[2])
       #print 'MolData.update_ModelInfo_secstr',seli_command,secstr
       if secstr:
         MODELINFO().add_info(set=self.name_label,info= 'edit_secstr',key='selection_command',value=seli_command)
         MODELINFO().add_info(set=self.name_label,info= 'edit_secstr',key='secstr',value=secstr)
       MODELINFO().delete_info(set=self.name_label,info= 'edit_secstr',key='edit_secstr')


#------------------------------------------------------------------
  def setDSSPFile(self,filn):
#------------------------------------------------------------------
    self.DSSP_file = filn
    self.apply_edit_secstr()
  
#------------------------------------------------------------------
  def apply_edit_secstr(self,apply=1,ParamsManager=None,**keywords):
#------------------------------------------------------------------

    if not hasattr(self,'molHnd'): return
    selindexp = pygl_coord.intp()

    load_err = 1
    if hasattr(self,'DSSP_file'):
      print self.DSSP_file
      self.DSSP_file[2]= get_filename(self.DSSP_file[0],self.DSSP_file[1])
      #print "DSSP_file",self.DSSP_file
      if os.path.exists(self.DSSP_file[2]):
        load_err = self.LoadDSSP(self.DSSP_file[2])

    npdbsecs = 0

    if load_err and npdbsecs==0:
      flagBulge = PM('SecStr_param').params['flagBulge']
      amino_acid = MODELINFO().get_names_string(key='amino_acid', \
                                  set=self.name_label)
      pm = PM('SecStr_param')
      if pm and pm.params.has_key('min_helix_length'):
        minHelixResidues = pm.params['min_helix_length']
      else:
        minHelixResidues = 4
      for model in range(1,self.molHnd.GetNumberOfModels()+1):
        if self.molHnd.GetModel(model):
          rv = self.parse_selection(command='/'+str(model)+'/*/'+amino_acid)
          if not rv[0]:
            self.molHnd.GetModel(model).CalcSecStructure(bool(flagBulge),rv[1])
            self.molHnd.RemoveSmallHelices(model,minHelixResidues)
            self.molHnd.DeleteSelection(rv[1])
          else:
            self.molHnd.GetModel(model).CalcSecStructure(bool(flagBulge))
            self.molHnd.RemoveSmallHelices(model,minHelixResidues)

    if self.molHnd.GetNumberOfSecStructure(SSE_Strand)+self.molHnd.GetNumberOfSecStructure(SSE_Bulge) + self.molHnd.GetNumberOfSecStructure(SSE_Helix) == 0:
      # MG found no secondary structure, try HELIX/SHEET cards.
      npdbsecs = self.molHnd.ApplyPDBSecStructure(self.first_nmr_model)
    
    nChanged = 0

    if apply:

      secstr = []
      status,data = MODELINFO().get_info(set=self.name_label,info= 'edit_secstr')
      #print 'MolData.apply_edit_secstr',status,data 
      if not status and data.has_key('selection_command') and  data.has_key('secstr'):
        selection_command = data['selection_command']
        secstr = data['secstr']
          
      if secstr:
        resSelHnd = self.molHnd.NewSelection()
        selResidues = newPPCResidue()
        for ii in range(len(secstr)):
          if not MolData.secstr_alias.count(secstr[ii]): break
          # beware interpretRanges might return seletion of form 'range A/1 B/10'
          # which can not be interpreted by mmdb - so use parse_selection
          status,selHnd = self.parse_selection(command=selection_command[ii])
          if status: break                       
          self.molHnd.Select(resSelHnd,STYPE_RESIDUE,selHnd,SKEY_NEW)
          #nselRes =self.molHnd.GetSelIndex(resSelHnd,selResidues)
          selResidues = mmut.GetResidueSelIndex(self.molHnd,resSelHnd,selindexp)
          nselRes = selindexp.value()
          #print "MolData.apply_secstr nselRes",selection_command[ii],nselRes
          if nselRes > 0:
            SSE = inta(1)
            SSE = MolData.secstr_enum[MolData.secstr_alias.index(secstr[ii])]
            nChanged = nChanged + nselRes
            for ir in range(0,nselRes):
              pcres = getPCResidue(selResidues,ir)
              pcres.SSE = SSE
          self.molHnd.DeleteSelection(selHnd)
        self.molHnd.DeleteSelection(resSelHnd)

 
    # Redraw all display objects
    for obj in self.get_dispobj(object_type = 'MolDisp'):
      #print "do_redraw",obj
      if hasattr(obj,'model_colour'):
        if ['secstr','rules','blend'].count(obj.model_colour.colour_mode):
          # Force the CMolColour class to redo colour 
          obj.model_colour.CMolColour.SetMode(1)
          obj.do_recolour = max(2,obj.do_recolour)
          obj.model_colour.do_recolour = max(2,obj.do_recolour)
      if ['SPLINE','WORM','FATWORM'].count(obj.style): obj.do_redraw=1

    if nChanged:
      return [1,"WARNING: The model secondary structure has been edited by the user"]
    else:
      return [0,'']


 
#-----------------------------------------------------------------
  def apply_edit_bonds(self):
#-----------------------------------------------------------------
    status,edit_bonds = MODELINFO().get_info( \
        set=self.name_label,info= 'edit_bonds',key='edit_bonds')
    #print "apply_edit_bonds get_info",status,edit_bonds
    if status or not edit_bonds.has_key('edit_bonds'): return 
    
    if hasattr(self,'current_edit_bonds') and self.current_edit_bonds:
      current_edit_bonds = self.current_edit_bonds
    else:
      current_edit_bonds = []

    # Has any action been removed from the list?
    # If so then reverse the action
    #print "current_edit_bonds",current_edit_bonds
    
    for item in current_edit_bonds:
      if not edit_bonds['edit_bonds'].count(item):
        p_atom1 = self.interpretAtomID(self,item[0],force_one_atom=1)
        p_atom2 = self.interpretAtomID(self,item[1],force_one_atom=1)
        if not p_atom1 or not p_atom2:
          pass
        elif item[2] == 'delete':
          self.molHnd.EditBonds(1,p_atom1,p_atom2)
        elif item[2] == 'add':
          self.molHnd.EditBonds(-1,p_atom1,p_atom2)


    new_edit_bonds = []
    for item in edit_bonds['edit_bonds']:
      if not current_edit_bonds.count(item):
        p_atom1 = self.interpretAtomID(self,item[0],force_one_atom=1)
        p_atom2 = self.interpretAtomID(self,item[1],force_one_atom=1)
        rv = 1
        if not p_atom1:
          print "Attempting to edit bonds - no atom called",item[0]
        elif not p_atom2:
          print "Attempting to edit bonds - no atom called",item[1]
        elif item[2] == 'add':
          rv = self.molHnd.EditBonds(1,p_atom1,p_atom2)
        elif item[2] == 'delete':
          rv = self.molHnd.EditBonds(-1,p_atom1,p_atom2)
        #print "applying ",item,p_atom1,p_atom2,rv
        if not rv: new_edit_bonds.append(item)
      else:
        new_edit_bonds.append(item)
        
    self.do_update_all_bonded_atoms = 1
    for obj in self.get_dispobj(object_type='MolDisp'):
      obj.do_update_connectivity = 1
      obj.do_redraw = 1

    self.current_edit_bonds =  new_edit_bonds
    #print 'apply_edit_bonds current_edit_bonds',self.current_edit_bonds
    return

  
#-----------------------------------------------------------------
  def updateCustomResTypes(self):
#-----------------------------------------------------------------
    status,sel_prot= MODELINFO().get_info(set=self.name_label, \
               info='selection_protocols')
    #print "updateCustomResTypes status,sel_prot",status,sel_prot
    if not status: 
      clear = True;
      for i in range(len(MolData.restype_alias)):
        restype = MolData.restype_alias[i]
        if sel_prot.has_key(restype) and sel_prot[restype].has_key('ORDER'):
          for item in sel_prot[restype]['ORDER']:
            #print 'updateCustomResTypes calling SetCustomRestype',item,int(MolData.restype_code[i])
            rv = self.molHnd.SetCustomRestype(item,int(MolData.restype_code[i]),clear)
            clear = False

    # Load the user defined residue name aliases
    status,alias= MODELINFO().get_info(set=self.name_label, \
               info='residue_aliases')
    if not status:
      if alias:
        clear = True
        for key,value in alias.items():
          rv = self.molHnd.SetCustomResSynonym(key,value,clear)
          #print "SetCustomResSynonym rv",key,value,rv
          clear = 0
      else:
        #Clear all ResSynonyms
        rv = self.molHnd.SetCustomResSynonym('','',True)

#-----------------------------------------------------------------
  def apply_edit_links(self):
#-----------------------------------------------------------------
    self.updateCustomLinks()
    output = self.GetMolBonds()
    #print 'apply_edit_links',output
    self.redraw_all_objects()
    

#-----------------------------------------------------------------
  def updateCustomLinks(self):
#-----------------------------------------------------------------
    # FIXME -- TOTALLY UNIMPLEMENTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # This is mechanism for user to define links that are not in the monomer library
    # There are (at least) a couple of weaknesses -
    # - each model has its own defined links which when added to CMGSBase will be applied to all models
    # - there is no way to delete links in CMGSBase so could retain mistaken definitions (restarting
    #      program will get round this
    status,edit_links = MODELINFO().get_info( 
        set=self.name_label,info= 'edit_links',key='edit_links')
    #print 'MolData.updateCustomLinks',status,edit_links
    if edit_links.has_key('edit_links'):
      for item in edit_links['edit_links']:
        RESOURCES().CMGSBase.AddLink(item[0], item[1], item[2], item[3])

        
#------------------------------------------------------------------
  def set_save_format(self):
#------------------------------------------------------------------
    pass

    

#-------------------------------------------------------------------
  def get_centre(self,selection='',selHnd=''):
#-------------------------------------------------------------------

    # try interpreting the selection and derive a selHnd
    if selection != "":
      rv = self.parse_selection([None,selection])
      #print 'MolData.get_centre rv',rv,'selection',selection
      if rv[0] == 0 and rv[1] > 0:
        if_del_selhnd = 1
        selHnd = rv[1]
      else:
        if not selHnd: return [1,(0.0,0.0,0.0)]
    else:
      if_del_selhnd = 0
        
    # Get the Centre of Mass of selected atoms
    retval,extent = self.get_extent(selHnd)
    if retval: return [1,(0.0,0.0,0.0)]
    com = []
    for ii in [0,1,2]:
      com.append(( extent[ii] + extent[ii+3] ) / 2.0)
    #print "MolData.get_centre com",com
    return [0,com]


#-----------------------------------------------------------------------
  def get_extent ( self,selHnd):
#-----------------------------------------------------------------------
    extentptr = self.molHnd.Extent(selHnd)
    #print extentptr
    tmp = doublea(1)
    extent = tmp.frompointer(extentptr)
    if extentptr:
      retval = 0
    else:
      retval = 1
    return [retval,extent]
    
#-----------------------------------------------------------------------
  def interpret_selobj(self,selobj):
#-----------------------------------------------------------------------
    # Is the object name one of the display objects?
    if self.objnames.count(selobj) == 1:
      ii = self.objnames.index(selobj)
      #print "ii",ii
      obj = self.objinsts[ii]
      #print "obj",obj
      selHnd = obj.selHnd
    # .. or is it a user defined selection object
    retval,user_selection=MODELINFO().get_info( \
      set=self.name_label,info='user_selection',key=selobj)
    if not retval and user_selection.has_key(selobj):
      selection = user_selection[selobj]
      if (len(self.objnames) == 0):
        retval = 3
      else:
        i = 0
        for dobj in self.objinsts:
          if selobj == dobj.alias:obj = dobj
        if obj == "": obj = self.objinsts[0]
        
#------------------------------------------------------------------
  def movie_interpolation(self,initial_status=None,final_status=None, \
                          fraction=0.0,step='',**keywords):
#------------------------------------------------------------------
    #print "model movie_interpolation",fraction

    if hasattr(self,'animation'):
      import Animation
      if step != 0:
        self.animation.next_frame()
        if Animation.AnimationParamsManager().get('update_properties'):
          self.update_dependents('reconfigure')
        else:
          self.update_dependents('transform')

      
    if initial_status and final_status:
      if initial_status.dataparams.has_key('transform_matrix') or \
          final_status.dataparams.has_key('transform_matrix'):
        if initial_status.dataparams.has_key('transform_matrix'):
          ivMat = initial_status.dataparams['transform_matrix']
        else:
          ivMat = [ 1.0, 0.0, 0.0, 0.0, \
                    0.0, 1.0, 0.0, 0.0, \
                    0.0, 0.0, 1.0, 0.0, \
                    0.0, 0.0, 0.0, 1.0 ] 
        if final_status.dataparams.has_key('transform_matrix'):
          fvMat = final_status.dataparams['transform_matrix']
        else:
          fvMat =  [ 1.0, 0.0, 0.0, 0.0, \
                   0.0, 1.0, 0.0, 0.0, \
                   0.0, 0.0, 1.0, 0.0, \
                   0.0, 0.0, 0.0, 1.0 ]
        #print "model movie_interpolation",ivMat,fvMat
        if ivMat != fvMat:
          import pygl_coord
          iQuat = pygl_coord.Quat(pygl_coord.matrix(4,4,ivMat))
          fQuat = pygl_coord.Quat(pygl_coord.matrix(4,4,fvMat))
          #rotQuat = (iQuat*(1.0-fraction))+(fQuat*fraction)
          rotMat = ((iQuat*(1.0-fraction))+(fQuat*fraction)).getMatrix()
          transMat = pygl_coord.matrix(4,4, \
           [ 0.0, 0.0, 0.0,(((1.0-fraction)*ivMat[3])+(fraction*fvMat[3])), \
             0.0, 0.0, 0.0,(((1.0-fraction)*ivMat[7])+(fraction*fvMat[7])), \
             0.0, 0.0, 0.0,(((1.0-fraction)*ivMat[11])+(fraction*fvMat[11])), \
             0.0, 0.0, 0.0, 0.0 ] )
          rotMat=rotMat+transMat
          self.molHnd.SetTransform(rotMat,True)
      
        self.update_dependents('transform')

    DataObj.movie_interpolation(self,initial_status=initial_status, \
           final_status=final_status,fraction=fraction,step=step)
    
#--------------------------------------------------------------------------
  def copy_data_files(self,project='',dir='',overwrite=1,extra_files=[]):
#--------------------------------------------------------------------------
    if hasattr(self,'animation') and self.animation:
      if self.animation.file_format == 'NMR_models':
        if os.path.exists(self.animation.filename[2]):
          extra_files.append(self.animation.filename[2])
      elif self.animation.file_format == 'multiple_PDBs':
        for iframe in range(self.animation.frame_range[0],self.animation.frame_range[1]):
          path = self.animation.filebase[0]+str(iframe)+ \
                 self.animation.filebase[1]
          if os.path.exists(path): extra_files.append(path)
      if project:
        newfile = os.path.join(dir,os.path.split(self.animation.filename[2])[1])
        self.animation.filename=[self.animation.filename[0],project,newfile]
    #print "model copy_data_files",extra_files
    return DataObj.copy_data_files(self,project=project,dir=dir, \
                            overwrite=overwrite,extra_files=extra_files)

#-----------------------------------------------------------------
  def LoadDSSP ( self, filename='',**keywords ) :
#-----------------------------------------------------------------
    # Read DSSP SecStr output file
    import string
    selindexp = pygl_coord.intp()
    #filename = get_filename(DIR_dsspfile,dsspfile)
    f = openFile(filename,'r')
    if not f: return 1
    lines=f.readlines()
    if len(lines)<1:
      f.close()
      return 1

    ii = 0
    match = 0
    while match == 0 and ii<len(lines):
      if string.find(lines[ii],"RESIDUE AA STRUCTURE")>0:
        match = 1
        ii=ii+1
      else:
        ii = ii +1

    if ii == len(lines): return 2

    pMdl = self.molHnd.GetModel(1)

    while ii <len(lines):
      if len(lines[ii]) < 19:
         ii=ii+1
         continue
      resid = safeInt(string.strip(lines[ii][6:10]),-999)
      ins =  string.strip(lines[ii][10])
      chain = string.strip(lines[ii][11])
      dssp = lines[ii][16]
      dssp_qual = lines[ii][18]
      if dssp == 'T' and (dssp_qual == '3' or dssp_qual == '4' or  dssp_qual == '5'):
         dssp = dssp + ' ' + dssp_qual
    
      if resid != -999 and self.chains.count(chain):
        residues = newPPCResidue()
        resSelHnd = self.molHnd.NewSelection()
        self.molHnd.Select(resSelHnd,STYPE_RESIDUE, 0,chain,resid,ins,resid,ins,"*","CA","C","*" ,SKEY_NEW )
        #nSelRes = self.molHnd.GetSelIndex (resSelHnd,residues)
        residues = mmut.GetResidueSelIndex(self.molHnd,resSelHnd,selindexp)
        nSelRes = selindexp.value()
        pRes = None
        if nSelRes>0:
          pRes = getPCResidue(residues,0)
        #pRes = pMdl.GetResidue0(chain,resid,ins)
        if pRes and MolData.secstr_code_dssp.count(dssp):
          sse = MolData.secstr_enum_dssp[MolData.secstr_code_dssp.index(dssp)]
          pRes.SSE = sse
      ii=ii+1

    f.close()
    return 0

#------------------------------------------------------------------
  def get_open_schemes(self,info='user_selection',context='generic'):
#------------------------------------------------------------------
    schemes = []

    if info == 'user_selection':
      for obj in self.objinsts:
        if ['MolDisp'].count(obj.object_type):
          selhandle_list = [ obj.SelHandle]
        elif ['SurfaceDispobj','HBonds','Contacts'].count(obj.object_type):
          selhandle_list = [ obj.SelHandle1, obj.SelHandle2]
        else:
          selhandle_list = []
        for selh in selhandle_list:
          if selh.selparams['select'] == 'selection_scheme':
            if not schemes.count(selh.selparams['selection_scheme']):
              schemes.append(selh.selparams['selection_scheme'])
              
    #elif info == 'user_colour_schemes':
    #  for obj in self.objinsts:
    #    if ['MolDisp','SurfaceDispobj'].count(obj.object_type):
    #      if obj.model_colour.colour_mode == 'rules'  and hasattr(obj,'user_scheme') and len(obj.user_scheme)>=2 and obj.user_scheme[1]==context and (not schemes.count(obj.user_scheme[0])):
    #        schemes.append(obj.user_scheme[0])
          
    return schemes

#-------------------------------------------------------------------
  def redraw_all_objects(self):
#-------------------------------------------------------------------

    # Force redraw of all display objects after model editted
    # beware - about to delete items from list
    dispobj_list = self.get_dispobj()
    #print "redraw_all_objects",dispobj_list
    for obj in dispobj_list:
      obj.set_reapply_selection()
      obj.do_update_connectivity = 1
     
    # Redo the connectivity
    self.do_update_all_bonded_atoms = 1
    self.update_all_bonded_atoms()
           

#-----------------------------------------------------------------
  def resetSelHandles(self):
#-----------------------------------------------------------------
    # Forces reselection of all mmdb selection handles - necessary
    # if number of atoms in model has changed (eg in handling symmetry)
    for obj in self.objinsts:
      for item in ['SelHandle','SelHandle1','SelHandle2']:
         selh = getattr(obj,item,None)
         if selh: selh.setSelHnd(-1)

          
 
# Functions to create and return the ParamsManager for analysis
# classes related to MolData

#---------------------------------------------------------------
def SecStructureParamsManager():
#---------------------------------------------------------------
    import services
    
    pm = services.ParamsManager (
          name='SecStr_param',
          title='Secondary structure',
          picture_definition = 1,
          gui =['flagBulge','min_helix_length'],
      default = { 'flagBulge' : 1, 'min_helix_length' : 4 },
      definition = {
        'flagBulge' : dict (type=int,label='Show beta bulges',style='checkbox'),
        'min_helix_length' : dict (type=int,label="Min. residues per alpha helix",style= 'spinbox',
                                     max=200.0,min=0,step=1),
       },
      help = 'analysis#secstr' )
    #MolData.SecStructureParamsManager = services.ParamsManager ( \
    #      name='SecStr_param', \
    #      title='SecStructure Parameters', \
    #      param_order=['NOmaxdist','NOCanglemin','flagBulge'], \
    #  default = { 'NOmaxdist' : 3.5, \
    #              'NOCanglemin' : 90.0, \
    #              'flagBulge' : 1 }, \
    #  gui = {'NOmaxdist' : [float,'Maximum N-O Hbond distance'], \
    #         'NOCanglemin' : [float,'Minimum N-O-C Hbond angle'], \
    #         'flagBulge' : [int,'Show beta bulges',['TYPE','_logical']] }, \
    #  help = 'secstr' )
    return  pm

#---------------------------------------------------------------
def createSASAreaPM(name='SASArea_param',copy_from=''):
#---------------------------------------------------------------
    import services
    return services.ParamsManager (
        name = name,
        picture_definition = 1,
        copy_from=copy_from,
        help = 'analysis#sas',
        title = 'Solvent accessible surface area',
        gui=['HOHrad','point_density','brick_margin','exclude_solvent','list_selection_1','list_selection_2','limit_selection'],
        default = {'HOHrad' : 1.4,
                   'point_density': 1.0,
                   'brick_margin': 5.0,
                   'exclude_solvent': 1,
                   'exclude_non_polymer': 1,
                   'list_selection_1': 'polar_atoms',
                   'list_selection_2': '',
                   'limit_selection' : 'amino_acid'
                   },
        definition = { 'HOHrad' : dict(type=float,label='Water radius',style='spinbox',max=10.0,min=0.0,step=0.5),
                'point_density': dict(type=float,label='Point density',style='spinbox',max=2.0,min=0.01,step=0.05),
                'brick_margin': dict(type=float,label='Brick margin',style='spinbox',max=20.0,min=0.0,step=1.0),
                'exclude_solvent': dict(type=int,label='Exclude solvent',style='checkbox'),
                'exclude_non_polymer': dict(type=int,label='Exclude non-polymer',style='checkbox'),
                'list_selection_1' : dict(type=str,label='List total SAS for selected atoms'),
                'list_selection_2' : dict(type=str,label='List total SAS for selected atoms'),
        'limit_selection' : dict(type=str , label='Apply SAS calculation to', style='combobox', menu=['all non-solvent','peptide','nucleic acid'],alias=['all','amino_acid','nucleic']) } )
            #    'altLoc': 'A' }
            #    'altLoc' : [text,'For alternate location'] },

   

   
#-----------------------------------------------------------------
#-----------------------------------------------------------------
#-----------------------------------------------------------------
class MolDisp(DispObj):
#-----------------------------------------------------------------
#-----------------------------------------------------------------
#-----------------------------------------------------------------


  # Drawing styles that do not need update of connectivity
  no_connectivity = ['SPHERES','SURFACE','NUCLEICBASEPAIRS','NUCLEICBASEBLOCKS','LIPIDS','ANISOU']
  spline_styles = ['SPLINE','WORM','FATWORM']
  custom_spline_style_prefix = 'VARIABLEWORM'
  connected_styles = ['BONDS','FATBONDS','THINBONDS','BALLSTICK','CYLINDERS','CIRCLES','IMPOSTER_SPHERES']
  
  #  'geometry':['geometry','geometry.geometry.insts.define_bond_menu'] }

  class_dependents = {}
  gui_offset = 'display_table'


  style_menu =  ['Bonds','Fat bonds','Thin bonds', 'Ball and stick','Cylinders', \
                      'Spheres','Ribbons','Worm','Worms/tubes','Base pair sticks','Nucleic base blocks','Lipid Cartoon','Thermal ellipse','Circles','Perfect spheres','Bloboids','Sugar blocks']
  style_commands_icons = {'BONDS':'bonds.svg','FATBONDS':'fatbonds.svg','THINBONDS':'thinbonds.svg','BALLSTICK':'ballandstick.svg','CYLINDERS':'cylinders.svg', \
         'SPHERES':'spheres.svg','SPLINE':'ribbons.svg','WORM':'worms.svg','FATWORM':'wormstubes.svg','NUCLEICBASEPAIRS':'basepairsticks.svg',
                    'NUCLEICBASEBLOCKS':'baseblocks.svg','LIPIDS':'lipidcartoons.svg','ANISOU':'thermalellipse.svg', 'CIRCLES':'circles.svg','IMPOSTER_SPHERES':'spheres.svg','BLOB_ELLIPSOIDS':'thermalellipse.svg','SUGAR_BLOCKS':'baseblocks.svg'}
  style_commands = ['BONDS','FATBONDS','THINBONDS','BALLSTICK','CYLINDERS', \
         'SPHERES','SPLINE','WORM','FATWORM','NUCLEICBASEPAIRS',
                    'NUCLEICBASEBLOCKS','LIPIDS','ANISOU','CIRCLES','IMPOSTER_SPHERES','BLOB_ELLIPSOIDS','SUGAR_BLOCKS']

  custom_style_methods = {}

  style_initialisation = {
    'style_mode' : 'BONDS',
    'inter_dispobj_bond' : 1 }

  anisou_style_alias = ['axes','solid','solidaxes']
  anisou_style_code = [cprim.SPHEROID_AXES,cprim.SPHEROID_SOLID,cprim.SPHEROID_SOLIDAXES]

  def toggleBiomolecule(self):
      self.doBiomolecule = not self.doBiomolecule
      if hasattr(self,"graphmod") and hasattr(self.graphmod,"obj") and hasattr(self.graphmod.obj,"SetSymmetryMatrices"):
         if self.doBiomolecule:
           self.graphmod.obj.SetSymmetryMatrices([])
         self.graphmod.doBiomolecule = self.doBiomolecule
         self.graphmod.obj.SetDrawSymmetry(1)
         if hasattr(MAINWINDOW(),"glWidget"):
           MAINWINDOW().glWidget.SetupSymmetry()
         import rebuild
         rebuild.UpdateDisplay()

  def toggleSymmetryStatus(self):
      self.doSymmetry = not self.doSymmetry
      if hasattr(self,"graphmod") and hasattr(self.graphmod,"obj") and hasattr(self.graphmod.obj,"SetSymmetryMatrices"):
         self.graphmod.doSymmetry = self.doSymmetry
         if self.doSymmetry:
           self.graphmod.doCentralSymmetry = self.doCentralSymmetry
         if hasattr(MAINWINDOW(),"glWidget"):
           MAINWINDOW().glWidget.SetupSymmetry()
         import rebuild
         rebuild.UpdateDisplay()
  
  def toggleCentralSymmetryStatus(self):
      self.doCentralSymmetry = not self.doCentralSymmetry
      if hasattr(self,"graphmod") and hasattr(self.graphmod,"obj") and hasattr(self.graphmod.obj,"SetSymmetryMatrices"):
         self.graphmod.doCentralSymmetry = self.doCentralSymmetry
         if hasattr(MAINWINDOW(),"glWidget"):
           MAINWINDOW().glWidget.SetupSymmetry()
         import rebuild
         rebuild.UpdateDisplay()
  
#------------------------------------------------------------------
  def __init__(self,parent,name='',visible=1,master_application=None, \
        display_table_visible = 1, 
	selparams={'command':'all'}, colparams={}, styleparams={'style_mode':'BONDS'}, params= {}, \
              restore_from_version=0, **keywords):
#------------------------------------------------------------------
    global gui
    DispObj.__init__(self,'MolDisp',name, parent,visible=visible, \
                     master_application=master_application,
                     display_table_visible=display_table_visible,
                     params=params)
    self.doSymmetry=True
    self.doCentralSymmetry=True
    self.doBiomolecule=False
    self.com = []
    self.origin = []
    # Flag to update display without updating any of the display
    # attributes - probably set because atom coordinates have
    # changed
    import loadPlugins
    loadPlugins.loadPlugins('initializeStylePlugin')
    self.reapply_selection=1
    self.redraw_picked_labels = 0
    self.connectivity_defns = []
    self.update_atomRadii = 0
    self.do_update_connectivity = 1
    self.update_surface = 1
    self.built = [] 
    self.pickedAtoms= {}
    self.pickedAtomsMatrices = {}
    self.pickedAtomsUserMoveMatrices = {}
    self.pickedAtomsLabel= {}
    self.pick_label_graphmod = None
    self.external_bonds_graphmod = None
    self.do_redraw = 1
    self.do_redraw_external = 1

    self.ignore_other_dispobjs = False
    self.ignore_symmetry = False
    
    self.Connectivity = Connectivity()
    
    # Set the SelHandle
    #self.SelHandle = SelHandle(self.parent.name,selparams=selparams,command=selparams.get('command',''))
    if restore_from_version and restore_from_version[0]==1 and restore_from_version[1]<108:
         selparams = model_selection.fix_v108(selparams)
      
    self.SelHandle = SelHandle(self.parent.name,selparams=selparams)

    # Ensure global params managers are loaded

    PM('render_quality').add_dependent(handleRenderParamsManager)
     
    if params.has_key('drawing_style'):
      # Force creating custom drawing stype PM
      pm = self.getDrawingStylePM(name=params['drawing_style'],custom=1)
    else:
      pm = self.getDrawingStylePM()     
    pm.add_dependent(self.handleDrawingStylePM)
    
    # Instantiate a a graphics object and assign some parameters
    import moleculargraphicsmodel
    self.atomRadii = pygl_coord.DoubleVector(2)
    for gobj_name in ['graphmod','external_bonds_graphmod']:
      #for gobj_name in ['graphmod']:
      gobj = moleculargraphicsmodel.moleculargraphicsmodel(
                       parameters = pm,
                      global_parameters =  PM('render_quality'))
      gobj.setparams(molHnd=self.parent.molHnd, \
                            Connectivity = self.Connectivity, \
             diagnostic_timing = HISTORY().diagnostic['timing'])
      gobj.setparams(atomRadii=self.atomRadii)
      setattr(self,gobj_name, gobj) 
    

    #Interface.__init__(self,contact_MolDisp=params.get('contact_MolDisp'))

    # Initialise extent - after initilising atom selection
    self.set_extent()

    self.masterCrystal = ''
    
    # Initialise the style selection parameters
    for key,value in MolDisp.style_initialisation.items():
      setattr(self,key,value)
    
    # update style params based on input params
    self.style = ''
    self.style_mode = 'BONDS'
    self.set_style(styleparams=styleparams)

      
    self.model_colour=model_colour.model_colour(colparams=colparams,MolData=self.parent,SelHandle=self.SelHandle)
    self.CMgAppUtils = CMgAppUtils(self.parent.molHnd,self.SelHandle.getSelHnd())

    # Setup the label object
    self.label = None
    #print "MolDisp label_params",params.get('label_params',{})
    self.label = MolLabel(parent=self.name,mol=self.parent.name, \
                            params = params.get('label_params',{}))

    if params.has_key('doCentralSymmetry'):
      self.doCentralSymmetry = params['doCentralSymmetry']
      self.graphmod.doCentralSymmetry = self.doCentralSymmetry

    if params.has_key('doBiomolecule'):
      self.doBiomolecule = params['doBiomolecule']
      self.graphmod.doBiomolecule = self.doBiomolecule
      if self.doBiomolecule:
        self.graphmod.obj.SetDrawSymmetry(1)

    if params.has_key('doSymmetry'):
      self.doSymmetry = params['doSymmetry']
      self.graphmod.doSymmetry = self.doSymmetry

    # Anything dependent on creation of a MolDisp?
    self.update_dependents('create')
    if self.visible and  not MolDisp.no_connectivity.count(self.style):
      if MolDisp.spline_styles.count(self.style) or self.style.startswith(MolDisp.custom_spline_style_prefix):
        self.parent.changedSplines.append(self.name)
      else:
        self.parent.changedConnected.append(self.name)

    dispobjs = self.parent.get_dispobj()
    for dispobj in dispobjs:
       if hasattr(dispobj,"masterCrystal") and dispobj.masterCrystal!='':
          self.setMasterCrystal(dispobj.masterCrystal)
          break

    if self.doBiomolecule:
      self.graphmod.obj.SetSymmetryMatrices([])

#--------------------------------------------------------------------
  def removeCustomDrawingStyle(self):
#--------------------------------------------------------------------
    pm = self.getDrawingStylePM()     
    pm.remove_dependent(self.handleDrawingStylePM)
    if hasattr(self,'drawingStylePM'):
      del self.drawingStylePM
    pm = self.getDrawingStylePM()     
    pm.add_dependent(self.handleDrawingStylePM)
    for gobj_name in ['graphmod','external_bonds_graphmod']:
      if hasattr(self,gobj_name):
         getattr(self,gobj_name).setparams(parameters = pm)

#--------------------------------------------------------------------
  def delete(self,**keywords):
#--------------------------------------------------------------------
    #print "Deleting MolDisp",self.name
    if self.visible and not MolDisp.no_connectivity.count(self.style):
      if MolDisp.spline_styles.count(self.style) or self.style.startswith(MolDisp.custom_spline_style_prefix):
        self.parent.changedSplines.append(self.name)
      else:
        self.parent.changedConnected.append(self.name)
    if hasattr(self,'label') and self.label:
      self.label.delete_labels()
      del self.label
    DispObj.delete(self)
    for gobj_name in ['pick_label_graphmod','external_bonds_graphmod']:
      gobj = getattr(self,gobj_name,None)
      if gobj:
        MAINWINDOW().removeGLDisplayObject(gobj)
        BUILD().deletemod(gobj)

    if hasattr(self,'Connectivity'): del self.Connectivity
    if hasattr(self,'CMgAppUtils'): del self.CMgAppUtils
    if hasattr(self,'SelHandle'): del self.SelHandle

    
    if hasattr(self,'drawingStylePM'):
      self.closeDrawingStylePM(reset=0)
    else:  
      self.getDrawingStylePM().remove_dependent(self.handleDrawingStylePM)
    

#-------------------------------------------------------------------------
  def set_visibility ( self,toggle_visibility='',visibility='',**keywords):
#-------------------------------------------------------------------------
    DispObj.set_visibility(self,toggle_visibility=toggle_visibility,visibility=visibility)
    for gobj_name in ['pick_label_graphmod','external_bonds_graphmod']:
      gobj = getattr(self,gobj_name,None)
      if gobj: gobj.obj.visible = self.visible

    if not MolDisp.no_connectivity.count(self.style):
      if MolDisp.spline_styles.count(self.style) or self.style.startswith(MolDisp.custom_spline_style_prefix):
        self.parent.changedSplines.append(self.name)
      else:
        self.parent.changedConnected.append(self.name)
      


#------------------------------------------------------------------------
  def set_selection(self,command):
#------------------------------------------------------------------------
    self.SelHandle.setCommand(command)
    self.reapply_selection=1


#------------------------------------------------------------------------
  def set_colour(self,colour):
#------------------------------------------------------------------------
    self.model_colour.set_colour(one_colour=colour)
    self.update_dependents('colour')
    
#------------------------------------------------------------------------
  def set_reapply_selection(self,mode=1):
#------------------------------------------------------------------------
    self.reapply_selection = mode
    
  def set_redraw_text(self):
    if self.label: self.label.do_redraw = 1


#------------------------------------------------------------------------
  def canBeTransparent(self):
#------------------------------------------------------------------------
    return 1

#------------------------------------------------------------------------
  def isLegendPossible(self):
#------------------------------------------------------------------------
    return 1

#------------------------------------------------------------------------
  def getLegend(self):
#------------------------------------------------------------------------
    return self.model_colour.getLegend()
  
#------------------------------------------------------------------------
  def params ( self , all=1):
#------------------------------------------------------------------------
    pars = DispObj.params(self,all=all)

    pars['label_params'] = self.label.params()
    if not pars['label_params']: del pars['label_params']
    if hasattr(self,'doCentralSymmetry'):
      pars['doCentralSymmetry'] = self.doCentralSymmetry
    if hasattr(self,'doBiomolecule'):
      pars['doBiomolecule'] = self.doBiomolecule
    if hasattr(self,'doSymmetry'):
      pars['doSymmetry'] = self.doSymmetry
    if hasattr(self,'drawingStylePM'):
      pars['drawing_style'] = self.drawingStylePM.name
    for item in ['contact_MolDisp']:
      pars[item] = getattr(self,item,'')

    return pars

  def get_colour_label(self):
    return self.model_colour.colour_label

  def get_colour_icon(self):
    return self.model_colour.colour_icon


#------------------------------------------------------------------
  def selparams(self,all=1):
#------------------------------------------------------------------
    #print "MolDisp selparams",self.SelHandle.getSaveParams()
    return self.SelHandle.getSaveParams(all=all)

#------------------------------------------------------------------
  def colparams(self,all=1):
#------------------------------------------------------------------
    return self.model_colour.colparams(all=all)
  
#------------------------------------------------------------------
  def apply_selection(self):
#------------------------------------------------------------------
    self.reapply_selection = 0
    
    if ['SURFACE'].count(self.style_mode):
        self.SelHandle.cleanSelection(close = 0.01)
        

    atomtype = self.SelHandle.selparams.get('atomtype')
    
  # Set the connectivy rules for bond generation
    self.connectivity_defns = []
    #If is simple catrace then do not need to limit
    # contact search so there are no selection criteria
    #print 'apply_selection',self, atomtype, 'command:', self.SelHandle.getCommand()
  
    if ['catrace','CA+side'].count(atomtype) or self.SelHandle.getCommand().count('catrace'):
      trace_selection = 'amino_acid'
      trace_atom = 'CA'
      trace_cutoff =  self.getDrawingStylePM().get('trace_cutoff')
      if self.parent.alt_locs:
        self.connectivity_defns.append([['and',[None,trace_selection],[None,'/*/*/*/'+trace_atom+':'+self.parent.alt_locs[0]+',']],trace_cutoff])
        for alt in self.parent.alt_locs[1:]:
          self.connectivity_defns.append([['and',[None,trace_selection],[None,'/*/*/*/'+trace_atom+':'+alt]],trace_cutoff])
      elif atomtype == 'catrace':
        self.connectivity_defns.append([[],trace_cutoff])
      else: 
        self.connectivity_defns.append([[None,'catrace'],trace_cutoff])

          
    elif ['trace'].count(atomtype) or self.SelHandle.getCommand().count('trace'):
      trace_sel = self.parent.expand_selection_alias('trace')
      #print 'apply_selection from expand_selection_alias',trace_sel
      #trace_selection = self.getDrawingStylePM().get('trace_selection')
      #trace_atom = self.getDrawingStylePM().get('trace_atom')
      trace_cutoff = self.getDrawingStylePM().get('trace_cutoff')
      self.connectivity_defns.append([[],trace_cutoff])

    self.set_extent()
    #PICKLABEL().update_label_visibility(self)
    
    self.do_recolour = max(1,self.do_recolour)
    self.do_update_connectivity = 1
    self.update_surface = 1

    self.model_colour.CMolColour.SetSelHandle(self.SelHandle.getSelHnd())
    self.model_colour.do_recolour=2
    if self.label: self.label.do_redraw = 2
    self.update_atomRadii = 1
    if not MolDisp.no_connectivity.count(self.style):
      if MolDisp.spline_styles.count(self.style) or self.style.startswith(MolDisp.custom_spline_style_prefix):
        self.parent.changedSplines.append(self.name)
      else:
        self.parent.changedConnected.append(self.name)


    return 1
    #if self.dependent: exec self.dependent 
    #self.update_dependents('selection')

#------------------------------------------------------------------
  def styleparams(self,all=1):
#------------------------------------------------------------------
    sty = {}
    if all:
      for item in MolDisp.style_initialisation.keys():
        if hasattr(self,item): sty[item]=getattr(self,item)
    else:
      for item in MolDisp.style_initialisation.keys():
        if  hasattr(self,item) and getattr(self,item) != MolDisp.style_initialisation[item]:
          sty[item]=getattr(self,item)

    #print "MolDisp.styleparams",all,sty
    return sty


#-----------------------------------------------------------------
  def set_style ( self, styleparams={},**kw):
#-----------------------------------------------------------------
    #print "set_style",styleparams,kw
    styleparams.update(kw)
    if styleparams:
      if styleparams.get('inter_dispobj_bond',self.inter_dispobj_bond) != self.inter_dispobj_bond: self.do_redraw=1
      if styleparams.get('style_mode',self.style_mode) != self.style_mode:
        self.do_redraw=1
        
      for item in styleparams.keys(): 
        setattr(self,item,styleparams[item])

      # If we are changing between a 'connecting' and a 'non-conecting'
      # style then need to update_all_bonded_atoms
      if self.visible and ((not MolDisp.no_connectivity.count(self.style_mode)) or
             not (MolDisp.no_connectivity.count(self.style)) ):
        if MolDisp.spline_styles.count(self.style_mode) != MolDisp.spline_styles.count(self.style):
          self.parent.changedSplines.append(self.name)
        if MolDisp.no_connectivity.count(self.style_mode) != MolDisp.no_connectivity.count(self.style):
          self.parent.changedConnected.append(self.name)
          
      if ["SPHERES","BALLSTICK",'CIRCLES','IMPOSTER_SPHERES'].count(self.style_mode) and ( self.style_mode != self.style) : self.update_atomRadii = 1

    import copy
    self.style = copy.deepcopy(self.style_mode)

    self.style_icon = None
    if self.style_mode in self.style_commands_icons:
       self.style_icon = self.style_commands_icons[self.style_mode]
       if os.path.exists(os.path.join(os.environ['CCP4MG'],'qticons','disptab','style',self.style_icon)):
         self.style_icon = os.path.join(os.environ['CCP4MG'],'qticons','disptab','style',self.style_icon)
    self.style_label = equivKeyword(self.style,self.style_commands, self.style_menu)

    
#------------------------------------------------------------------------
  def centre_on ( self, **keywords):
#------------------------------------------------------------------------
    VW('mainview').centreModels(dispobj=[self])
    
      
#-------------------------------------------------------------------------
  def clone(self,clone=1,**keywords):
#-------------------------------------------------------------------------
     if self.style == 'SURFACE':
       visible = 0
     else:
       visible = self.visible
     selparams = self.selparams()
     colparams=self.colparams()
     styleparams=self.styleparams() 
     MolDisp(self.parent.name,visible=visible,
	selparams=selparams,
	colparams=colparams,
	styleparams=styleparams )
     return None

#-----------------------------------------------------------------------
  def list_data(self,filename='',overwrite='query',**keywords):
#-----------------------------------------------------------------------
    return self.parent.list_data_file ( list_data=self.name )


#------------------------------------------------------------------------
  def getDrawingStylePM(self,name='',custom=0,auto_save=0):
#------------------------------------------------------------------------
    if custom and not hasattr(self,'drawingStylePM'):
      if not name: name = 'model_drawing_style'+self.name
      PM('model_drawing_style').remove_dependent(self.handleDrawingStylePM)
      self.drawingStylePM = createDrawingStylePM(name=name,copy_from='model_drawing_style')
      self.drawingStylePM.add_dependent(self.handleDrawingStylePM)
      for gobj_name in ['graphmod','external_bonds_graphmod']:
        gobj = getattr(self,gobj_name,None)
        if gobj: gobj.setparams(parameters=self.drawingStylePM)
        dt = DISPLAYTABLE()
        if dt: dt.emit(QtCore.SIGNAL("CustomDrawingStyleChanged"))
      # Save the status if user opening new PM
      # (but not if recreating at MolDisp initialisation)
      if auto_save: HISTORY().autoSave()
    if hasattr(self,'drawingStylePM'):
      return self.drawingStylePM
    else:
      return PM('model_drawing_style')

#------------------------------------------------------------------------
  def closeDrawingStylePM(self,reset=1):
#------------------------------------------------------------------------
    if hasattr(self,'drawingStylePM'):
      #print "closeDrawingStylePM",self.drawingStylePM.name
      self.drawingStylePM.remove_dependent(self.handleDrawingStylePM)
      self.drawingStylePM.delete()
      del self.drawingStylePM
      if reset:
        for gobj_name in ['graphmod','external_bonds_graphmod']:
          gobj = getattr(self,gobj_name,None)
          if gobj: gobj.setparams(parameters=PM('model_drawing_style'))
        PM('model_drawing_style').add_dependent(self.handleDrawingStylePM)

#------------------------------------------------------------------------
  def handleDrawingStylePM(self,ParamsManager=None,updated={},**keywords):
#------------------------------------------------------------------------

    # Called when the user hits apply in either the
    # generic or the custom ParamsManager gui

    #print "handleDrawingStylePM updated",ParamsManager,updated
    self.do_redraw = 1
    
    if updated.has_key('ball_radii_scale') and  (self.style == "BALLSTICK" or self.style == 'CIRCLES') and \
        updated['ball_radii_scale'] > 0.001 and updated['ball_radii_scale'] <  10.0:
      self.do_redraw = 1
      self.update_atomRadii = 1
    if updated.has_key('sphere_radii_scale') and  (self.style == "SPHERES" or self.style == "IMPOSTER_SPHERES") and \
        updated['sphere_radii_scale'] > 0.001 and updated['sphere_radii_scale'] <  10.0:
      self.do_redraw = 1
      self.update_atomRadii = 1

    
#------------------------------------------------------------------------
  def update_connectivity(self):
#------------------------------------------------------------------------
    if not self.do_update_connectivity: return
    nSelAtoms,selAtoms = self.SelHandle.getSelIndex()
    selindexp = pygl_coord.intp()
    #print "update_connectivity nSelAtoms",self.name,nSelAtoms
    #start_time = time.clock()
    self.Connectivity.Clear()
    if nSelAtoms <= 0: return
    
    #self.parent.molHnd.ListBonds(self.SelHandle.getSelHnd(),nSelAtoms,selAtoms)
    #if self.selection != [None,'catrace']:
    if self.inter_dispobj_bond:      
      self.Connectivity.AddBonds(self.parent.molHnd,self.SelHandle.getSelHnd(),
        selAtoms,nSelAtoms,self.parent.all_bonded_atoms,self.parent.all_bonded_atoms_spline)
    else:
      self.Connectivity.AddBonds(self.parent.molHnd,self.SelHandle.getSelHnd(),
        selAtoms,nSelAtoms,0,0)
        
    # Loop over definitions for additional connectivities 
    #print self.connectivity_defns
    for defn in self.connectivity_defns:
      #print 'MolDisp.update_connectivity',self.name,self.SelHandle.getCommand(),defn
      if defn[0]:
        # defn[0] is selection definition to define atoms to apply
        # contact search
        # parse the selection and 'and' with the atoms in this
        # display object
        status,selHnd1 = self.parent.parse_selection(defn[0])
        #print 'update_connectivity',status,selHnd1
        if status:break
        self.parent.molHnd.Select( selHnd1,STYPE_ATOM,self.SelHandle.getSelHnd(),SKEY_AND)
        selAtoms1 = newPPCAtom()
        #nSelAtoms1 =self.parent.molHnd.GetSelIndex(selHnd1,selAtoms1)
        selAtoms1 = mmut.GetAtomSelIndex(self.parent.molHnd,selHnd1,selindexp)
        nSelAtoms1 = selindexp.value()
        #print 'update_connectivity nSelAtoms1',nSelAtoms1
        self.Connectivity.AddTrace(self.parent.molHnd,selAtoms1,nSelAtoms1, defn[1])
        self.parent.molHnd.DeleteSelection(selHnd1)
      else:
        # No limiting selection so use all atoms in the display object
        #print 'update_connectivity nSelAtoms',nSelAtoms
        self.Connectivity.AddTrace(self.parent.molHnd,selAtoms,nSelAtoms, defn[1])

    self.do_update_connectivity = 0
    #print 'update_connectivity',self.name,self.Connectivity.GetNumberOfAtoms(),time.clock()-start_time

#-----------------------------------------------------------------------
  def set_do_redraw(self,exit='',max_atoms='',**keywords):
#-----------------------------------------------------------------------
    self.do_redraw = 1

#-----------------------------------------------------------------------
  def unthreadedDraw(self):
#-----------------------------------------------------------------------
    self.draw_text()
    self.draw_pick_text()
    #print 'MolDisp.unthreadedDraw', self.do_rebuildGL
    
#-----------------------------------------------------------------------
  def draw_text(self):
#-----------------------------------------------------------------------
    #print 'MolDisp.unthreaded_draw',self.do_redraw,self.label.do_redraw
    rebuild = 0
    if self.reapply_selection>0:
      selection_changed=self.apply_selection()

    if self.label and self.label.do_redraw:
      # Redo the picked labels if they have been trashed by the MolLabel.draw
      if not self.label_graphmod:
        import graphicsmodel
        self.label_graphmod = graphicsmodel.graphicsmodel()
        BUILD().append(self.label_graphmod)
      rebuild = self.label.draw()
    if rebuild: self.do_rebuildGL.append('label_graphmod')
    return rebuild
  
#-----------------------------------------------------------------------
  def clearPickedAtoms(self):
#-----------------------------------------------------------------------
    import cprim
    for uid,label_ob in self.pickedAtomsLabel.items():
      cprim.DeleteTextLabel(self.pick_label_graphmod.obj,label_ob.GetID())
    self.pickedAtomsLabel = {}
    self.pickedAtoms = {}
    self.pickedAtomsMatrices = {}
    self.pickedAtomsUserMoveMatrices = {}

#-----------------------------------------------------------------------
  def draw_pick_text(self):
#-----------------------------------------------------------------------      
    import pygl_coord,cprim,TextLabel

    if not self.redraw_picked_labels: return 0

    for uid,label_ob in self.pickedAtomsLabel.items():
      cprim.DeleteTextLabel(self.pick_label_graphmod.obj,label_ob.GetID())
    self.pickedAtomsLabel = {}
    font = FONTMANAGER().getFont('pick_atom_label')

    if not self.pick_label_graphmod:
      import graphicsmodel
      self.pick_label_graphmod = graphicsmodel.graphicsmodel()
      BUILD().append(self.pick_label_graphmod)
    for uid,atm_ptr in self.pickedAtoms.items():
      pos = pygl_coord.Cartesian(atm_ptr.x,atm_ptr.y,atm_ptr.z)
      if self.pickedAtomsMatrices.has_key(uid) and self.pickedAtomsMatrices[uid]:
        pos = self.pickedAtomsMatrices[uid]*pos
      if self.pickedAtomsUserMoveMatrices.has_key(uid) and self.pickedAtomsUserMoveMatrices[uid]:
        pos = self.pickedAtomsUserMoveMatrices[uid]*pos
      text_label = PICKLABEL().getAtomLabel(atm_ptr )
       
      #print "MolDisp.draw_pick_text text_label",text_label

      #label_ob = cprim.Text(pygl_coord.Cartesian(atm_ptr.x,atm_ptr.y,atm_ptr.z),text_label,
        #                    pygl_coord.Cartesian(atm_ptr.x,atm_ptr.y,atm_ptr.z))


      label_ob = TextLabel.CreateTextLabel(text_label, pos, pos,font)
      glWidget = GLWIDGETS()[0]
      if glWidget and hasattr(glWidget,"getBackground") and callable(glWidget.getBackground):
        bgcol = glWidget.getBackground()
        bright_y = bgcol[0]*0.299 + bgcol[1]*0.587 + bgcol[2]*0.114;
        if bright_y <0.5:
          label_ob.SetColour(1,1,1,1)
        else:
          label_ob.SetColour(0,0,0,0)

      #print 'MolDisp.drawPickedLabels',label_ob,self.graphmod.obj
      self.pickedAtomsLabel[uid] = label_ob
      self.pick_label_graphmod.obj.add_text_primitive(label_ob)

    self.redraw_picked_labels = 0
    #print 'MolDisp.draw_pick_text', self.pickedAtomsLabel
    self.do_rebuildGL.append('pick_label_graphmod')

    return 1
  
#------------------------------------------------------------------------
  def draw (self):
#------------------------------------------------------------------------
    '''
    Called at end of each cycle of a command loop
    Redraw the graphmod if necessary
    '''   

    start_time = time.time()
    # Get the build module to build this object
    #print "MolDisp.draw",self.name,self.reapply_selection,self.do_redraw
    selection_changed = 0
    colour_changed=0

    pm = self.getDrawingStylePM()
    
    if self.reapply_selection>0:
      selection_changed=self.apply_selection()
      if selection_changed: self.do_redraw=1

    #print 'MolDisp.draw',self.name,self.do_redraw,self.parent.do_update_all_bonded_atoms,self.parent.changedSplines,self.parent.changedConnected
    if not self.ignore_other_dispobjs and (self.parent.do_update_all_bonded_atoms or self.parent.changedSplines or self.parent.changedConnected):
      self.parent.update_all_bonded_atoms()
      #print "from update_all_bonded_atoms"
       
    if self.do_update_connectivity:
        self.update_connectivity()
    
    if self.model_colour.do_recolour:
      colour_changed = self.model_colour.apply_colour()
      #print "MolDisp.draw colour_changed",colour_changed
      self.do_redraw=1
      
    if ['SPHERES','BALLSTICK','CIRCLES','IMPOSTER_SPHERES'].count(self.style_mode):
        if self.update_atomRadii and self.SelHandle.getSelHnd()>=0:
          if self.atomRadii: del self.atomRadii
          if self.style_mode == "SPHERES" or self.style_mode == "IMPOSTER_SPHERES":
            scale = pm.get('sphere_radii_scale')
          elif self.style == "BALLSTICK" or self.style == 'CIRCLES':
            scale = pm.get('ball_radii_scale')
          self.atomRadii=self.parent.molHnd.GetAtomRadii(self.SelHandle.getSelHnd(),VDWRADIUS,scale)
          for gobj_name in ['graphmod','external_bonds_graphmod']:
            gobj = getattr(self,gobj_name,None)
            if gobj: gobj.setparams(atomRadii=self.atomRadii)
          self.update_atomRadii=0
          
    if ['ANISOU'].count(self.style_mode):
      if self.atomRadii: del self.atomRadii
      style_code = MolDisp.anisou_style_code[MolDisp.anisou_style_alias.index(pm.get('anisou_style'))]
      scale=1.0
      self.atomRadii=self.parent.molHnd.GetAtomRadii(self.SelHandle.getSelHnd(),VDWRADIUS,scale)
      for gobj_name in ['graphmod','external_bonds_graphmod']:
            gobj = getattr(self,gobj_name,None)
            if gobj:
              gobj.setparams(anisou_style=style_code,anisou_scale=pm.get('anisou_scale'))
              gobj.setparams(atomRadii=self.atomRadii)
      

    if self.do_redraw or self.do_redraw_external:
      #print "MolDisp.draw",self.name,self.do_redraw,self.do_redraw_external
      # Update graphmod parameters
      nSelAtoms,selAtoms = self.SelHandle.getSelIndex(always=1)
      #print "MolDisp.draw nSelAtoms",nSelAtoms,selAtoms
      selHnd = self.SelHandle.getSelHnd(always=1)
      
      if self.model_colour.stick_colour == 'ATOM':
        stick_colour_code = -1
      else:
        stick_colour_code = MGCOLOUR().index(self.model_colour.stick_colour)

        
      import cprim
      do_redraw = 0
      for gobj_name,do_redraw_name,external_bonds_mode in [['graphmod','do_redraw',cprim.DRAW_INTERNAL_BONDS],
                                   ['external_bonds_graphmod','do_redraw_external',cprim.DRAW_EXTERNAL_BONDS]]:
        do_redraw = max(do_redraw,getattr(self,do_redraw_name,0))

      for gobj_name,do_redraw_name,external_bonds_mode in [['graphmod','do_redraw',cprim.DRAW_INTERNAL_BONDS],
                                   ['external_bonds_graphmod','do_redraw_external',cprim.DRAW_EXTERNAL_BONDS]]:
        #do_redraw = getattr(self,do_redraw_name,0)
        gobj = getattr(self,gobj_name,None)
        if gobj:
          if do_redraw:
            gobj.setparams(stick_colour=stick_colour_code)

            gobj.setparams(selHnd=selHnd, \
                              selAtoms = selAtoms, \
                              nSelAtoms = nSelAtoms, \
                              com = self.com, \
                              origin = self.origin, \
                              style = self.style, \
                              visible = self.visible, \
                              update_surface = self.update_surface,
                  atomColourVector = self.model_colour.atomColourVector, \
                  ifColourContinuous = self.model_colour.ifColourContinuous,
                           external_bonds_mode= external_bonds_mode,
                        side_to_ribbon=self.parent.spline_selHnd,
                        side_to_worm=self.parent.worm_selHnd,
                        splineinfo_ribbon=self.parent.splineinfo_ribbon,
                        splineinfo_worm=self.parent.splineinfo_worm
                  )

            if not self.built.count(gobj_name):
              BUILD().append(gobj)
              gobj.obj.SetAlpha(self.opacity)
              self.built.append(gobj_name)
              if self.doBiomolecule:
                self.graphmod.obj.SetSymmetryMatrices([])
                if not self.ignore_symmetry:
                  for gl in MAINWINDOW().glWidgets:
                    gobj.SetupSymmetry(gl.rpos,gl.extents())
            else:
              #print "before graphmod.rebuild",self.name, time.time()-start_time
              #gobj.diagnostic_timing = 1
              gobj.rebuild()
              #print "after graphmod.rebuild",self.name, time.time()-start_time
              gobj.obj.SetAlpha(self.opacity)
              #print "after SetAlpha",self.name, time.time()-start_time
              if not self.ignore_symmetry:
                for gl in MAINWINDOW().glWidgets:
                  gobj.SetupSymmetry(gl.rpos,gl.extents())
              #print "after SetupSymmetry",self.name, time.time()-start_time
              # This forces recalculation of connections of side-chains, SS bridges, etc. to worm.
              self.set_visibility(toggle_visibility=1)
              self.set_visibility(toggle_visibility=1)
            self.do_rebuildGL.append(gobj_name)

            
    if self.do_update_opacity:
      for gobj_name in ['graphmod','external_bonds_graphmod']:
        gobj = getattr(self,gobj_name,None)
        if gobj:
          gobj.obj.SetAlpha(self.opacity)
          if not self.do_rebuildGL.count(gobj_name): self.do_rebuildGL.append(gobj_name)

          
    self.do_redraw = 0
    self.do_redraw_external = 0
    self.do_update_opacity = 0

    #print 'MolDisp.draw',self.parent.name,self,do_updatedisplay
    return 0

#-----------------------------------------------------------------------
  def set_extent(self):
#-----------------------------------------------------------------------
    self.extent = [0.0,0.0,0.0,0.0,0.0,0.0]
    if self.SelHandle.getNofAtoms() > 0:
      extptr = self.parent.molHnd.Extent(self.SelHandle.getSelHnd())
      tmp = doublea(1)
      ext=tmp.frompointer(extptr)
      if ext:
        self.extent = []
        for i in range(0,6): self.extent.append (ext[i])
      delrp(extptr)
    #print "extent",sel.extent
    self.origin = [ \
           (self.extent[3] + self.extent[0])/2.0,
           (self.extent[4] + self.extent[1])/2.0,
           (self.extent[5] + self.extent[2])/2.0 ]
    #print "sel.origin",sel.origin
    self.com = [0.0,0.0,0.0]


#----------------------------------------------------------------
  def set_graphmod_visibility(self):
#----------------------------------------------------------------
    vis = DispObj.set_graphmod_visibility(self)
    if self.pick_label_graphmod: self.pick_label_graphmod.obj.visible = vis
    if self.external_bonds_graphmod: self.external_bonds_graphmod.obj.visible = vis
    return vis
  
#----------------------------------------------------------------
  def handle_moving_data(self,attribute='',master='',**keywords):
#----------------------------------------------------------------
    #print 'MolDisp.handle_moving_data',self
    if self.style == 'SURFACE':
      if attribute == 'transform':
        #self.set_visibility(visibility=0)
        self.reapply_selection=1
      elif attribute == 'reconfigure':
        self.reapply_selection=1
    else:
      self.do_redraw = 1

#-----------------------------------------------------------------
  def update_data_status(self,shadowdisp):
#-----------------------------------------------------------------
    import utils
    rv = 0
    edited_list = []

    diffs = utils.dict_diffs(self.selparams(),shadowdisp.selparams)[1]
    #print "MolDisp.update_data_status selparams diffs",diffs
    if diffs:
      self.SelHandle.setSelParams(shadowdisp.selparams)
      self.reapply_selection = 1
      rv = 1

    diffs = utils.dict_diffs(self.colparams(),shadowdisp.colparams)[1]
    if diffs:
      self.model_colour.set_colour(colparams=diffs)
      rv = 1
        
    diffs =  utils.dict_diffs(self.styleparams(),shadowdisp.styleparams)[1]
    if diffs:
      self.set_style(styleparams=diffs)
      self.do_redraw = 1
      rv = 1
      
    rv0,diffs,undiffs = utils.dict_diffs(self.params(),shadowdisp.params)
    #print "MolDisp update_data_status diffs",diffs
    if diffs:
      if diffs.has_key('label_params'):
        self.label.update_data_status(diffs['label_params'])
        rv = 1
      if diffs.has_key('drawing_style'):
        #Add a drawing style PM
        pm = self.getDrawingStylePM(name=diffs['drawing_style'],custom=1)
        #print "diffs drawing_style",pm.name,pm.params
        pm.add_dependent(self.handleDrawingStylePM)
        for gobj_name in ['graphmod','external_bonds_graphmod']:
          gobj = getattr(self,gobj_name,None)
          if gobj: gobj.setparams(parameters=pm)
        self.do_redraw = 1
        HISTORY().update_gui.append(['add_PM',diffs['drawing_style']])
        rv = 1
    if undiffs.has_key('drawing_style'):
      #print "undiffs drawing_style"
      #Remove a drawing style PM
      HISTORY().update_gui.append(['delete_PM',undiffs['drawing_style']])
      self.closeDrawingStylePM()
      self.do_redraw = 1
      rv = 1

    if rv: self.update_gui.append(['update'])
    rv = max(rv, DispObj.update_data_status0(self,shadowdisp))
    return rv



  def addPickedAtom(self,uid,atm_ptr,matrix=None):
    x,y,z = atm_ptr.x,atm_ptr.y,atm_ptr.z
    for uid1,pa in self.pickedAtoms.items():
       x1,y1,xz = pa.x,pa.y,pa.z
       if abs(x-x1)<1e-5 and abs(y-y1)<1e-5 and abs(y-y1)<1e-5:
          print "Already have a pick label here"
          self.removePickedAtom(uid1)
          self.redraw_picked_labels = 1
          return uid1
     
    self.pickedAtoms[uid] = atm_ptr
    self.pickedAtomsMatrices[uid] = matrix
    self.redraw_picked_labels = 1
    return 0
    #print 'MolDisp.addPickedAtom', self.pickedAtoms, self
    
  def removePickedAtom(self,uid):
    import cprim
    if self.pickedAtomsLabel.has_key(uid):
      cprim.DeleteTextLabel(self.pick_label_graphmod.obj,self.pickedAtomsLabel[uid].GetID())
      del self.pickedAtomsLabel[uid]
    if self.pickedAtomsMatrices.has_key(uid):
      del self.pickedAtomsMatrices[uid]
    if self.pickedAtomsUserMoveMatrices.has_key(uid):
      del self.pickedAtomsUserMoveMatrices[uid]
    if self.pickedAtoms.has_key(uid):
      del self.pickedAtoms[uid]
    self.redraw_picked_labels = 1

    #cprim.DeleteTextLabel(obj,label_ob.GetID())





#*************************************************************************
# Beware the following are functions
#*************************************************************************

def convertSecstrCode(inp=''):
  import types
  if isinstance(inp,types.StringType):
    if MolData.secstr_menu.count(inp):
      return MolData.secstr_enum[MolData.secstr_menu.index(inp)]
    else:
      return -2
  else:
    if MolData.secstr_enum.count(inp):
      return MolData.secstr_menu[MolData.secstr_enum.index(inp)]
    else:
      return ' '
   
#---------------------------------------------------------------------------------
def createDrawingStylePM(name='',copy_from=''):
#---------------------------------------------------------------------------------
  import services

  import re
  title='Model drawing style '+re.sub('model_drawing_style','',name)
  
  pm = services.ParamsManager ( 
          name=name,
          #custom_gui = graphics_draw_gui,
          picture_definition = 1,
          copy_from=copy_from,
          help="drawing_styles",
          c_access= 1,
          title=title,
          gui=['bond_width','fat_bond_width','thin_bond_width',
               'dashed_bonds','dashed_bond_length','show_multiple_bonds','deloc_ring',
               'cylinder_width',
               'ballstick_stick','ball_radii_scale',
               'sphere_radii_scale','nucleic_stick_width','base_block_thickness',
               'alpha_helix_width','ribbon_width','arrow_width','worm_width','helix_tube_diameter',
               'ribbon_style','helix_style',
               'flatten_loop','loop_frac','flatten_beta','two_colour_ribbon','grey_ribbon_edge',
               'anisou_style','anisou_scale','anisou_scale_byvdw','trace_cutoff'],
      default = {
         'bond_width' : 2,
         'fat_bond_width' : 4,
         'thin_bond_width' : 1,
         'cylinder_width' : 0.2,
         'nucleic_stick_width' : 0.4,
         'ballstick_stick' : 0.1,
         'ribbon_width' : 2.5,
         'alpha_helix_width' : 1.5,
         'helix_tube_diameter' : 1.5,
         'arrow_width' : 2.2,
         'arrow_length' : 2.2,
         'worm_width' : 0.2,
         'ribbon_style' : 0,
         'two_colour_ribbon' : 0,
         'grey_ribbon_edge' : 0,
         'helix_style' : 0,
         'flatten_loop' : 0,
         'loop_frac' : 1,
         'flatten_beta' : 1,
         'deloc_ring' : 0,
         'show_multiple_bonds' : 0,
         'anisou_scale_byvdw' : 0,
         'sphere_radii_scale' : 0.8,
         'ball_radii_scale' : 0.2,
         'spline_beta_flat' : 1,
         'dashed_bonds' : 0,
         'dashed_bond_length' : 0.3,
         'pyramid_size' : 0.1,
         'anisou_style' : 'solidaxes',
         #'anisou_style' : 0,
         'anisou_scale' : 1.0,
         #'trace_selection' : 'amino_acid',
         #'trace_atom' : 'CA',
         'trace_cutoff' : 4.8,
         'base_block_thickness' : 0.2 }, 
      definition = {'bond_width' : dict(type=int,label='Bond width',style='spinbox',
                                         min=1,max=10),
             'fat_bond_width' : dict(type=int,label='Fat bond width',style='spinbox',
                                        min=1,max=10),
             'thin_bond_width' : dict(type=int,label='Thin bond width',style='spinbox',
                                        min=1,max=10),
             'dashed_bonds' : dict(type=int,label='Dashed bonds',style='checkbox'),
             'dashed_bond_length' :  dict(type=float,label='Dash length for dashed bonds',
                                          style='spinbox',max=1.0,min=0.0,step=0.02),
             'sphere_radii_scale' : dict(type=float,label='Sphere radius = VDW radii x',
                                      style='spinbox',max=5.0,min=0.0,step=0.02),
             'ball_radii_scale' :  dict(type=float,label='Ball radius = VDW radii x',
                                        style='spinbox',max=5.0,min=0.0,step=0.02),
             'ballstick_stick' : dict(type=float,label='Ball cylinder thickness',
                                        style='spinbox',max=5.0,min=0.0,step=0.02),
             'worm_width': [float,''],
             'ribbon_style': dict(type=int,label='Nucleic acid/strand ribbon style',
                  style='combobox',menu=['Oval','Flat','Flat/round'],alias=[0,1,2]),
             'helix_style': dict(type=int,label='Helix ribbon style',style='combobox',
                   menu=['Oval','Flat','Flat/Round','Fancy'],alias=[0,1,2,3]),
             'two_colour_ribbon': dict(type=int,label='Helix colouring',style='combobox',
                  menu=['Normal','Inside grey'],alias=[0,1]),
             'grey_ribbon_edge': dict(type=int,label='Ribbon colouring',style='combobox',
                  menu=['Normal','Edges grey'],alias=[0,1]),
             'cylinder_width': dict(type=float,label='Cylinder thickness (Angstrom)',
                                    style='spinbox',max=5.0,min=0.0,step=0.02),
             'nucleic_stick_width': dict(type=float,label='Nucleic base stick thickness (A)',
                                      style='spinbox',max=5.0,min=0.0,step=0.02),
             'alpha_helix_width': dict(type=float,label='Protein ribbon width',
                                      style='spinbox',max=10.0,min=0.0,step=0.02),
             'arrow_width': dict(type=float,label='Beta strand arrrow width',
                                      style='spinbox',max=10.0,min=0.0,step=0.02),
             'ribbon_width':  dict(type=float,label='Nucleic acid ribbon width',
                                   style='spinbox',max=10.0,min=0.0,step=0.02),
             'flatten_loop' : dict(type='bool',label='Shorten loops',style='checkbox'),
             'loop_frac': dict(type=float,label='Degree of loop shortening (0-1)',
                                    style='spinbox',max=1.0,min=0.0,step=0.02),
             'flatten_beta' : dict(type='bool',label='Smooth beta-sheets',style='checkbox'),
             'show_multiple_bonds' : dict(type=bool,label='Show multiple-bonds',style='checkbox'),
             'deloc_ring' : dict(type=bool,label='Draw deloc. circle in aromatic rings',style='checkbox'),
             'worm_width':  dict(type=float,label='Worm width',
                                 style='spinbox',max=10.0,min=0.0,step=0.02),
             'helix_tube_diameter':  dict(type=float,label='Helix tube diameter',
                                 style='spinbox',max=10.0,min=0.0,step=0.2),
             'spline_beta_flat':  dict(type=int),
             'pyramid_size' :  dict(type=float,label='Pyramid size',
                                    style='spinbox',max=10.0,min=0.0,step=0.02),
              'anisou_style' : dict (type=str,label='Thermal ellipse style',style='combobox',
                 menu = ['Axes','Solid','Solid&Axes'],alias = ['axes','solid','solidaxes']),
              'anisou_scale' : dict (type=float,label='Thermal ellipse scale',style='spinbox',
                         max=10.0,min=0.0,step=0.05),
             'anisou_scale_byvdw' : dict(type='bool',label='Scale ellipses by VDW radii',style='checkbox'),
              #'trace_selection' : dict ( type=str, label = "'CA' trace selection"),
              #'trace_atom' : dict ( type=str, label = "'CA' trace atom"),
              'trace_cutoff' : dict (type=float,label="'CA' trace cutoff distance",style= 'spinbox',
                                     max=20.0,min=0.0,step=1.0),
              'base_block_thickness':  dict(type=float,label='Base block thickness',
                                 style='spinbox',max=10.0,min=0.0,step=0.05),

             } )


  return pm

#---------------------------------------------------------------------------------
def createRenderParamsManager():
#---------------------------------------------------------------------------------
  import services
 
  pm = services.ParamsManager (
          name="render_quality",
          picture_definition = 1,
          help="drawing_styles#render_quality",
          title='Drawing quality', 
          c_access= 1,
          gui=['solid_quality',
               'smooth_line', 
               'use_vbo',
               'force_use_va'],
      default = { 'solid_quality' : 1,
                  'smooth_line' : 1,
                  'use_vbo' : 1,
                  'force_use_va':0 },
      definition = {
             'solid_quality' : dict(type=int,label='Solid objects drawing quality',
                     style='combobox',menu=['Fast','Smooth','Deluxe'],alias=[0,1,2]),
             'smooth_line' : dict(type=bool, label='Anti-alias lines', style='checkbox' ),
             'use_vbo' : dict(type=bool, label='Use vertex buffer objects', style='checkbox' ),
             'force_use_va' : dict(type=bool, label='Force Vertex Arrays on Win32/Intel', style='checkbox' )
             })

  return pm

def handleRenderParamsManager(updated={},**keywords):
  # Called on changing RenderParamsManager param - redraw all MolDisp
  for disp in get_dispobj(object_type='MolDisp',mode='all'):
      disp.do_redraw = 1

  return 0

#-----------------------------------------------------------------------
def strip_html_from_PDB(filename=''):
#-----------------------------------------------------------------------
  '''
  Strip html tags from PDB that might have come from doing 'Save to'
  in web browser viewing EBI
  '''
  
  import re
  import string
  import os


  #print "Stripping html tags from filename",filename
  #print "Copying original file to",filename+'.html'
  os.rename(filename,filename+'.html')
  try:
    f = open(filename+'.html','r')
  except:
    return [1,'Error opeing file: '+filename+'.html']
  l = f.read()
  f.close()

  # Extract everything between <pre> and </pre)
  t =  re.search('(.+)<pre>(.+)</pre>(.+)',l,re.DOTALL)
  if not t:
    return [2,'Error attempting to find <pre> tags in file:\n',filename]

  l = t.groups()[1]

  # Separate out the ATOM records to speed up searching
  n = string.find(l,'\nATOM')
  #print n
  if n>0:
    l1 =  l[0:n]
  else:
    l1 = l
    
  #Remove the </a>
  t =  re.search('(.+)</a>(.+)',l1,re.DOTALL)
  while t:
    #print "removing </a>"
    l1 = t.groups()[0] + t.groups()[1]
    t =  re.search('(.+)</a>(.+)',l1,re.DOTALL)

  # Remove the <a href..... >
  t = re.search("(.+)<a href(.+)>(.+)",l1,re.DOTALL)
  while t:
    #print 'removing <a href'+t.groups()[1]+'>'
    l1 = t.groups()[0] + t.groups()[2]
    t = re.search("(.+)<a href(.+)>(.+)",l1,re.DOTALL)

  try:
    f = open(filename,'w')
    if n>0:
      f.write(l1+l[n:])
    else:
      f.write(l1)
    f.close
  except:
    return [3,'Error attempting to write cleaned up file:\n',filename]

  return [0,'']

def list_user_colour_rules():
  for obj in MolData.insts:
    print "**",obj.name
    if hasattr(obj.objinsts[0],'colour_rules'):
      print obj.objinsts[0].colour_rules[1][0]
    #print obj.objinsts[0].user_colour_objects

    
