"""
     python/ui/dataobj.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.
"""
import sys,os
import traceback
from global_definitions import *
import version

DATAOBJ_TYPES = ['MolData','MapData','FieldData','VectorsData','GL2dScreen','CrystalData']
PLUGIN_DATAOBJ_TYPES = []


    
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------

class Dependency:


  valid_attributes =  ['selection','colour','visibility','create','delete',
                       'rename', 'transform','reconfigure','edit']
  
  def __init__(self):

    #print "init Dependency",self
    # For the master object
    # dependents[attribute][object] = callback method
    self.dependents = {}

    # For dependent object
    # dependencies[master] = list of attributes
    self.dependencies = {}

#-------------------------------------------------------------------
  def delete(self):
#-------------------------------------------------------------------
    # A derived object is being deleted so clean up..

    # inform any object which is dependent on this one
    self.update_dependents('delete')
    # relieve any object of responsibility for this one
    self.remove_all_dependency()

#---------------------------------------------------------------------
  def add_dependency(self,master='',attributes='',handlers='',
                     callback=''):
#---------------------------------------------------------------------
    '''
    Add a dependency
    master = the master object on which this object is dependent
    attributes = list of the masters attributes that this is dependent on
    handlers = list of methods of this to be called when master 
               attribute changes
    callback = string which is can be executed in global context
               to identify the master object.  This is not
               essential input for any DispObj or DataObj
    '''

    import types
    # Is there already some dependency registered?
    if self.dependencies.has_key(master):
      dependencies =  self.dependencies[master]
    else:
      dependencies = []

    # Add each new dependency to the dependencies list and
    # also to the appropriate attribute dependents list in
    # the master object

    i = -1
    for att in attributes:
      i = i + 1;
      if not self.valid_attributes.count(att): break

      if not dependencies.count(att):
        dependencies.append(att)
        #print "add_dependency master",master
        if isinstance(master,types.ClassType):
          if master == DispObj:
            if not  DispObj.class_dependents.has_key(att):
              DispObj.class_dependents[att]={}
            DispObj.class_dependents[att][self] = handlers[i]
            #print "add_dependency DispObj",self.dependencies, DispObj.class_dependents
          else:
            if not  master.class_dependents.has_key(att): master.class_dependents[att]={}
            #master.class_dependents[att][self] = callback+handlers[i]
            master.class_dependents[att][self] = handlers[i]
            #print "add_dependency class",self.dependencies, master.class_dependents
        else:
          if not  master.dependents.has_key(att): master.dependents[att]={}
          if len(handlers)>i and handlers[i]:
            master.dependents[att][self] = handlers[i]
          else:
            master.dependents[att][self] = ''
          #print "add_dependency",self.dependencies, master.dependents
    self.dependencies[master] = dependencies


#---------------------------------------------------------------------
  def remove_dependency(self,master,attributes=[]):
#---------------------------------------------------------------------
    # Are there dependencies registered?
    if self.dependencies.has_key(master):
      dependencies =  self.dependencies[master]
    else:
      return

    # If no input attributes then assume to remove all
    if len(attributes) == 0: attributes = dependencies

    # Remove specified dependency from the dependencies list and
    # from the appropriate attribute dependents list in
    # the master object

    import types
    for att in attributes:
      if dependencies.count(att):
        dependencies.remove(att)
        #exec "del master."+att+"_dependents[self]"
        if isinstance(master,types.ClassType) and \
                 hasattr(master,'class_dependents') and \
                 master.class_dependents.has_key(att) and \
                 master.class_dependents[att].has_key(self):
          del master.class_dependents[att][self]
        if hasattr(master,'dependents') and \
               master.dependents.has_key(att) and \
               master.dependents[att].has_key(self):
          del master.dependents[att][self]
        
    if len(dependencies):
      self.dependencies[master] = dependencies
    else:
      del self.dependencies[master]

#---------------------------------------------------------------------
  def remove_all_dependency(self):
#---------------------------------------------------------------------
    '''
    Remove this object from all dependent lists - probably
    because we are deleting this object
    '''
    import types
    for master in self.dependencies.keys(): 
      if hasattr(master,'class_dependents'):
        for att in self.dependencies[master]:
          if  master.class_dependents.has_key(att) and \
              master.class_dependents[att].has_key(self):
            del master.class_dependents[att][self]
      if  hasattr(master,'dependents'):
        for att in self.dependencies[master]:
          if master.dependents.has_key(att) and \
               master.dependents[att].has_key(self):
            del master.dependents[att][self]
          
      del self.dependencies[master]


#--------------------------------------------------------------------
  def update_dependents(self,attribute):
#--------------------------------------------------------------------
    '''
    This method will be called by the master object to update its  dependents
    '''
    import map
    import model

    #print "into update_dependents ",self,attribute,self.dependents
    if not self.valid_attributes.count(attribute): return

    if hasattr(self,'class_dependents'):
        #print "class_dependents",self.class_dependents
        if self.class_dependents.has_key(attribute):
          for obj,cmd in self.class_dependents[attribute].items():
            if cmd:
              try:
                exec cmd+"(master='"+self.name+"',attribute='"+attribute+"')"
              except:
                pass
    if isinstance(self,DispObj):
        #print "DispObj class_dependents",DispObj.class_dependents
        if DispObj.class_dependents.has_key(attribute):
          for obj,cmd in DispObj.class_dependents[attribute].items():
            #print "trying",DispObj.class_dependents[attribute][obj]
            try:
              apply(DispObj.class_dependents[attribute][obj],[], \
                  {'master' : self.name, 'attribute':attribute } )
            except:
              pass
      
    if hasattr(self,'dependents') and self.dependents.has_key(attribute):
      for obj in self.dependents[attribute].keys():
        #print "updating obj",attribute,obj,self.dependents[attribute][obj]
        if DEVELOPER():
          #print "apply",obj,self.dependents[attribute][obj]
          #apply(self.dependents[attribute][obj],[], \
          #      {'master' : self.name, 'attribute':attribute } )
          apply(self.dependents[attribute][obj],[], \
                {'master' : self, 'attribute':attribute } )
        else:
          try:
            #apply(self.dependents[attribute][obj],[], \
            #    {'master' : self.name, 'attribute':attribute } )
            apply(self.dependents[attribute][obj],[], \
                {'master' : self, 'attribute':attribute } )
          except:
            pass
#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------
# The dataobj class
#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------

class DataObj(Dependency):
  
  dataobj_names = []
  dataobj_insts = []
  moldata_order = ['MolDisp','SurfaceDispobj','HBonds','Contacts','AnisoU','Annotation']
  
#------------------------------------------------------------------
  def __init__(self,type='',name='',filename='',module={},dataparams={}):
#------------------------------------------------------------------
    #print "DataObj dataparams",name,dataparams,filename
    import os
    import utils
    self.ccp4mg_creator_version = version.ccp4mg_version
    self.name = name
    self.filename = ['','','']
    self.visible = 1
    self.expanded = 1
    self.update_gui = []
    self.moving = 0

    if dataparams:
      for item in dataparams.keys(): 
        setattr(self,item,dataparams[item])
    if filename: self.filename = utils.check_filename(filename)
    #if filename: self.filename = os.path.abspath(filename)
    if type: self.object_type = type
    if module: self.module = module
    # Derive a unique name
    #print "DataObj.init self.filename",self,self.filename
    if not name:
      name = os.path.basename( os.path.splitext(self.filename[2])[0])
      if not name:
        name = name + self.object_type
    self.uniquename(name)
    
    # Initiallise the visiblity and objects data
    self.objnames = []
    self.objinsts = []
    Dependency.__init__(self)

#------------------------------------------------------------------
  def renameuniquename(self,name,oldname):
#------------------------------------------------------------------
    if not oldname or name==oldname:
      return

    oldidx = DataObj.dataobj_names.index(oldname)
    # find a name unique across all data objects
    import re,string
    # Make sure no dots that will break Tcl
    name = re.sub('\.','_',name)
    # -- or arithmetic symbols that will break python when they
    # appear in the keyword 
    name = re.sub('-|\+|\/|%|\*','_',name)
    self.name_label = name
    # Beware picture definition may use name as python variable
    # --- not currently done
    # if string.find(string.digits,name[0:1])>0: name = '_'+name
    if DataObj.dataobj_names.count ( name ) <= 0:
      nname = name
    else:
      cont = 1
      n = 0
      while cont:
        n = n + 1
        nname = name + "_" + str(n)
        if DataObj.dataobj_names.count ( nname ) <= 0: cont = 0

    #print "Renaming",self.name,"to",nname
    self.name = nname
    # append to list of all data objects
    DataObj.dataobj_names[oldidx] = nname
    return nname
 
#------------------------------------------------------------------
  def uniquename(self,name,oldname=""):
#------------------------------------------------------------------
    # find a name unique across all data objects
    import re,string
    # Make sure no dots that will break Tcl
    name = re.sub('\.','_',name)
    # -- or arithmetic symbols that will break python when they
    # appear in the keyword 
    name = re.sub('-|\+|\/|%|\*','_',name)
    self.name_label = name
    # Beware picture definition may use name as python variable
    # --- not currently done
    # if string.find(string.digits,name[0:1])>0: name = '_'+name
    if DataObj.dataobj_names.count ( name ) <= 0:
      nname = name
    else:
      cont = 1
      n = 0
      while cont:
        n = n + 1
        nname = name + "_" + str(n)
        if DataObj.dataobj_names.count ( nname ) <= 0: cont = 0

    self.name = nname
    # append to list of all data objects
    DataObj.dataobj_names.append (nname )
    DataObj.dataobj_insts.append (self)
    return nname

  def get_label(self):
    return self.name_label

#------------------------------------------------------------------
  def delete(self,**keywords):
#------------------------------------------------------------------

    # delete child (disp) objects
    if len (self.objinsts) > 0:
      #Make an independent list or Python get confused by
      #items disappearing from objinst
      llen = len( self.objinsts)
      for ii in range(llen-1,-1,-1):
        #print "DataObj.delete deleting",ii,self.objinsts[ii]
        self.objinsts[ii].delete()

      #print "remaining objinsts",self.objinsts
      

    Dependency.delete(self)
    # remove from the global dataobj list
    if DataObj.dataobj_insts.count(self):
      i = DataObj.dataobj_insts.index(self)
      DataObj.dataobj_names.pop(i)
      DataObj.dataobj_insts.pop(i)

#------------------------------------------------------------------
  def getDependentsForDeletion(self):
#------------------------------------------------------------------
    return []

    
#------------------------------------------------------------------
  def dataparams(self,all=1):
#------------------------------------------------------------------
    # save the key data which is necessary when restoring the
    # object on restart or 'restore status'
    datapar = {}
    for item in ['module','object_type','filename','visible','ccp4mg_creator_version','expanded','customResCIFFiles']:
      if hasattr(self,item):
        datapar[item]=getattr(self,item)
    #print "dataparams",datapar
    return datapar

#-------------------------------------------------------------------
  def loaddata(self,**keywords):
#-------------------------------------------------------------------
    # loaddata should load the data from self.filename.  It should
    # not create any display objects.
    # The return tuple should be
    #  status 0=success, 1=failure
    #  message  (note a message can be set even if status is 0
    #   - this may happen if data file has been restored from backup)
    #  info - optional parameters whose interpretation is class dependent
    return [0,'']
  
#-----------------------------------------------------------------------
  def add_dispobj(self,dispobj):
#-----------------------------------------------------------------------
    # Support the dataobj icon menu by providing functionality
    # to add a new display object
    self.objnames.append(dispobj.name)
    self.objinsts.append(dispobj)
    #print "objnames",self.objnames

#--------------------------------------------------------------------
  def get_dispobj(self,object_type='',mode='all',name=''):
#--------------------------------------------------------------------
    import types
    if not object_type:
      obj_type = []
    if not isinstance(object_type,types.ListType) and object_type:
      obj_type = [object_type]
    else:
      obj_type = object_type

    if name:
      if self.objnames.count(name):
        return self.objinsts[self.objnames.index(name)]
      else:
        return ''
    else:
      ret = []
      for obj in self.objinsts:
        if not obj_type or  obj_type.count(obj.object_type):
          ret.append(obj)
          if mode == 'first': return ret
      return ret
    

#-------------------------------------------------------------------
  def getFilename(self):
#-------------------------------------------------------------------
   return self.filename[2]

#-------------------------------------------------------------------
  def setFilename(self,filename):
#-------------------------------------------------------------------
   import utils,os
   self.filename = utils.check_filename(filename)
   self.name_label = os.path.basename( os.path.splitext(self.filename[2])[0])
   
#-------------------------------------------------------------------
  def transform(self,dx='',dy='',dz='',rotx='',roty='',rotz=''):
#-------------------------------------------------------------------
    '''
    Handle translation commands from mouse movement
    '''
    pass
  
#-------------------------------------------------------------------------
  def set_visibility ( self,toggle_visibility='',visibility='',**keywords):
#-------------------------------------------------------------------------
    if toggle_visibility:
      vis = 1 - self.visible
    elif visibility != '':
      vis = int(visibility)
    else:
      return
    self.visible = vis

  def get_visibility(self):
    return self.visible

#-------------------------------------------------------------------------
  def set_expanded ( self,toggle_expanded='',expanded='',**keywords):
#-------------------------------------------------------------------------
    if toggle_expanded:
      expd = 1 - self.expanded
    elif expanded != '':
      expd = int(expanded)
    else:
      return
    self.expanded = expd

  def get_expanded(self):
    return self.expanded


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

#------------------------------------------------------------------
  def movie_interpolation(self,initial_status=None,final_status=None, \
                          fraction=0.0,step='',**keywords):
#------------------------------------------------------------------
    if initial_status and final_status:
      for item in final_status.objnames:
        if initial_status.objnames.count(item) and self.objnames.count(item):
          self.objinsts[self.objnames.index(item)].movie_interpolation( \
            initial_status=initial_status.objinsts[initial_status.objnames.index(item)], \
            final_status=final_status.objinsts[final_status.objnames.index(item)], \
            fraction=fraction,step=step )

#--------------------------------------------------------------------
  def isSameData(self,shadow_data):
#--------------------------------------------------------------------
   # print "dataobj isSameData",self.filename,shadow_data.dataparams['filename']
   if not hasattr(self,'filename') or not shadow_data.dataparams.has_key('filename'):
     return False
   elif self.filename == shadow_data.dataparams['filename']:
     return False
   else:
     return True

#---------------------------------------------------------------------
  def update_data_status (self,shadow_data,clear=1):
#---------------------------------------------------------------------
    #print "update_data_status",self,shadow_data,shadow_data.dataparams
    rv = 0
    if isinstance(shadow_data,DataObj):
      other_dataparams = shadow_data.dataparams()
    else:
      other_dataparams = shadow_data.dataparams
      
    if other_dataparams.get('visible',1) != getattr(self,'visible',1):
      rv = 1
      self.update_gui.append(['visible'])
      self.set_visibility(visibility=other_dataparams.get('visible',1))
      
    # Do we currently have some display obj which is not
    # in the updated shadow_data?
    delinsts_list = []

    for ii in range(0,len(self.objnames)):
      if not shadow_data.objnames.count(self.objnames[ii]):
        delinsts_list.append(self.objinsts[ii])

      else:
        shadow_disp = shadow_data.objinsts[ \
          shadow_data.objnames.index(self.objnames[ii])]
        if self.objinsts[ii].object_type != shadow_disp.object_type: \
          delinsts_list.append(self.objinsts[ii])
        else:
          rv0 = self.objinsts[ii].update_data_status(shadow_disp)
          if rv0:
            self.update_gui.append(['update_DispObj',self.objnames[ii]])
            rv = 1
          #print "after DispObj.update_data_status",self.objnames[ii],rv0
    
    #print "DataObj:update_data_status delete_list",self,delinsts_list
    if clear:
      for obj in delinsts_list:
        self.update_gui.append(['delete',obj.name])
        rv = 1
        obj.delete()
      del delinsts_list

    addinsts_list = []
    for ii in range(0,len(shadow_data.objnames)):
      if not self.objnames.count(shadow_data.objnames[ii]):
        addinsts_list.append(shadow_data.objinsts[ii])

    #print "DataObj:update_data_status add_list",addinsts_list
    for obj in addinsts_list:
      obj.restore(self.name)
      self.update_gui.append(['add',obj.name])
      rv = 1

    #print "DataObj.update_data_status",self.name,self.update_gui
    return rv

#-----------------------------------------------------------------
  def animate(self,shadow_data):
#-----------------------------------------------------------------
    if isinstance(shadow_data,DataObj):
      other_dataparams = shadow_data.dataparams()
    else:
      other_dataparams = shadow_data.dataparams
      
    if other_dataparams.get('visible',1) != getattr(self,'visible',1):
      rv = 1
      #self.update_gui.append(['visible'])
      self.set_visibility(visibility=other_dataparams.get('visible',1))
      
    for ii in range(0,len(self.objnames)):
      if shadow_data.objnames.count(self.objnames[ii]):
        shadow_disp = shadow_data.objinsts[shadow_data.objnames.index(self.objnames[ii])]
        if self.objinsts[ii].object_type == shadow_disp.object_type: \
          rv0 = self.objinsts[ii].animate(shadow_disp)


#-----------------------------------------------------------------
  def copy_data_files(self,project='',dir='',overwrite=1,extra_files=[]):
#-----------------------------------------------------------------
    '''
    Copy the data file(s) to another directory
    '''
    import utils,os,shutil
    copied = []
    errors = []
    if self.filename:
      filn = utils.get_filename(self.filename[0],self.filename[1])
      ret=utils.copy_data_file(filename=filn,dir=dir,overwrite=overwrite)
      errors.extend(ret[1])
      copied.extend(ret[2])
      if not ret[0] and project:
        self.dataparams['filename']=[project,filename[1],ret[1]]

      if self.object_type == 'MolData':
        mgfile = os.path.splitext(filn)[0]+'.ccp4mg'
        if os.path.exists(mgfile): extra_files.insert(1,mgfile)
        if hasattr(self,'animation') and self.animation:
          ret=anim.copy_data_files(project=project,dir=dir)
          copied.extend(ret[2])
                
    for filn in extra_files:
      ret=utils.copy_data_file(filename=filn,dir=dir,overwrite=overwrite)
      errors.extend(ret[1])
      copied.extend(ret[2])        

    print "copy_data_files",errors,copied
    return [len(errors),errors,copied]
    
#-------------------------------------------------------------------
class COneColour:
#-------------------------------------------------------------------

#-------------------------------------------------------------------
  def __init__(self,colour='',ifcomplement=1):
#-------------------------------------------------------------------
    self.colour=colour
    self.colour_label = ''
    self.ifcomplement = ifcomplement

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

#-----------------------------------------------------------------------
  def set_colour ( self,colour='',colparams='',force=0,**keywords):
#-----------------------------------------------------------------------
    #print "COneColour.set_colour",colour
    changed = 0
    if colparams:
      for item in colparams.keys(): 
        setattr(self,item,colparams[item])

    if colour == 'complement':
      self.colour = 'complement'
      self.colour_label = 'complement'
      self.do_recolour = 1
      if self.legend_visible: self.update_legend = 1
      return 1
    
    ii=  MGCOLOUR().colour_index(colour)
    #print "COneColour.set_colour",colour,ii
    if ii>=0:
      self.colour = colour
      self.colour_label = colour
      self.do_recolour = 1
      if self.legend_visible: self.update_legend = 1
      changed = 1
  
    return changed        

#-------------------------------------------------------------------
  def get_colour(self):
#-------------------------------------------------------------------
    return self.colour

#-------------------------------------------------------------------
  def handle_colour_change (self,name='',standard=0,rename='',delete=0):
#-------------------------------------------------------------------
    # Handle a redefinition of the colour with name 'name'
    # If the changed colour is one of the 'standard' colours
    # then just need to refresh the display
    # If the colour is user defined then it will only be used in
    # a user defined colour scheme - addition/deletion of colours
    # would require apply_colour
    #print "handle_colour_change",self,name,'*',rename,delete
    if rename:
      if self.colour == name:
        self.colour=rename
        self.colour_label = self.colour
    elif delete:
      if self.colour == name:
        self.colour='grey'
        self.colour_label = self.colour
    else:
      self.do_recolour = 1
      if self.legend_visible: self.update_legend = 1


#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------
class DataObjShadow:
#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------

  def __init__(self,name='',object_type='',filename='',objnames=[],objinsts=[],dataparams={}):
    self.name = name
    self.object_type = object_type
    self.filename=filename
    self.dataparams= {}
    self.dataparams.update(dataparams)
    self.objnames = objnames 
    self.objinsts = objinsts
    self.visible = 1
      
#----------------------------------------------------------------------
  def save(self,dataobj='',all=1,save_visible=0):
#----------------------------------------------------------------------
    # If the data has not been saved to file then don't attempt
    # to save the dataobj
    if not ['CrystalData','GL2dScreen'].count(dataobj.object_type):
      if not  dataobj.filename[0]: return 1
      self.filename = dataobj.filename
      
    # For legend/images check there are some objects which are not slaves
    #if dataobj.object_type == 'GL2dScreen':
    #  nobj = 0
    #  for obj in dataobj.objinsts:
    #    if (not hasattr(obj,'master_application') or \
    #      not obj.master_application) and not hasattr( obj,'master') :
    #      nobj = nobj+1
    #  if nobj == 0 : return 1
      
    #print "saving",dataobj.name
    self.name = dataobj.name
    self.object_type = dataobj.object_type
    if isinstance(dataobj,DataObjShadow):
      self.dataparams=dataobj.dataparams
    else:
      self.dataparams=dataobj.dataparams(all=all)
    self.objnames = []
    self.objinsts = []
    for i in range(0,len(dataobj.objnames)):
      #Save objects which do not have a master controlling application
      if (not save_visible or dataobj.objinsts[i].visible) and \
         (not hasattr(dataobj.objinsts[i],'master_application') or not \
          dataobj.objinsts[i].master_application ) and  \
          not hasattr( dataobj.objinsts[i],'master') :
        self.objnames.append(dataobj.objnames[i])
        objinsts = DispObjShadow()
        self.objinsts.append( objinsts )
        objinsts.save(dataobj.objinsts[i],all=all)
    return 0

#-------------------------------------------------------------------------
  def get_filename(self):
#-------------------------------------------------------------------------
    if self.dataparams.has_key('filename'):
      return self.dataparams['filename']
    else:
      return []
#-------------------------------------------------------------------------
  def set_filename(self,filn):
#-------------------------------------------------------------------------
    self.dataparams['filename'] = filn

#-------------------------------------------------------------------------
  def set_dataobj(self,dataobj):
#-------------------------------------------------------------------------
    self.dataobj = dataobj
    
#-------------------------------------------------------------------------
  def restore(self,version=0,default_dir='',**kw):
#------------------------------------------------------------------------
    from model import MolData
    from map import MapData
    from Legend import GL2dScreen
    from Crystal import CrystalData
    from VectorsDispobj import VectorsData
    import os
    import history
    import utils

    #print 'DataObjShadow.restore',self.name,self.dataparams.get('filename',''),default_dir
    self.dataparams["name"] = self.name
    restore_message = ''
    if hasattr(self,'dataobj') and False:
      #return [0,'Data object already restored',self.dataobj.name]
      return [0,'',self.dataobj.name]
    self.dataobj = None

    # Test if data file still exists
    if self.dataparams.has_key('filename'):
      # Reset the full file path on basis of current
      # project directory definition
      filedef = [self.dataparams['filename'][0],self.dataparams['filename'][1]]
      fullpath =  utils.get_filename( self.dataparams['filename'][0],
                                      self.dataparams['filename'][1],
                                      default_dir )
      if not os.path.exists(fullpath) and os.path.exists(self.dataparams['filename'][2]):
        #print "get_filename failed, try",self.dataparams['filename'][2]
        filedef.append(self.dataparams['filename'][2])
        # Since get_filename failed so dotCCP4MG or project dir must be wrong, but it is a valid path.
        if sys.platform != 'win32': # I am not at all sure that drive letters etc. would help here.
          if False and os.path.dirname(self.dataparams['filename'][2]) == '' and os.path.exists(os.path.join(os.environ['PWD'],self.dataparams['filename'][2])):
            # Coerce int a full path. Not sure about this, so disabling for now (False above)
            filedef[0] = 'FULLPATH' 
            filedef[2] = os.path.join(os.environ['PWD'],self.dataparams['filename'][2])
          elif os.path.dirname(self.dataparams['filename'][2]) != '':
            # It seesm to be a valid fullpath here, but project, etc. is not valid, so call it a fullpath.
            filedef[0] = 'FULLPATH' 
        #print "using filedef",filedef
      else: 
        filedef.append( fullpath )
        filedef[1] = os.path.basename(fullpath)

      if len(filedef)>1 and type(filedef[1]) == unicode:
        filedef[1] = str(filedef[1])
      if len(filedef)>2 and type(filedef[2]) == unicode:
        filedef[2] = str(filedef[2])

      self.dataparams['filename'] = filedef
      if self.dataparams['filename'][2] and not os.path.exists(self.dataparams['filename'][2]):
        try:
          if PM('save_sesion_options').get('backup_data_files'):
            rv = restore_data_file(self.dataparams['filename'])
            if rv[0]:
              return [rv[0],rv[1],None]
            else:
              restore_message = rv[1]
          else:
            return ['1','Error restoring data.\n File does not exist '+ \
                             self.dataparams['filename'][2]]
        except:
            return ['1','Error restoring data.\n File does not exist '+ \
                             self.dataparams['filename'][2]]
       
    #print "DataObjShadow.restore dataparams:",self.dataparams.get('filename','')
    if self.dataparams.has_key('modules'):
      for module in self.dataparams['modules']:
        try:
          exec 'from ' +  module + ' import *'
        except:
          pass
    """
    thisRestored = False
    if self.dataparams.has_key('modules'):
      for module in self.dataparams['modules']:
        try:
          exec 'from ' +  module + ' import *'
          thisRestored = True
        except:
	  pass
    """
    """
    if not thisRestored:
        import MGApplication
        mainwin = MGApplication.GetMainWindow()
	mainwin.loadPlugins()
        if self.dataparams.has_key('modules'):
          for module in self.dataparams['modules']:
            try:
	      print "trying module", module
              exec 'from ' +  module + ' import *'
	      print "success"
              thisRestored = True
            except:
	      pass
    """
    #print 'DataObjShadow.restore object_type',self.object_type
    try:
      exec 'self.dataobj='+self.object_type + '(dataparams=self.dataparams,restore_from_version=version)'
        #   '(name="' + self.name + '",dataparams=self.dataparams)'
    except:
      return [1,'Failed to create data object of type '+self.object_type,None]

    
    rv = self.dataobj.loaddata(recentre=0,warnings=0) 
    # Only react if loading data file completely failed?
    if rv[0]==1:
      self.dataobj.delete()
      del self.dataobj
      return [1,restore_message+rv[1],None]
    
    return [0,restore_message,self.dataobj]

  
  
#-----------------------------------------------------------------------
  def restore_display_objects(self,override_visible=1,version=0):
#-----------------------------------------------------------------------
    # Load the display objects
    import model
    import map
    import Legend
    #print "restore_display_objects",self,self.dataobj,self.dataobj.name,self.objinsts,version,self.get_filename(),dir(self)
    if not hasattr(self,'dataobj') or not self.dataobj:
      traceback.print_stack()
      return [0,'No defined data object restoring display object '+self.name]
    if isinstance(self.dataobj,model.MolData) or isinstance(self.dataobj,map.MapData):
      fname = self.get_filename()
      if fname[2] != os.path.abspath(fname[2]):
        fname[2] = os.path.abspath(fname[2])
        self.set_filename(fname)
    restore_message = ''
    if self.objinsts:
      for obj in self.objinsts:
        if isinstance(obj,DispObjShadow):
          if isinstance(self.dataobj,Legend.GL2dScreen):
            #print "restore_display_object",obj,self.dataobj
            fname = obj.get_filename()
            if type(fname) == list or type(fname) == tuple:
              if fname[2] != os.path.abspath(fname[2]):
                fname[2] = os.path.abspath(fname[2])
                obj.set_filename(fname)
          obj.restore(self.dataobj.name,override_visible=override_visible,version=version)
         
    return [0,restore_message]


#-----------------------------------------------------------------
  def copy_data_files(self,project='',dir='',overwrite=1,extra_files=[]):
#-----------------------------------------------------------------
    '''
    Copy the data file(s) to another directory
    '''
    import utils,os,shutil
    copied = []
    errors = []
    if self.dataparams['filename']:
      print self.dataparams['filename']
      if os.path.exists(self.dataparams['filename'][1]):
        filn = self.dataparams['filename'][1]
      elif os.path.exists(self.dataparams['filename'][2]):
        filn = self.dataparams['filename'][2]
      else:
        filn = utils.get_filename(self.dataparams['filename'][0],self.dataparams['filename'][1])
      if not os.path.exists(filn):
        return [1,['No source data file '+filn],[]]
      if os.path.exists(filn) and not os.path.isdir(filn):
        ret=utils.copy_data_file(filename=filn,dir=dir,overwrite=overwrite)
        errors.extend(ret[1])
        copied.extend(ret[2])
        if project: self.dataparams['filename']= [project,self.dataparams['filename'][1],'']
        #print "dataparamsfilename",self.dataparams['filename']

        if self.object_type == 'MolData':
           path = os.path.splitext(filn)[0]+'.ccp4mg'
           if os.path.exists(path): extra_files.insert(1,path)
           if self.dataparams.has_key('load_animation'):
             import Animation
             anim = Animation.Animation(parent=self, \
               params=self.dataparams['load_animation'],load=0)
             ret=anim.copy_data_files(project=project,dir=dir)
             del anim
             errors.extend(ret[1])
             copied.extend(ret[2])

    for filn in extra_files:
      ret=utils.copy_data_file(filename=filn,dir=dir,overwrite=overwrite)
      errors.extend(ret[1])
      copied.extend(ret[2])        

    return [len(errors),errors,copied]

#----------------------------------------------------------------
  def difference(self,first=None,second=None):
#----------------------------------------------------------------
    import utils
    errcodes = {
      'object_type' : 4 ,
      'filename' : 3 }
    
    rv = 0

    self.minus_dataparams = {}
    self.minus_objnames = []
    self.minus_objinsts = []
    
    rv,self.dataparams,self.minus_dataparams = utils.dict_diffs( \
      first.dataparams,second.dataparams,errcodes)

    ii = -1
    for objname in first.objnames:
      ii = ii+1
      if second.objnames.count(objname) and first.objinsts[ii].object_type == second.objinsts[ second.objnames.index(objname)].object_type:
        jj =  second.objnames.index(objname)
        tmp = DispObjShadow(name=objname,object_type=first.objinsts[ii].object_type)
        diff = tmp.difference(first=first.objinsts[ii],second=second.objinsts[jj])
        if not diff: 
          del tmp
        else:
          rv = max(rv,diff)
          self.objnames.append(first.objnames[ii])
          self.objinsts.append(tmp)
      else:  
        self.minus_objnames.append(first.objnames[ii])
        rv = max(rv,2)

    jj = -1
    for objname in second.objnames:
      jj = jj+1
      if not first.objnames.count(objname) or second.objinsts[jj].object_type != first.objinsts[ first.objnames.index(objname)].object_type:
        tmp = DispObjShadow()
        tmp.save(second.objinsts[jj])
        tmp.minus_params={}
        self.objinsts.append(tmp)
        self.objnames.append(tmp.name)
        rv = max(rv,2)

    #print "DataObjShadow difference minus_objnames",self.minus_objnames

    return rv

#------------------------------------------------------------------
  def add(self,source=None):
#------------------------------------------------------------------
    import utils
    self.dataparams = utils.dict_add(self.dataparams, \
          source.dataparams,source.minus_dataparams)

    for objname in source.minus_objnames:
      if self.objnames.count(objname):
        ii = self.objnames.index(objname)
        #print "DataObjShadow::add delete self.objnames[ii]"
        del self.objinsts[ii]
        del self.objnames[ii]

    for objname in source.objnames:
      if self.objnames.count(objname):
        self.objinsts[self.objnames.index(objname)].add( \
          source.objinsts[source.objnames.index(objname)])
      else:
        #print "DAtaObjShadow::add add objname"
        tmp = DispObjShadow()
        tmp.save(source.objinsts[source.objnames.index(objname)])
        self.objinsts.append(tmp)
        self.objnames.append(tmp.name)

#--------------------------------------------------------------------
  def save_picture_definition(self,pretty_print,style='method'):
#--------------------------------------------------------------------
    # CrystalData and GL2dScreen objects are not written out
    if ['CrystalData','GL2dScreen'].count(self.object_type): return ''
  
    import utils
    indent=5
    if style == 'mgpic':
      return ''
    elif style == 'method':
      b_end = ')'
      stream = self.object_type+' ('
    else:
      b_end = '}'
      stream = self.name + ' = {'
    
    #for item in ['name','object_type','filename','objnames']:
    for item in ['filename','name']:
      if getattr(self,item,''):
        stream = stream+utils.picture_defn_item(item,getattr(self,item,''),pretty_print,style=style,indent=indent)
    #if getattr(self,'objnames',''):
    #  stream = stream+utils.picture_defn_item('display_objects',self.objnames,pretty_print,style=style,indent=indent)
      # Only write out 'visible' if it is false
    if self.dataparams.has_key('visible') and not self.dataparams['visible']:
      stream =stream+utils.picture_defn_item('visible',0,pretty_print,style=style,indent=indent)
    for item in self.dataparams.keys():
      if not ['filename','object_type','module','visible'].count(item):
        stream = stream+utils.picture_defn_item(item,self.dataparams[item],pretty_print,style=style,indent=indent)

    stream = stream[0:-1]+ '\n      '+b_end+'\n\n'

    return stream

#--------------------------------------------------------------------
  def nofTransparentObjects(self):
#--------------------------------------------------------------------
    nn = 0
    for obj in self.objinsts:
      nn = nn + obj.isTransparent()
    return nn
      
      
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------
class DispObjShadow:
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------

  objnames = []
  objinsts = []

#-------------------------------------------------------------------
  def __init__(self,name='',object_type='',visible=1,selparams={},colparams={},styleparams={},params={}, default_dir='',**keywords):
#-------------------------------------------------------------------
    
    self.name = name
    self.object_type = object_type
    self.visible = visible
    
    if params.has_key("opacity"):
      if type(params["opacity"]) == str:
          params["opacity"] = float(params["opacity"])

    if  ['MolDisp','SurfaceDispobj','HBonds','Contacts'].count(self.object_type):
      if selparams:
        self.selparams = {}
        self.selparams.update(selparams)
      elif params.has_key('selection'):
        self.selparams = {
            'cid': params['selection'],
            'select': 'cid'}
      else:
        self.selparams = {}
        
      if colparams:
        self.colparams = {}
        self.colparams.update(colparams)
      elif  params.has_key('colour'):
        if ['MolDisp','SurfaceDispobj'].count(self.object_type):
          if MGCOLOUR().index(params['colour'],safe=0)>=0:
            self.colparams = {
             'colour_mode': 'one_colour',
             'one_colour': params['colour'] }
          else:
             self.colparams = {
             'colour_mode':  params['colour'] ,
             'one_colour': '' }
        else:
          self.colparams = {
            'colour' : params['colour']
            } 
      else:
        self.colparams = {}

        
      if styleparams:
        self.styleparams = {}
        self.styleparams.update(styleparams)
      elif params.has_key('style'):
        if ['MolDisp'].count(self.object_type):
           self.styleparams = {
             'style_mode': params['style']
             }
        else:
          if ['same_as'].count( params['style']):
            self.styleparams = {
              'select': params['selection'] }
          else:
            self.styleparams = {
              'cid': params['style'],
              'select': 'cid'}
      else:
        self.styleparams = {}

        
      #print "init",self.name
      #print "selparams",self.selparams
      #print "colparams",self.colparams
      #print "styleparams",self.styleparams

        
    self.params = {  }
    self.params.update(params)
    #print "Setting self.params['default_dir']",default_dir; sys.stdout.flush()
    self.params['default_dir'] = default_dir

#------------------------------------------------------------------
  def get_filename(self):
#------------------------------------------------------------------

    if "Image" in self.object_type:
      return self.params.get('filename','')
    else:
      return ''

#-------------------------------------------------------------------------
  def set_filename(self,filn):
#-------------------------------------------------------------------------
    self.params['filename']=filn

#------------------------------------------------------------------
  def convert_selection(self,select='',mode='string'):
#------------------------------------------------------------------
    if  not ['MolDisp','SurfaceDispobj','HBonds','Contacts'].count(self.object_type): return [0,'']
    
    import types
    if mode == 'list':
      if isinstance(select,types.StringType):
        if not select:
          return [0,[None,'']]
        else:
          import model_selection
          parser= model_selection.SelectionParser()
          return parser.command_to_list(select)
      else:
        return [0,select]
    else:
      if isinstance(select,types.ListType):
        import model_selection
        parser= model_selection.SelectionParser()
        return parser.list_to_command(select)
      else:
        return [0,select]
      
#--------------------------------------------------------------------
  def save ( self,dispobj ,all=1):
#--------------------------------------------------------------------
    self.name = dispobj.name
    self.object_type = dispobj.object_type
    self.visible = dispobj.visible
    if ['MolDisp','SurfaceDispobj','HBonds','Contacts'].count(self.object_type) :
      if isinstance(dispobj,DispObjShadow):
        self.selparams = dispobj.selparams
        self.colparams = dispobj.colparams
        self.styleparams = dispobj.styleparams
      else:
        self.selparams = dispobj.selparams(all=all)
        self.colparams = dispobj.colparams(all=all)
        self.styleparams = dispobj.styleparams(all=all)

    #print 'DispObjShadow.save',self,self.styleparams
    if isinstance(dispobj,DispObjShadow):
      self.params = dispobj.params
    else:
      self.params = dispobj.params(all=all)
    #      "visible",self.visible,'params',self.params


#---------------------------------------------------------------------    
  def restore(self,parent_name,override_visible=1,version =0,deleteEmpty=False):
#---------------------------------------------------------------------
    from model import MolDisp
    from map import MapDisp
    import HBonds,SurfaceDispobj,Contacts,Annotation

    #print "DispObjShadow restore",self,self.params
    
    if not ['MolDisp','MapDisp'].count(self.object_type):
      try:
        exec 'from ' +  self.object_type + ' import *'
      except:
        import re
        module =  re.sub('Dispobj','',self.object_type)
        try:
          exec 'from ' +  module + ' import *'
        except:
          pass

    # Restore surfaces invisible
    if override_visible and \
         ( self.object_type == 'SurfaceDispobj' or \
         (self.object_type == 'MolDisp' and self.styleparams.has_key('style_mode') and self.styleparams['style_mode'] == 'SURFACE') ):
      #print "setting invisible",dispobj.name
      self.visible = 0
        
    if  ['MolDisp','SurfaceDispobj','HBonds','Contacts'].count(self.object_type):
      if self.params.has_key('selection'):
        print '??? old selection',self.params['selection']
        self.selparams['oldSelection'] = self.params['selection']
      exec self.object_type + '(parent_name,name=self.name ' \
         +',visible=self.visible,params = self.params' \
         +',selparams=self.selparams,colparams=self.colparams' \
         +',styleparams=self.styleparams,restore_from_version=version)'
      if deleteEmpty:
        dobjs = get_dataobj(object_type="MolData")
        theDobj = None
        for dobj in dobjs:
          if dobj.name == parent_name:
            theDobj = dobj
        if theDobj is not None:
          dispobjs = theDobj.get_dispobj()
          for dispobj in dispobjs:
            if dispobj.name == self.name:
              if hasattr(dispobj,"SelHandle1") and hasattr(dispobj,"SelHandle2"):
                nat1,nat2 = dispobj.SelHandle1.getNofAtoms(),dispobj.SelHandle2.getNofAtoms()
                if nat1 == 0 or nat2 == 0:
                  dispobj.delete()
              elif hasattr(dispobj,"SelHandle"):
                nat = dispobj.SelHandle.getNofAtoms()
                if nat == 0:
                  dispobj.delete()
                elif hasattr(dispobj,"style") and dispobj.style == "SPLINE" and nat < 3:
                  dispobj.delete()
    else:
      thisRestored = False
      try:
         exec self.object_type + '(parent_name,name=self.name ' \
            +',visible=self.visible,params = self.params )'
         thisRestored = True
      except:
	      pass
      if not thisRestored:
        import MGApplication
        mainwin = MGApplication.GetMainWindow()
        if hasattr(mainwin,"plugins"):
          for plug in mainwin.plugins:
	      for obj in dir(plug):
		      if obj == self.object_type: 
			      try:
				exec 'from '+plug.__name__+' import '+obj
				print self.object_type + '(parent_name,name=self.name ' \
					+',visible=self.visible,params = self.params )'
				exec self.object_type + '(parent_name,name=self.name ' \
					+',visible=self.visible,params = self.params )'
				thisRestored = True
				break
			      except:
				      pass
        if not thisRestored:
	  mainwin.loadPlugins()
          for plug in mainwin.plugins:
	      for obj in dir(plug):
		      if obj == self.object_type: 
			      try:
				exec 'from '+plug.__name__+' import '+obj
				print self.object_type + '(parent_name,name=self.name ' \
					+',visible=self.visible,params = self.params )'
				exec self.object_type + '(parent_name,name=self.name ' \
					+',visible=self.visible,params = self.params )'
				thisRestored = True
				break
			      except:
				      pass
      if not thisRestored:
       try:
          from MGGLImage import Image
          print 'Attempting to restore',self.object_type,self.name,self.visible,self.params
          exec self.object_type + '(parent_name,name=self.name ' \
              +',visible=self.visible,params = self.params )'
       except:
         exc_type, exc_value,exc_tb = sys.exc_info()[:3]
         print exc_type
         print exc_value
         traceback.print_tb(exc_tb)
         print 'DID NOT restore',self.name
        
#-----------------------------------------------------------------
  def difference(self,first=None,second=None):
#-----------------------------------------------------------------
    import utils
    rv = 0

    if first.visible != second.visible:
      self.visible = second.visible
      rv = 1

    rv0,self.params,self.minus_params=utils.dict_diffs(first.params,second.params)
    #print 'dispobjshadow.difference',self,rv0,first.params,second.params
    rv=max(rv,rv0)
    
    if ['MolDisp','SurfaceDispobj','HBonds','Contacts'].count(self.object_type):
      for item in 'selparams','colparams','styleparams':
        ff = getattr(first,item,{})
        ss =  getattr(second,item,{})
        rv0,fdiff,mdiff = utils.dict_diffs(getattr(first,item,{}), getattr(second,item,{}))
        rv=max(rv,rv0)
        setattr(self,item,fdiff)
        setattr(self,'minus_'+item,mdiff)
        
      
    #print "DispObj difference",self.name
    #print "diffs",self.params
    #print "minus_params",self.minus_params
    return rv

#----------------------------------------------------------------
  def add(self,source=None):
#----------------------------------------------------------------
    import utils
    if hasattr(source,'visible'):
      self.visible = source.visible
    attrib_list = ['params']
    if ['MolDisp','SurfaceDispobj','HBonds','Contacts'].count(self.object_type):
      attrib_list.extend(['selparams','colparams','styleparams'])

    for attrib in attrib_list:
      setattr(self,attrib, utils.dict_add(getattr(self,attrib,{}), \
          getattr(source,attrib,{}),getattr(source,'minus_'+attrib,{})))


#--------------------------------------------------------------------
  def save_picture_definition(self,pretty_print,style='method',attributes={},parent=''):
#--------------------------------------------------------------------
    import utils,model,model_selection,model_colour
    if style == 'method':
      b_end = ')'
      stream = self.object_type+' ('
    else:
      b_end='}'
      stream = self.name + ' = {'


    #print "save_picture_definition",self.name,self.colparams

    #for item in ['name','object_type']:
    #  stream=stream+utils.picture_defn_item(item,getattr(self,item,''),pretty_print,style=style)
    if parent:
      stream=stream+"\n          parent = "+parent+","
    for key,value in attributes.items():
      stream=stream+utils.picture_defn_item(key,value,pretty_print,style=style)
    
    if not getattr(self,'visible',1):
      stream=stream+utils.picture_defn_item('visible',0,pretty_print,style=style)
      
    for item in self.params.keys():
        if not ['name','object_type','visible'].count(item):
          stream=stream+utils.picture_defn_item(item,self.params[item],pretty_print,style=style)
          
    if ['MolDisp','SurfaceDispobj','HBonds','Contacts'].count(self.object_type):
      if  self.selparams:       
        stream=stream+utils.picture_defn_item('selection_parameters',self.selparams,pretty_print,style=style,dict_order=model_selection.SelHandle.selection_initialisation)

      if self.colparams:
        if ['MolDisp','SurfaceDispobj'].count(self.object_type):
          #if ['SurfaceDispobj'].count(self.object_type):
            #print 'save_picture_definition',self.colparams
            #print 'picture_defn_item',utils.picture_defn_item('colour_parameters',self.colparams,pretty_print,style=style,dict_order=model_colour.model_colour.colour_initialisation_order)
            
          stream=stream+utils.picture_defn_item('colour_parameters',self.colparams,pretty_print,style=style,dict_order=model_colour.model_colour.colour_initialisation_order)
        else:
          stream=stream+utils.picture_defn_item('colour_parameters',self.colparams,pretty_print,style=style,dict_order=['colour'])
          
      if self.styleparams:
        if ['SurfaceDispobj','HBonds','Contacts'].count(self.object_type):
          stream=stream+utils.picture_defn_item('style_parameters',self.styleparams,pretty_print,style=style,dict_order=model_selection.SelHandle.selection_initialisation)
        else:
          stream=stream+utils.picture_defn_item('style_parameters',self.styleparams,pretty_print,style=style,dict_order=model.MolDisp.style_initialisation)
      
    elif ['MapDisp'].count(self.object_type):
      pass
      """
      import map,MapPlane
      for item in map.MapDisp.map_initialisation_order:
        if self.params.has_key(item):
          stream=stream+utils.picture_defn_item(item,self.params[item],pretty_print,style=style)
      if self.params.has_key('plane_definition'):
        stream=stream+utils.picture_defn_item('plane_definition',self.params['plane_definition'],pretty_print,style=style,dict_order=MapPlane.MapPlane.initialisation_order)
      """

    
    stream = stream[0:-1]+ '          '+b_end+'\n\n'

    return stream

#------------------------------------------------------------------
  def uniquename(self,name='',parent=None):
#------------------------------------------------------------------
    if name:
      return name
    else:
      #print "uniquename",name,parent,parent.objnames
      n = len(parent.objnames)
      contu = 1
      while contu:
        n = n + 1
        nname = parent.name+self.object_type+str(n)
        contu = parent.objnames.count(nname)
    return nname


#------------------------------------------------------------------
  def isTransparent(self):
#------------------------------------------------------------------
    if self.params.get('opacity',1.0)< 0.99:
      return 1
    else:
      return 0
      
#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------
# DispObj class
#------------------------------------------------------------------
#------------------------------------------------------------------
#------------------------------------------------------------------

class DispObj(COneColour,Dependency):
  class_dependents = {}
  
#------------------------------------------------------------------
  def __init__(self,theType,name='',parent='',visible=1, 
               master_application=None, display_table_visible = 1, \
               selparams={},colparams={},styleparams={},params={}):
#------------------------------------------------------------------
    #print "DispObj init parent",parent,'params',params,'master_application',master_application
    import types
    if params.has_key("opacity"):
      if type(params["opacity"]) == str:
          params["opacity"] = float(params["opacity"])

    if isinstance(parent,types.StringType):
      self.parent = data(parent)
    else:
      self.parent = parent
    self.object_type = theType
    self.visible = int(visible)
    self.opacity = float(params.get('opacity',1.0))
    self.display_class = params.get('display_class',"")
    self.do_update_opacity = 0
    self.flashing = 0
    self.do_redraw = 1
    self.redraw_labels= 0
    self.do_recolour = 1
    self.built = 0
    self.moving = 0
    self.graphmod = None
    self.label_graphmod = None
    self.do_rebuildGL = []   # List of graphmods that need rebuilding in GL windows
    self.update_gui = []
    self.name_label = ""
    self.style_label = ""
    self.style_icon = None
    self.colour_label = ""
    self.legend_visible = params.get('legend_visible',0)
    self.customSelectionName = params.get('customSelectionName','')
    
    
    # Name of object which controls this MolDisp - default is 'None'
    # which means the object is controlled by user/gui
    self.master_application = master_application
    self.display_table_visible = display_table_visible
    import copy
    self.transparent = 0
    COneColour.__init__(self)    
    self.name = self.uniquename (name)
    #print "DispObj name",name
    self.parent.add_dispobj(self)

    #Unpack parameters from dictionaries
    if selparams:
      for item in selparams.keys(): 
        setattr(self,item,selparams[item])
    if colparams:
      for item in colparams.keys(): 
        setattr(self,item,colparams[item])
    if styleparams:
      for item in styleparams.keys(): 
        setattr(self,item,styleparams[item])

        
    if params:
      #print 'dispobj params',params
      for item in params.keys():
        if item == "font":
          if hasattr(self,"font"): 
		font = self.font
          else:
		font = FONTMANAGER().GetValidFont()
          for item2 in params[item].keys():
		font[item2] = params[item][item2]
          self.font = font
        else:
          setattr(self,item,params[item])
  

    Dependency.__init__(self)
    self.update_dependents('create')

#------------------------------------------------------------------
  def uniquename(self,name=''):
#------------------------------------------------------------------
    # Beware picture definition may use name as python variable
    #if name:
    #  import string
    #  if string.find(string.digits,name[0:1])>0: name = '_'+name
    if name and not get_dispobj( name = name ):
      nname = name
    else:
      n = len(self.parent.objnames)
      contu = 1
      while contu:
        n = n + 1
        nname = self.parent.name+self.object_type+str(n)
        contu = get_dispobj ( name = nname )
    return nname


  def get_label(self):
    if self.object_type == 'MolDisp':
      return self.get_selection_label()
    else:
      return self.object_type+' '+self.get_selection_label()

  def get_selection_icon(self):
    import model_selection
    icon = None
    label = self.get_selection_label()
    if label in model_selection.SELECTION_ICONS:
      icon = model_selection.SELECTION_ICONS[label]
      if icon:
        if os.path.exists(os.path.join(os.environ['CCP4MG'],'qticons','disptab','selections',icon)):
           icon = os.path.join(os.environ['CCP4MG'],'qticons','disptab','selections',icon)
    return icon

  def setCustomSelectionName(self,t):
    self.customSelectionName = t

  def get_selection_label(self):
    if hasattr(self,'customSelectionName') and self.customSelectionName:
      return self.customSelectionName
    elif hasattr(self,'SelHandle'):
      return self.SelHandle.getLabel()
    elif hasattr(self,'SelHandle1'):
      return self.SelHandle1.getLabel()
    elif hasattr(self,'selection_label'):
      return self.selection_label
    else:
      return self.name_label
  
  def get_colour_label(self):
    #print "DispObj.get_colour_label",self.colour_label
    return self.colour_label

  def get_style_label(self):
    return self.style_label

  def get_style_icon(self):
    return self.style_icon


  def set_reapply_selection(self,mode=1):
    self.reapply_selection = 1

  def set_redraw_text(self):
    if hasattr(self,"get_position_label") or hasattr(self,"create_annotation") or hasattr(self,"get_text") or hasattr(self,"Connectivity2"):
       self.do_redraw = 2

#-------------------------------------------------------------------------
  def set_visibility ( self, toggle_visibility='', visibility='',*args,**keywords):
#-------------------------------------------------------------------------
    if toggle_visibility:
      vis = 1 - self.visible
    elif visibility != '':
      vis = int(visibility)
    else:
      return
    self.visible = vis
    self.update_dependents('visibility')
    for gobj in [self.graphmod, self.label_graphmod ]:
      if gobj: setattr(gobj.obj,'visible',self.visible)
    return vis

  def get_visibility(self):
    return self.visible


#-------------------------------------------------------------------
  def set_flashing(self,mode=-1):
#-------------------------------------------------------------------
    if mode<0:
      mode = 1- self.flashing
    if mode:
      self.flashing = 1
      self.flash_count = 0
      setattr(self,'saved_visible',self.visible)
      MAINWINDOW().addAnimationHandler('flash'+self.name,self.flash)
    else:
      self.flashing = 0
      MAINWINDOW().removeAnimationHandler('flash'+self.name)
      if hasattr(self,'saved_visible'):
        setattr(self,'visible',self.saved_visible)
        import rebuild
        rebuild.UpdateDisplay(auto_save=0)
        del self.saved_visible
      

#-------------------------------------------------------------------
  def isFlashing(self):
#-------------------------------------------------------------------
    return self.flashing

#-------------------------------------------------------------------
  def flash(self):
#-------------------------------------------------------------------
    self.flash_count = self.flash_count+1
    # beware animationInterval is in milliSec
    if self.flash_count< PM('Animation_param').get('flash_delay') * ( 1000/MAINWINDOW().animationInterval()): return
    self.flash_count = 0

    self.visible = 1 - self.visible
    import rebuild
    rebuild.UpdateDisplay(auto_save=0)

#-------------------------------------------------------------------
  def set_do_redraw(self):
#-------------------------------------------------------------------
    self.do_redraw = 1

#-------------------------------------------------------------------
  def unthreadedDraw(self):
#-------------------------------------------------------------------
    return 0
  
#-------------------------------------------------------------------
  def draw(self):
#-------------------------------------------------------------------
    return 0

#-------------------------------------------------------------------
  def rebuildGLDisplayObject(self,main):
#-------------------------------------------------------------------
    #print 'rebuildGLDisplayObject',self.do_rebuildGL
    if self.do_rebuildGL:
      for gobj_name in self.do_rebuildGL:
        gobj = getattr(self,gobj_name,None)
        if gobj:
          main.rebuildGLDisplayObject(gobj)
    self.do_rebuildGL = []
    return 0
    
#-------------------------------------------------------------------
  def clone (self,**keywords):
#-------------------------------------------------------------------
    pars = self.params()
    exec 'import '+self.object_type
    cmd = self.object_type+'.'+ self.object_type+ \
        '(self.parent.name,visible=self.visible,params = pars)'
    exec cmd


#------------------------------------------------------------------
  def delete(self,**keywords):
#------------------------------------------------------------------
    # Beware that updating dependencies may have already
    # zapped an object

    Dependency.delete(self)
    if self.isFlashing(): MAINWINDOW().removeAnimationHandler('flash'+self.name)
    
    for gobj_name in ['graphmod','label_graphmod']:
      gobj = getattr(self,gobj_name,None)
      if gobj:
        MAINWINDOW().removeGLDisplayObject(gobj)
        BUILD().deletemod(gobj)
    self.legend_object = None
      
    if self.parent.objinsts.count(self):
      i = self.parent.objinsts.index(self)    
      self.parent.objnames.pop(i)
      self.parent.objinsts.pop(i)
      
    del self
    
#------------------------------------------------------------------
  def params(self,all=1,rename={}):
#------------------------------------------------------------------
    sel = {}
    #print "params",self,self.opacity
    for item in ['visible','opacity','legend_visible','customSelectionName','display_class']:
      if hasattr(self,item):
        sel[item]=getattr(self,item)
        
    if rename:
      for key,newkey in rename.items():
        if sel.has_key(key):
          sel[newkey] = sel[key]
          del sel[key]
 
    return sel

#-----------------------------------------------------------------
  def isSameData(self,shadowdisp):
#-----------------------------------------------------------------
    return False

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

    if shadowdisp.params.has_key('selection') and self.selection != shadowdisp.params['selection']:
      #print "DisPObj update_data_status",shadowdisp.params['selection']
      rv = 1
      self.set_selection(selection=shadowdisp.params['selection'])
    if shadowdisp.params.has_key('colour') and self.colour != shadowdisp.params['colour']:
      rv = 1
      self.set_colour(colour=shadowdisp.params['colour'])
    if shadowdisp.params.has_key('style') and self.style != shadowdisp.params['style']:
      rv = 1
      self.set_style(style=shadowdisp.params['style'])

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

#-----------------------------------------------------------------
  def update_data_status0(self,shadowdisp):
#-----------------------------------------------------------------
    import utils
 

    if shadowdisp.visible != self.visible:
      self.set_visibility(visibility = shadowdisp.visible)
      self.update_gui.append(['visible'])
      return 1
    else:
      return 0

#-----------------------------------------------------------------
  def animate(self,shadowdisp):
#-----------------------------------------------------------------
    #print "Dispobj.animate",self.name
    
    if shadowdisp.visible != self.visible:
      self.set_visibility(visibility = shadowdisp.visible)
      #self.update_gui.append(['visible'])
      return 1
    else:
      return 0

#------------------------------------------------------------------
  def selparams(self):
#------------------------------------------------------------------
    return {}
  
#------------------------------------------------------------------
  def colparams(self,all=1):
#------------------------------------------------------------------
    col = {}
    for item in ['colour']:
      col[item]=getattr(self,item)
    return col
  
#------------------------------------------------------------------
  def styleparams(self,all=1):
#------------------------------------------------------------------
    col = {}
    #for item in ['style']:
    #  col[item]=getattr(self,item)
    return col    


    
#------------------------------------------------------------------
  def centre_on ( self,**keywords ):
#------------------------------------------------------------------
    pass

#------------------------------------------------------------------
  def list_data(self):
#------------------------------------------------------------------
    return ''
  
#--------------------------------------------------------------------
  def name_string(self):
#-------------------------------------------------------------------
    return  "disp('" + self.parent.name + "','" + self.name + "')"

#--------------------------------------------------------------------
  def handle_moving_data(self,**keywords):
#--------------------------------------------------------------------
    pass
  
#----------------------------------------------------------------------
  def set_graphmod_visibility(self):
#----------------------------------------------------------------------
    """
    Set the visibility of the graphmod appropriately
    for the visibility of the dataobj and dispobj
    """
    
    vis = 1
    if not int(self.visible):
      vis = 0
    elif not int(self.parent.visible):
      vis = 0
    # does this object has visibility dependency on any
    # master object which is currently invisible
    elif len(self.dependencies.keys())>0:
      for master,attlist in self.dependencies.items():
        if attlist.count('visibility') and not master.visible:
          vis = 0
          break

    if self.graphmod: self.graphmod.obj.visible = vis
    if self.label_graphmod: self.label_graphmod.obj.visible = vis
      
    #print "set_graphmod_visibility",self.visible,self.parent.visible,vis
    return vis

#---------------------------------------------------------------------
  def draw (self):
#---------------------------------------------------------------------
    return 0

#---------------------------------------------------------------------
  def drawPickedLabels(self):
#---------------------------------------------------------------------
    return 0
#
# Movement of object should usually apply to the dataobj but for
# legend/image the following is needed
#

#-------------------------------------------------------------------
  def transform(self,dx='',dy='',dz='',rotx='',roty='',rotz=''):
#-------------------------------------------------------------------
    '''
    Handle translation commands from mouse movement
    '''
    pass

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

#-----------------------------------------------------------------------
  def set_opacity(self,opacity=-1.0,**keywords):
#-----------------------------------------------------------------------
    #print 'Dispobj.set_opacity',self.name,opacity,self.canBeTransparent()
    if self.canBeTransparent() and opacity>=0.0:
        self.opacity = float(opacity)
        self.do_update_opacity = 1

    
#------------------------------------------------------------------
  def movie_interpolation(self,initial_status=None,final_status=None, \
                          fraction=0.0,step='',**keywords):
#------------------------------------------------------------------
    #print "DispObj.movie_interpolation"
    if final_status.params.has_key('opacity') and initial_status.params.has_key('opacity'):
      do = float(final_status.params['opacity'])-float(initial_status.params['opacity'])
      if (do<-0.001 or do>0.001) and hasattr(self,'graphmod') and self.graphmod:
        self.opacity = float(initial_status.params['opacity'])+fraction*do # Belt and braces
        self.graphmod.obj.SetAlpha(float(initial_status.params['opacity'])+fraction*do)

#----------------------------------------------------------------
  def setMasterCrystal(self,crystal):
#----------------------------------------------------------------
    self.masterCrystal = crystal
    self.updateCrystal()


#----------------------------------------------------------------
  def updateCrystal(self):
#----------------------------------------------------------------

    if not self.graphmod: return
    if not hasattr(self,'masterCrystal'): return
        
    if not self.label_graphmod:
        import graphicsmodel
        self.label_graphmod = graphicsmodel.graphicsmodel()
        BUILD().append(self.label_graphmod)
    if self.label_graphmod:
       self.label_graphmod.symmetry_dependent = self.graphmod

    #print "dataobj.updateCrystal",self.masterCrystal
    if not self.masterCrystal:
      self.graphmod.DrawUnitCell(0)
      self.graphmod.obj.SetDrawUnitCell(0) 
      self.graphmod.setparams(draw_symmetry = 0)
      self.graphmod.setparams(symmetry_labels=0)

    else:
      xtl = get_dispobj(name=self.masterCrystal)
      self.graphmod.obj.SetDrawSymmetryColoured(xtl.symm_diff_colour)
      self.graphmod.obj.SetDrawUnitCell(xtl.cell_edges) 
      self.graphmod.DrawUnitCell(draw_unit_cell=xtl.draw_unit_cell, \
                     xshift=xtl.unit_cell_shift[0], \
                     yshift=xtl.unit_cell_shift[1],
                     zshift=xtl.unit_cell_shift[2])
      self.graphmod.setparams(symmetry_labels= xtl.symmetry_labels)
      self.graphmod.setparams(draw_symmetry = xtl.mode)
      self.graphmod.setparams(symmetry_radius = xtl.atom_symm_radius)
      self.graphmod.draw_contacts=xtl.draw_contacts

    self.do_redraw = 1


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

'''
#------------------------------------------------------------------------
  def drawLegend(self):
#------------------------------------------------------------------------
    # Draw the legend - append text to DispObj.graphmod.obj
    if (not self.update_legend) or (not self.graphmod) or (not self.isLegendPossible()): return 0
    if self.legend_object:
        import cprim
        cprim.DeleteTextLabel(self.graphmod.obj,self.legend_object.GetID())
        self.legend_object = None
        
    if self.legend_visible:
         import TextLabel,pygl_coord
         x,y= 0.0,0.0
         text = self.getLegend()
         self.legend_object = TextLabel.CreateBillBoardTextLabel(text,pygl_coord.Cartesian(x,y,0.0),pygl_coord.Cartesian(x,y,0.0))
         self.legend_object.thisown = 0

         self.graphmod.obj.add_text_primitive(self.legend_object)
    self.update_legend = 0
    print 'DispObj.drawLegend'
    return 1
'''
  
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class CFont:

  

#------------------------------------------------------------------
  def set_font(self,font={},**keywords):
#------------------------------------------------------------------
    redraw = 0
    # Input could be be a dictionary 'font' or individual
    # font attributes which will be put in keywords dictionary
    #print "set_font",self.name,font,'self',self.font
    font.update(keywords)
    font['underline']=0
    for item in ['family','size','weight','slant','underline']:
      if font.has_key(item) and font[item] and font[item] != self.font[item]:
        self.font[item] = font[item]
        redraw = 1
        #print "resetting",item
        
    if redraw: self.update_font()

#-----------------------------------------------------------------------
  def update_font(self,label_ob = None):
#-----------------------------------------------------------------------
    import utils
    #self.font = utils.GetValidFont(self.font)
    #print "CFont update_font",self.font
    if label_ob:
      label_ob.SetFontFamily(self.font['family'])
      label_ob.SetFontSize(int(self.font['size']))
      label_ob.SetFontWeight(self.font['weight'])
      label_ob.SetFontSlant(self.font['slant'])
      label_ob.SetFontUnderline(bool(self.font['underline']))
    elif self.graphmod and self.graphmod.obj:
      self.graphmod.obj.SetTextFont(str(self.font['family']),str(self.font['weight']), \
                                    str(self.font['slant']),self.font['size'],self.font['underline'])
      self.graphmod.obj.rebuild()

#----------------------------------------------------------------------
  def scale_font(self,scale=-1.0,**keywords):
#----------------------------------------------------------------------
    #print "scale_font",self,scale
    font_sizes = [8,10,11,12,14,18,20,24,34,45,60]

    if scale < 0.0:
      self.graphmod.obj.SetTextFont(str(self.font['family']),str(self.font['weight']), \
                                    str(self.font['slant']),self.font['size'],self.font['underline'])
      self.graphmod.obj.rebuild()

    else:
      scaled_size = float(self.font['size']) * scale
      if scaled_size < font_sizes[0]:
        new_size = font_sizes[0]
      elif scaled_size > font_sizes[-1]:
        new_size = font_sizes[-1]
      else:
        n = 0
        while n < len(font_sizes)-1 and scaled_size > font_sizes[n]: n = n+1
        if scaled_size-font_sizes[n-1] > font_sizes[n]-scaled_size:
          new_size = font_sizes[n]
        else:
          new_size = font_sizes[n-1]
        #print "scale_font" , scaled_size, n, new_size
        self.graphmod.obj.SetTextFont(str(self.font['family']),str(self.font['weight']), \
                                    str(self.font['slant']),str(new_size),self.font['underline'])
        self.graphmod.obj.rebuild()
    
#----------------------------------------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------


  
#----------------------------------------------------------------------
def restore_data_file(filename=[],dir='',query=1):
#----------------------------------------------------------------------
  import utils
  import shutil
  filn = utils.get_filename(filename[0],filename[1])
  if not dir:
    # Make an empty directory .CCP4MG/saved_data_files
    dir = os.path.join( utils.get_CCP4MG(),'saved_data_files' )
  backup = os.path.join(dir,os.path.split(filn)[1])
  if not os.path.exists(backup):
    return [1,"Backup for file "+filn+" does not exist"]

   # if query and not exit:
   #   GUI.insts.CreateGUIWindow ('restore_data_file',self, [
   #         ['TITLE','Restore backed up file'],
   #         ['RETURN_METHOD','restore_data_file'],
   #         ['PARAM','filename',
   #            ['INITIAL',filename]],
   #         ['PARAM','dir',
   #            ['INITIAL',dir]],
   #         ['DISPLAY','WINDOW',
   #            ['LINE',
   #              ['LABEL','Attempting to restore datafrom file']],
   #            ['LINE',
   #              ['LABEL',filename]],
   #           ['LINE',
   #              ['LABEL','File does not exist. Do you want']],
   #            ['LINE',
   #              ['LABEL','to copy backup file to that filename?']],          
   #          ['BUTTONS',
   #           ['ok', ['LABEL','Yes']],
   #           ['dismiss',['LABEL','No'],['CLOSE']]]
   #          ]])
   #   return

  try:
    shutil.copy( backup, os.path.split(filn)[0])
  except:
    return [1,'Error attempting to restore file '+filn]

  return [0,'*****PLEASE NOTE*****\nRestoring data - data file '+filn+' not found.\n It has being recreated from backup made on '+utils.fileDate(backup)]


