"""
     qtgui/CCP4ModelWidgets.py: CCP4 Gui Project
     Copyright (C) 2010 University of York

     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.
"""

##@package CCP4ModelWidgets (QtGui) Collection of widgets for model data types

from PyQt4 import QtGui,QtCore
import CCP4ModelData
from CCP4ErrorHandling import *
import CCP4Widgets


class CResidueRangeView(CCP4Widgets.CComplexLineWidget):

    MODEL_CLASS = CCP4ModelData.CResidueRange
    ERROR_CODES = { }

    def __init__(self,parent=None,model=None,qualifiers={}):
      qualis = qualifiers
      CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis)
      #if self.editable: self.connect(self.iconButton,QtCore.SIGNAL('acceptDropData'),self.handleDrop)
      if model is not None: self.setModel(model)

      self.layout().addWidget(QtGui.QLabel('Chain'))
      if self.editable:
        self.widgets['chainId'] = CCP4Widgets.CComboBox(self,charWidth=2,dragType=self.dragType())
      else:
        self.widgets['chainId'] = CCP4Widgets.CLabel(self,charWidth=4,uneditable=True,dragType=self.dragType())
      self.layout().addWidget(self.widgets['chainId'])

      for item,label in [['firstRes','residue'],['lastRes','to']]:
        self.layout().addWidget(QtGui.QLabel(label))
        if self.editable:
          self.widgets[item] = CCP4Widgets.CLineEdit(self,charWidth=10,dragType=self.dragType())
        else:
          self.widgets[item] = CCP4Widgets.CLabel(self,charWidth=10,uneditable=True,dragType=self.dragType())
        self.layout().addWidget(self.widgets[item])

      if self.editable:
        for item in self.widgets.keys():
          self.connect(self.widgets[item],QtCore.SIGNAL('editingFinished()'),self.updateModelFromView)
          self.connect(self.widgets[item],QtCore.SIGNAL('acceptDropData'),self.acceptDropData)
        pdbFileObj = self.model.parent().getDataByKey('pdbFileKey')
        if pdbFileObj is not None:
          self.loadChains()
          self.connect(pdbFileObj,QtCore.SIGNAL('dataChanged'),self.loadChains)
      self.layout().addStretch(5)

    def loadChains(self):
      self.widgets['chainId'].clear()
      for chn in self.model.parent().getChains():
        self.widgets['chainId'].addItem(chn)
          

    def getMenuDef(self):
      return ['clear','copy','paste','help']

class CSequenceEdit:
  def __init__(self,parent=None,model=None):
    pass
      

class CSequenceView(CCP4Widgets.CComplexLineWidget):

  ERROR_CODES = { }
  MODEL_CLASS = CCP4ModelData.CSequence
  
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = {'gridLayout':True}
    qualis.update(qualifiers)
    
    CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis)
    if model is not None: self.setModel(model)
    #if  qualis.get('stackButton',None) is not None:
    #  self.layout().addWidget(qualis['stackButton'],0,1)
    self.layout().addWidget(QtGui.QLabel('Description:',self),1,0)
    if self.editable:
      self.widgets['identifier'] = CCP4Widgets.CLineEdit(self,qualifiers=qualis)
      self.widgets['reference'] = CCP4Widgets.CLineEdit(self,qualifiers=qualis)
      self.widgets['referenceDb'] = CCP4Widgets.CComboBox(self,qualifiers= CCP4ModelData.CSequence.CONTENTS['referenceDb']['qualifiers'] ) 
    else:
      self.widgets['identifier'] = CCP4Widgets.CLabel(self,qualifiers=qualis)
      self.widgets['reference'] = CCP4Widgets.CLabel(self,qualifiers=qualis)
      self.widgets['referenceDb'] = CCP4Widgets.CLabel(self,qualifiers=qualis)
    self.widgets['identifier'].setToolTip('Name of the sequence')
    self.widgets['reference'].setToolTip('Database identifier')
    self.widgets['referenceDb'].setToolTip('Sequence database')    
    self.layout().addWidget(self.widgets['identifier'],1,1,1,2)
    self.layout().addWidget(QtGui.QLabel('Database reference:',self),0,0)
    self.layout().addWidget(self.widgets['referenceDb'],0,1)
    self.layout().addWidget(self.widgets['reference'],0,2)
    #if self.editable:
    #  self.widgets['moleculeType']=CCP4Widgets.CComboBox(self,qualifiers= CCP4ModelData.CSequence.CONTENTS['moleculeType']['qualifiers'])
    #else:
    #  self.widgets['moleculeType']=CCP4Widgets.CLabel(self,qualifiers=qualis)
    #self.layout().addWidget(self.widgets['moleculeType'],0,5)

    self.widgets['sequence'] = CCP4Widgets.CTextEdit(self,qualifiers=qualis)
    self.layout().addWidget(self.widgets['sequence'],2,0,1,3)

    for item in CCP4ModelData.CSequence.CONTENTS_ORDER:
      widget = self.widgets.get(item,None)
      if widget is not None:
        #print 'CSequenceView.__init__',item,repr(self.model.getDataObjects(item)),type(self.model.getDataObjects(item))
        if model is not None:
          tT = self.model.getDataObjects(item).qualifiers('toolTip')
          if tT is NotImplemented: tT = ''
          widget.setToolTip(tT)
        if self.editable:
          self.connect(widget,QtCore.SIGNAL(widget.editSignal()),self.updateModelFromView)
          self.connect(widget,QtCore.SIGNAL('acceptDropData'),self.acceptDropData)


       
  def getMenuDef(self):
    menu = CCP4Widgets.CComplexLineWidget.getMenuDef(self)
    menu.insert(0,'content')
    return menu

  def updateViewFromModel(self):
    #print 'CSequenceView.updateViewFromModel model',self.model
    #print 'CSequenceView.updateViewFromModel',self.model.get()
    if len(self.widgets) == 0: return
    if self.model is None:
      for item in ['identifier','reference','referenceDb','sequence']:
        self.widgets[item].setValue('')
    else:
      for item in ['identifier','reference','referenceDb','sequence']:
        #print 'CSequenceView.updateViewFromModel',item,self.model.__dict__['_value'][item].__str__()
        self.widgets[item].setValue(self.model.__dict__['_value'][item].__str__())
    self.validate()

class CSeqDataFileView(CCP4Widgets.CDataFileView):
  MODEL_CLASS = CCP4ModelData.CSeqDataFile
  
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = {'vboxLayout':True}
    qualis.update(qualifiers)
    self.enableEdit = qualifiers.get('enableEdit',True)
    CCP4Widgets.CDataFileView.__init__(self,parent=parent,model=model,qualifiers=qualis)
    
    qualis['iconButton'] = False  
    if model is not None:
      self.widgets['sequence'] = CSequenceView(parent=parent,model=model.getFileContent(),qualifiers=qualis)
    else:
      self.widgets['sequence'] = CSequenceView(parent=parent,qualifiers=qualis)   
    self.layout().addWidget(self.widgets['sequence'])
    self.widgets['sequence'].hide()
    self.setModel(model)
    parentTask = self.parentTaskWidget()
    if parentTask is not None:
      self.connect(parentTask,QtCore.SIGNAL('doFix'),self.saveSequence)
                             

  def getMenuDef(self):
    if self.editable:
      menu = ['clear',['View','view_text'],'sep','copy','paste','help']
      if self.enableEdit: menu.insert(2,'edit')
    else:
      menu = [['View','view_text'],'sep','copy','editLabel','export','help']
    return menu

  def getActionDef(self,name):
    if name == 'edit':
      return dict (
        text = self.tr("View/edit sequence"),
        tip = self.tr('You can cut-n-paste sequence into the widget'),
        slot = self.showEditor
      )
    else:
      return CCP4Widgets.CDataFileView.getActionDef(self,name)


  def showEditor(self,visible=None):
    if visible is None: visible = not self.widgets['sequence'].isVisible()
    #print 'CSeqDataFile.showEditor',visible,self.model.exists(),self.widgets['sequence'].model
    if visible and self.model.exists():
        self.model.loadFile()
        #print 'CSeqDataFile.showEditor after loadFile',repr(self.widgets['sequence'].model),self.widgets['sequence'].model
        if self.widgets['sequence'].model is None:
          self.widgets['sequence'].setModel(model.fileContent)
        self.widgets['sequence'].updateViewFromModel()
    self.widgets['sequence'].setVisible(visible)

  def setModel(self,model):
    CCP4Widgets.CDataFileView.setModel(self,model)
    if self.widgets.get('sequence',None) is None: return
    if model is not None:
      if hasattr(model,'fileContent'):
        self.widgets['sequence'].setModel(model.fileContent)
      self.connect(model,QtCore.SIGNAL('dataChanged'),self.loadSequence)
      self.loadSequence()
    else:
      self.widgets['sequence'].setModel(None)

  def loadSequence(self):
    if not self.editable: return
    if self.model.isSet():
      try:
        self.model.loadFile()
      except:
        self.model.fileContent.unSet()
    else:
      self.model.fileContent.unSet()
    self.updateViewFromModel()
    #self.widgets['sequence'].updateViewFromModel()
    
  def saveSequence(self):
    widgetValue = self.widgets['sequence'].widgets['sequence'].getValue()
    #print 'CSeqDataFileView.saveSequence',widgetValue
    if widgetValue is None or len(widgetValue.strip())==0: return
    jobId = self.parentTaskWidget().jobId()
    self.widgets['sequence'].updateModelFromView()
    if not self.model.baseName.isSet():
      self.model.saveSequence(jobId=jobId)
      self.validate()
    else:
      if self.model.fileContent.sequence.isSet():
        # User has a filename set and a sequence set - test if they match
        fileContent = CCP4ModelData.CSequence()
        #try:
        if 1:
          fileContent.loadFile(str(self.model),format=self.model.__dict__['format'])
          err = self.model.fileContent.assertSame(fileContent)
          #print 'CSeqDataFileView.saveSequence',self.model.fileContent,fileContent,err.report()
          if err.maxSeverity()>SEVERITY_WARNING:
            mess = QtGui.QMessageBox(self)
            mess.setWindowTitle('Sequence file')
            mess.setText('Save sequence or reference data to a new file')
            mess.addButton('Save to file', QtGui.QMessageBox.AcceptRole)
            mess.addButton('Keep existing file',QtGui.QMessageBox.ActionRole)
            mess.addButton('Cancel', QtGui.QMessageBox.RejectRole)
            mess.show()
            ret = mess.exec_()
            if ret == QtGui.QMessageBox.RejectRole:
              return
            elif ret == QtGui.QMessageBox.ActionRole:
              self.model.loadFile()
            else:

              self.model.saveSequence(jobId=jobId)
            self.validate()
              
        
  def validate(self,isValid=None,reportMessage=True):
    dataFileIsValid = CCP4Widgets.CDataFileView.validate(self,isValid=isValid,reportMessage=reportMessage)
    if self.editable:
      try:
        sequenceIsValid =  self.widgets['sequence'].validate(isValid=isValid,reportMessage=reportMessage)
        if self.model.qualifiers('allowUndefined') and sequenceIsValid.maxSeverity()<SEVERITY_WARNING:
          #Allow the sequence to be undefined if the sequence file is allowed to be undefined
          isValid = dataFileIsValid
        else:
          isValid = dataFileIsValid and sequenceIsValid
      except:
        isValid = dataFileIsValid
    else:
      isValid = dataFileIsValid
    if isValid != self.isValid:
      self.isValid = isValid
      self.setProperty("isValid",isValid)
      self.updateValidityIndicator()
    return self.isValid

  def handleBrowserOpenFile(self,filename,downloadInfo,**kw):
    kw['validate'] = False
    kw['updateView'] = False
    # Imperative to clear all model related data before trying to load new file
    self.model.unSet()
    self.model.blockSignals(True)
    CCP4Widgets.CDataFileView.handleBrowserOpenFile(self,filename,downloadInfo,**kw)
    self.model.blockSignals(False)
    #print 'CSeqDataFileView.handleBrowserOpenFile identifiers',self.model,self.model.isSet(),self.model.__dict__.get('identifiers','No identifiers'),self.model.fileContent.__dict__.get('loadWarning','No loadWarning'),self.model.__dict__['format']
    if not self.model.isSet():
      self.validate()
      return
    if self.model.__dict__['format'] == 'unknown':
      self.showEditor(True)
      QtGui.QMessageBox.warning(self,'Reading sequence file','The format of the file has not been recognised. Please edit the sequence to remove any non-sequence information.' )
    elif self.model.fileContent.__dict__.get('loadWarning',None) is not None:
       #print 'CSeqDataFileView.handleBrowserOpenFile loadWarning',self.model.fileContent.__dict__.get('loadWarning')
       if len(self.model.fileContent.__dict__['loadWarning'])>0 and self.model.fileContent.__dict__['loadWarning'][0]['code'] == 108:
         import CCP4Utils
         win = self.makeImportReport('Importing '+str(filename),self.acceptFixedPir)
         label = QtGui.QLabel(self)
         label.setText( 'The input file is not correct PIR format but has been fixed and is shown below.\nIf this is still incorrect please correct the file and select it again.\n'  + CCP4ModelData.PIR_DESCRIPTION + '\nYour corrected file:')
         #label.setReadOnly(True)
         win.layout().insertWidget(0,label)
         label = QtGui.QTextEdit(self)
         label.setText(CCP4Utils.readFile(self.model.fileContent.__dict__['loadWarning'][0]['details']))
         label.setReadOnly(True)
         win.layout().insertWidget(1,label)
         win.show()
         return
       elif self.model.fileContent.__dict__['loadWarning'].maxSeverity()>SEVERITY_WARNING:
         self.model.fileContent.__dict__['loadWarning'].warningMessage(windowTitle='Importing sequence file',parent=self,message='Failed importing sequence file')
         self.model.unSet()
         self.updateViewFromModel()
         return
    elif len(self.model.__dict__.get('identifiers',[]))>1:
      #print 'handleBrowserOpenFile drawing idChooser',getattr(self,'idChooser',None)
      if getattr(self,'idChooser',None) is None:
        self.idChooser = QtGui.QListWidget(self)
        self.idChooser.setSelectionMode(QtGui.QListWidget.SingleSelection)
        win = self.makeImportReport('Importing '+str(filename),self.handleIdChooser)
        win.layout().insertWidget(0,self.idChooser)
        win.layout().insertWidget(0,QtGui.QLabel('Select one of the sequences in the file',self))
      self.idChooser.clear()
      for item in self.model.__dict__['identifiers']: self.idChooser.addItem(item)
      self.idChooser.window().show()
      self.idChooser.window().raise_()
      return

    if self.model.__dict__['format'] is None or self.model.__dict__['format'] != 'internal':
      #self.model.importFile(jobId=self.parentTaskWidget().jobId(),jobNumber=self.parentTaskWidget().jobNumber())
      self.doImportFile()
    else:
      self.model.emitDataChanged()
      
    # No problems just display it!
    self.updateViewFromModel()
    self.validate()

  def makeImportReport(self,title,callBack=None):
    win = QtGui.QDialog(self)
    win.setWindowTitle(title)
    win.setLayout(QtGui.QVBoxLayout())
    line = QtGui.QHBoxLayout()
    box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
    win.layout().addLayout(line)
    line.addStretch(1)
    line.addWidget(box)
    line.addStretch(1)
    if callBack is not None: self.connect(box,QtCore.SIGNAL('clicked ( QAbstractButton * )'),callBack)
    #self.connect(box,QtCore.SIGNAL('clicked ( QAbstractButton * )'),win.close)
    return win

  def doImportFile(self,label=None):
      if self.model.fileContent.identifier.isSet() and len(self.model.fileContent.identifier)>0:
        anno = self.model.fileContent.identifier
      elif label is not None:
        anno = label+' imported from '+ str(self.model.baseName)
      else:
        anno = self.model.qualifiers('guiLabel')+' imported from '+ str(self.model.baseName)

      #print 'doImportFile anno',anno,'label',label

      #self.model.blockSignals(True)
      validatedFile=self.model.fileContent.__dict__.get('validatedFile',None)
      self.model.importFile(jobId=self.parentTaskWidget().jobId(),annotation=anno,validatedFile=validatedFile,jobNumber=self.parentTaskWidget().jobNumber())
      self.model.annotation = anno
      #self.model.blockSignals(False)
      self.updateViewFromModel()
      import CCP4Modules
      if CCP4Modules.PREFERENCES().AUTO_INFO_ON_FILE_IMPORT and not self.model.dbFileId.isSet():        
        self.openInfo(label=self.model.qualifiers('guiLabel').lower(),sourceFileAnnotation=self.model.__dict__.get('sourceFileAnnotation',''))

  def acceptFixedPir(self,but):
      #print 'CSeqDataFileView.acceptFixedPir',but.text().__str__()
      if but.text().__str__() == 'Cancel':
          self.model.unSet()
          self.updateViewFromModel()
      else:
        self.doImportFile()
      win = but.window()
      win.close()
      self.updateViewFromModel()

  def handleIdChooser(self,but):
      #print 'CSeqDataFileView.handleIdChooser', but.text().__str__()
      record = self.idChooser.currentRow()
      win = but.window()
      win.setModal(False)
      win.close()
      
      if but.text().__str__() == 'Cancel':
          self.model.unSet()
          self.updateViewFromModel()
          return
      else:
        #print 'CSeqDataFileView.handleIdChooser',record; import sys; sys.stdout.flush()
        self.model.fileContent.loadExternalFile(self.model.__str__(),self.model.__dict__['format'],record=record)
        self.doImportFile()
        print 'handleIdChooser',self.model.fileContent.identifier,self.model.fileContent.sequence
        self.updateViewFromModel()
       

      
  def filterText(self):
    # make the filters text for QFileDialog to include the alignment extensions
    textList = []
    for desc,extList in [ [CCP4ModelData.CSeqDataFile.QUALIFIERS['mimeTypeDescription'] , CCP4ModelData.EXTLIST ],
                          ]:
      text = desc + ' ('
      for ext in extList.keys():
        text = text + '*.'+ext+' '
      textList.append( text[0:-1]+')' )    
    return textList

'''
This reimplemetation of CDataFileView provides tools for user to merge Refmac dictionary files
It is no longer presented on the gui but the issues are handled in the appropriate scripts
eg.CPluginScript.mergeDictToProjectLib() is used.

class CDictDataFileView(CCP4Widgets.CDataFileView):
  MODEL_CLASS = CCP4ModelData.CDictDataFile
  PROJECT_FILE_LABEL = 'Ideal ligand geometry file for project'

  def __init__(self,parent=None,model=None,qualifiers={},**kw):
    qualis = {}
    qualis.update(qualifiers)
    qualis.update(kw)
    CCP4Widgets.CDataFileView.__init__(self,parent=parent,model=model,qualifiers=qualis)
    

  def getActionDef(self,name):
    if name == 'manage_dict':
      return dict (
        text = self.tr("View/edit dictionary"),
        tip = self.tr('View/edit the currently selected dictionary'),
        slot = self.manageProjectDictionary
      )
    else:
      return CCP4Widgets.CDataFileView.getActionDef(self,name)

  def manageProjectDictionary(self):
    if not hasattr(self,'dictManager'):     
      #self.dictManager = CDictDataDialog(model=self.model.fileContent)
      self.dictManager = CDictDataDialog(parent=self.parentTaskWidget(),projectId=self.parentTaskWidget().projectId())
    self.dictManager.show()
    self.dictManager.raise_()
    
  def handleMerge(self):
    import CCP4FileBrowser,CCP4Modules
    self.mergeFileBrowser = CCP4FileBrowser.CFileDialog(parent=self,title='Select dictionary file to merge into project dictionary',
          defaultSuffix=CCP4Modules.MIMETYPESHANDLER().getMimeTypeInfo(name='application/refmac-dictionary',info='fileExtensions'),
                                 fileMode=QtGui.QFileDialog.ExistingFiles,saveButtonText='Merge these files')
    self.mergeFileBrowser.show()
    self.connect(self.mergeFileBrowser,QtCore.SIGNAL('selectFiles'),self.mergeFiles)

  def mergeFiles(self,selectedFiles=[]):   
    # 'CDictDataFileView.mergeFiles',selectedFiles
    if hasattr(self,'mergeFileBrowser'):
      self.mergeFileBrowser.close()
      self.mergeFileBrowser.deleteLater()
    
    import CCP4Modules
    workDirectory = CCP4Modules.PROJECTSMANAGER().jobDirectory(
        jobId=self.parentTaskWidget().jobId(),projectId=self.parentTaskWidget().projectId())
    newFile,err = self.model.mergeInDictFiles(dictFileList=selectedFiles,parentWorkDirectory=workDirectory)
    
    for fileName in selectedFiles:
      err = self.model.fileContent.mergeFile(fileName=fileName,overwrite=True)
      #print 'CDictDataFileView.mergeFiles',err.report(),err.maxSeverity()
      if err.maxSeverity()>SEVERITY_WARNING:
        err.warningMessage(windowTitle='Error merging Refmac dictionaries',parent=self,message='Error attempting to merge Refmac dictionaries')
      return

  """
  def loadJobCombo(self):
    CCP4Widgets.CDataFileView.loadJobCombo(self)
    if self.jobCombo.findText(CDictDataFileView.PROJECT_FILE_LABEL)<0:
      # Call the defaultProjectDict even though result is not used it will ensure that
      # a file exists (by creting emptly file if necessary)
      if  self.model is None: return
      dictFile = self.model.defaultProjectDict(projectId=self.parentTaskWidget().projectId())
      self.jobCombo.insertItem(1,CDictDataFileView.PROJECT_FILE_LABEL)
      self.jobCombo.setCurrentIndex(1)
  """

  def handleFollowFrom(self,contextJobId,projectId):
    self.jobCombo.setCurrentIndex(1)

  def handleJobComboChange(self,indx=None):
    self.connectUpdateViewFromModel(False)
    if indx is None: indx = 0
    if str(self.jobCombo.itemText(indx)) == CDictDataFileView.PROJECT_FILE_LABEL:
      self.model.set(self.model.defaultProjectDict(projectId=self.parentTaskWidget().projectId()))
      self.model.annotation = CDictDataFileView.PROJECT_FILE_LABEL
    else:
      CCP4Widgets.CDataFileView.handleJobComboChange(self,indx0=indx)
    
'''
      
class CMonomerView(CCP4Widgets.CComplexLineWidget):

  MODEL_CLASS = CCP4ModelData.CMonomer

  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = {'gridLayout':True}
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis)
    if model is not None: self.setModel(model)
    self.layout().addWidget(QtGui.QLabel('Identifier:',self),0,1)
    if self.editable:
      self.widgets['identifier'] = CCP4Widgets.CStringView(self,qualifiers=qualis)
    else:
      self.widgets['identifier'] = CCP4Widgets.CLabel(self,qualifiers=qualis)
    self.layout().addWidget(self.widgets['identifier'],0,2)
    self.layout().addWidget(QtGui.QLabel('Formula:',self),0,3)
    if self.editable:
      self.widgets['formula'] = CCP4Widgets.CStringView(self,qualifiers=qualis)
    else:
      self.widgets['formula'] = CCP4Widgets.CLabel(self,qualifiers=qualis)
    self.layout().addWidget(self.widgets['formula'],0,4)

    self.layout().addWidget(QtGui.QLabel('Dictionary name:',self),1,1)
    if self.editable:
      self.widgets['dictionaryName'] = CCP4Widgets.CStringView(self,qualifiers=qualis)
    else:
      self.widgets['dictionaryName'] = CCP4Widgets.CLabel(self,qualifiers=qualis)
    self.layout().addWidget(self.widgets['dictionaryName'],1,2)
    
    self.layout().addWidget(QtGui.QLabel('Smiles string:',self),1,3)
    if self.editable:
      self.widgets['smiles'] = CCP4Widgets.CStringView(self,qualifiers=qualis)
    else:
      self.widgets['smiles'] = CCP4Widgets.CLabel(self,qualifiers=qualis)
    self.layout().addWidget(self.widgets['smiles'],1,4)


    '''
    toolTip = self.model.qualifiers('toolTip')
    if toolTip is NotImplemented:
      toolTip = ''
    else:
      toolTip = toolTip
    
      
    for item in self.model.CONTENTS_ORDER:
      widget = self.widgets[item]
      tT = self.model.getDataObjects(item).qualifiers('toolTip')
      if tT is NotImplemented: tT = ''
      widget.setToolTip(toolTip+tT)
      if self.editable:
        self.connect(widget,QtCore.SIGNAL(widget.editSignal()),self.updateModelFromView)
        self.connect(widget,QtCore.SIGNAL('acceptDropData'),self.acceptDropData)
    '''

class CPdbEnsembleItemView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4ModelData.CPdbEnsembleItem

  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = {'vboxLayout':True,'iconButton':False}
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis)
    if model is not None: self.setModel(model)
    self.widgets = {}
    self.widgets['structure'] = CPdbDataFileView(self,qualifiers={'iconButton':True,'iconName':'PdbDataFile','ifAtomSelection' : True})
    self.layout().addWidget(self.widgets['structure'])
    self.widgets['identity_to_target'] = CCP4Widgets.CFloatView(self)
    self.widgets['rms_to_target'] = CCP4Widgets.CFloatView(self)
    self.widgets['number'] = CCP4Widgets.CIntView(self, qualifiers = { 'editable' : self.editable, 'guiMode' : 'combo', 'enumerators' : [i for i in range(21)], 'menuText' : [str(i) for i in range(21)]})
    self.widgets['number'].setMaximumWidth(60)
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Copies:',self))
    line.addWidget(self.widgets['number'])
    line.addWidget(QtGui.QLabel('Sequence identity:',self))
    line.addWidget(self.widgets['identity_to_target'])
    line.addWidget(QtGui.QLabel('OR RMS difference:',self))
    line.addWidget(self.widgets['rms_to_target'])
    self.layout().addLayout(line)

  def setModel(self, model):
    ensemble = None
    if model is not None and hasattr(model,'parent') and model.parent() is not None:
      ensemble = model.parent().parent()
    cEnsembleLabelView = self.parent().findChildren(CEnsembleLabelView)[0]
    if self.model is not None:
      for item in ['structure','identity_to_target','rms_to_target']:
        self.disconnect(self.model.get(item),QtCore.SIGNAL('dataChanged'),self.validate)
      if ensemble is not None:
        for item in ['number']:
          self.disconnect(ensemble.get(item),QtCore.SIGNAL('dataChanged'),self.numberChanged)
  
    if model is None or isinstance(model,self.MODEL_CLASS):
      from CCP4Widgets import CViewWidget
      CViewWidget.setModel(self,model)
      
      if model is not None:
        self.connect(model,QtCore.SIGNAL('dataChanged'),self.validate)
        toolTip = model.qualifiers('toolTip')
        if toolTip is not NotImplemented and toolTip is not None  and self.iconButton is not None:
          self.iconButton.setToolTip(toolTip+'\n'+self.iconButton.toolTip())
        for key,w in self.widgets.items():
          if key in ['structure','identity_to_target','rms_to_target']:
            if isinstance(w,CViewWidget): w.setModel(model.get(key))
          elif key in ['number']:
            if ensemble is not None:
              if isinstance(w,CViewWidget): w.setModel(ensemble.get(key))
      else:
        for key,w in self.widgets.items():
           if isinstance(w,CViewWidget): w.setModel(None)
  
    # Set allowUndefined True for the first CPdbEnsembleItem in the first CEnsemble where the CEnsembleList
    # is allowed zero length
    if model is not None and ensemble is not None:
      if isinstance(ensemble,CCP4ModelData.CEnsemble) and ensemble.qualifiers('allowUndefined'):
        if model.parent().index(model) == 0:
          model.setQualifier('allowUndefined',True)
          model.structure.setQualifier('allowUndefined',True)

    if model is not None and self.editable:
      for item in ['structure','identity_to_target','rms_to_target']:
        self.connect(model.get(item),QtCore.SIGNAL('dataChanged'),self.validate)
      for item in ['number']:
        if ensemble is not None:
          self.connect(ensemble.get(item),QtCore.SIGNAL('dataChanged'),self.numberChanged)

    if self.widgets['structure'].widgets.get('selection',None) is None:
      self.widgets['structure'].showAtomSelection()

  def updateViewFromModel(self):
    for key,widget in self.widgets.items():
      widget.updateViewFromModel()
    #self.parent().findChildren(CEnsembleLabelView)[0].updateViewFromModel()
      
  def numberChanged(self, **kw):
    cEnsembleLabelView = self.parent().findChildren(CEnsembleLabelView)[0]
    self.parent().parent().updateViewFromModel(**kw)
    cEnsembleLabelView.validate()
  
  '''
  def getMenuDef(self):
    return self.widgets['structure'].getMenuDef()

  def getActionDef(self,name):
    return self.widgets['structure'].getActionDef(name)

  def showAtomSelection(self):
    self.widgets['structure'].showAtomSelection()

  def viewContents(self):
    self.widgets['structure'].viewContents()
  '''

class CEnsembleView(CCP4Widgets.CComplexLineWidget):
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = {'vboxLayout':True}
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis)
    if model is not None: self.setModel(model)
    self.widgets = {}
    line = QtGui.QHBoxLayout()
    iconWidgetItem = self.layout().takeAt(0)
    line.addWidget(iconWidgetItem.widget())
    line.addWidget(QtGui.QLabel('Label for ensemble:',self))
    self.widgets['label'] = CCP4Widgets.CStringView(self)
    line.addWidget(self.widgets['label'])
    self.layout().addLayout(line)
    self.widgets['pdbItemList'] = CCP4Widgets.CListView(self,model=self.model.pdbItemList,qualifiers={
        'editable' : self.editable,
        'mode' : 'table',
        'tableItems': ['structure','identity_to_target','rms_to_target'],
        'columnHeaders':['Filename','Identity','RMS']  })
    self.layout().addWidget(self.widgets['pdbItemList'])

class CEnsembleLabelView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4ModelData.CEnsemble
  def __init__(self,parent=None,model=None,qualifiers={}):
    CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualifiers=qualifiers)
    self.widgets['use'] = CCP4Widgets.CBooleanView(self, qualifiers = { 'editable' : self.editable } )
    self.widgets['number'] = CCP4Widgets.CIntView(self, qualifiers = { 'editable' : self.editable, 'guiMode' : 'combo', 'enumerators' : [i for i in range(21)], 'menuText' : [str(i) for i in range(21)]})
    self.widgets['number'].setMaximumWidth(60)
    self.widgets['label'] = CCP4Widgets.CStringView(self, qualifiers = { 'editable' : self.editable } )
    self.layout().addWidget( self.widgets['use'] )
    self.layout().addWidget(QtGui.QLabel('Find',self))
    self.layout().addWidget( self.widgets['number'] )
    self.layout().addWidget(QtGui.QLabel('of search ensemble called',self))
    self.layout().addWidget( self.widgets['label'] )
    self.setModel(model)

  def validate(self,isValid=None,reportMessage=True):
    # Just validate the number and label
    if isValid is None:
      if self.model is None:
        isValid = False
      else:
        v = self.model.number.validity(self.model.number.get())
        v.extend(self.model.label.validity(self.model.label.get()))
        isValid = (v.maxSeverity()<=SEVERITY_WARNING)
    
    CCP4Widgets.CComplexLineWidget.validate(self,isValid=isValid,reportMessage=reportMessage)

class CEnsembleListView(CCP4Widgets.CTreeView):
  MODEL_CLASS = CCP4ModelData.CEnsembleList
  def __init__(self,parent=None,model=None,qualifiers={}):
    displayRole = QtCore.Qt.DisplayRole
    qualis = { 'editors' : [ { 'modelClass' : CCP4ModelData.CEnsemble, 'name' : 'ensemble', 'label':'ensemble' } ,
                             { 'modelClass' : CCP4ModelData.CPdbEnsembleItem , 'name' : 'pdbEnsembleItem','label':'structure in ensemble' } ],

               'columnHeaders':[  {displayRole:'Ensemble/Filename','width':240},
                                  {displayRole:'Selection','width':240},
                                  {displayRole:'Identity','width':50},
                                  {displayRole:'RMS','width':50}  ]
               }
    """
        'hierarchy' : [ {'name':'self','label':'ensemble','editorClass':CEnsembleLabelView, 'grey':True,  'list':
                                   [{'name':'pdbItemList','label':'structure in ensemble','editorClass':CPdbEnsembleItemView }]
                             } ],
    """
    #print 'INTO CEnsembleListView',model
    qualis.update(qualifiers)
    super(CEnsembleListView,self).__init__(parent,model=model,qualifiers=qualis)


class CPdbDataFileView(CCP4Widgets.CDataFileView):
  MODEL_CLASS = CCP4ModelData.CPdbDataFile
  def __init__(self,parent=None,model=None,qualifiers={}):
    self.ifAtomSelection = qualifiers.get('ifAtomSelection',False)
    qualis = { 'vboxLayout' : True }
    qualis.update(qualifiers)
    #print 'CPdbDataFileView parent',parent
    #CCP4Widgets.CDataFileView.__init__(self,parent=parent,model=model,qualifiers=qualis)
    super(CPdbDataFileView,self).__init__(parent=parent,model=model,qualifiers=qualis)
    #if self.ifAtomSelection: self.showAtomSelection()
  
  def getMenuDef(self):
    if self.editable:
      #menu = ['clear','view','annotate','sep','copy','paste','help']
      menu = ['clear',['View','quick_view','view_text','view_CCP4mg','view_Coot'],'sep','copy','paste','help']
      if self.ifAtomSelection: menu.insert(menu.index('sep'),'select')
    else:
      if self.role is not None and self.role == 'output':
        menu = [['View','quick_view','view_text','view_CCP4mg','view_Coot'],'sep','copy','editLabel','export','help']
      else:
        menu = [['View','quick_view','view_text','view_CCP4mg','view_Coot'],'sep','copy','export','help']
    if self._stacked: menu.insert(0,'handleStack')
    if self.ifInfo: menu.insert(menu.index('sep'),'annotate')
    return menu
  
  def getActionDef(self,name):
    if name == 'select':
      def iC():
        try:
            return self.widgets['selection'].isVisible()
        except:
            return False
      return dict (
        text = self.tr("Select atoms"),
        tip = self.tr('Select limited set of atoms'),
        slot = self.showAtomSelection,
        checkable = True,
        isChecked = iC
      )
    elif name == 'quick_view':
      def e():  return (self.model is not None and self.model.exists())
      return dict (
        text = self.tr("Quick view"),
        tip = self.tr('Quick view of model data'),
        slot = self.viewContents,
        enabled = e
      )
      
    else:
      return CCP4Widgets.CDataFileView.getActionDef(self,name)

  def openViewer(self,mode):
    import CCP4Modules
    if mode == 'view_text':
      CCP4Modules.WEBBROWSER().openFile(fileName=self.model.__str__(),toFront=True)
    else:
      CCP4Widgets.CDataFileView.openViewer(self,mode)

  def setModel(self,model=None):
    #print 'CPdbDataFileView.setModel',repr(model),self.widgets.has_key('selection')
    #if model is not None: print model.objectName()
    if self.model is not None and self.widgets.has_key('selection'):
      self.disconnect(self.model,QtCore.SIGNAL('dataChanged'),self.widgets['selection'].applySelection)
    
    super(CPdbDataFileView,self).setModel(model=model)
    
    if self.ifAtomSelection and model is not None:
        if 'selection' in self.widgets:
            self.widgets['selection'].setModel(model.selection)
            self.widgets['selection'].updateViewFromModel()
    elif model is not None and model.selection.isSet():
        if 'selection' not in self.widgets: self.showAtomSelection()
        self.widgets['selection'].setModel(model.selection)
        self.widgets['selection'].updateViewFromModel()
            
  def getValue(self):
    val = CCP4Widgets.CDataFileView.getValue(self)
    if getattr(self,'selectionLine',None) is not None:
      val['selection'] = self.widgets['selection'].getValue()
    #print 'CPdbDataFileView.getValue',val
    return val

  def setValue(self,value):
    #print 'CPdbDataFileView.setValue',self.model.objectName(),value
    CCP4Widgets.CDataFileView.setValue(self,value)
    if value.get('selection',None) is not None:
      self.showAtomSelection()
      self.widgets['selection'].setValue(value.get('selection',{}))
    elif  getattr(self,'selectionLine',None) is not None:
      self.widgets['selection'].clear()
    else:
      return
    self.widgets['selection'].applySelection()
      

  def showAtomSelection(self):
    #print 'CPdbDataFileView.showAtomSelection'
    import sys,os
    import CCP4Utils
    if self.widgets.get('selection',None) is None:
      self.selectionLine = QtGui.QFrame(self)
      self.selectionLine.setLayout(QtGui.QHBoxLayout())
      self.selectionLine.layout().setSpacing(0)
      self.selectionLine.layout().setContentsMargins(0,0,0,0)
      self.selectionLine.layout().addWidget(QtGui.QLabel('Atom selection'))
      selection = None
      if hasattr(self,'model') and self.model is not None:
          selection = self.model.selection
      self.widgets['selection'] = CAtomSelectionView(parent=self,model=selection, qualifiers= { 'editable' :self.editable} )
      
      if hasattr(self,'model') and self.model is not None:
          self.connect(self.model,QtCore.SIGNAL('dataChanged'),self.widgets['selection'].applySelection)
      self.selectionLine.layout().addWidget(self.widgets['selection'])
      self.layout().addWidget(self.selectionLine)

      simpleSelectionsCheck = QtGui.QCheckBox('Simple selections ...')
      self.layout().addWidget(simpleSelectionsCheck)

      expanded_cb_path = os.path.abspath(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','toc-minus.png'))
      unexpanded_cb_path = os.path.abspath(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','toc-plus.png'))

      expandStyle = """
      QCheckBox::indicator:unchecked {
         image: url("""+unexpanded_cb_path+""");
      }

      QCheckBox::indicator:checked {
          image: url("""+expanded_cb_path+""");
      }
      """
      simpleSelectionsCheck.setStyleSheet(expandStyle)

      self.selectionTypes = QtGui.QFrame(self)
      self.layout().addWidget(self.selectionTypes)
      self.chainsWidget = QtGui.QFrame(self)
      self.layout().addWidget(self.chainsWidget)
      if self.editable:
          self.selectionTypes.setLayout(QtGui.QGridLayout())
          self.chainsWidget.setLayout(QtGui.QGridLayout())
          self.selectionTypes.layout().addWidget(QtGui.QLabel('Residue types'),0,0)
          peptideWidget = QtGui.QCheckBox('Peptide')
          nucleicWidget = QtGui.QCheckBox('Nucleic acid')
          ligandWidget = QtGui.QCheckBox('Ligands')
          waterWidget = QtGui.QCheckBox('Water')
          soluteWidget = QtGui.QCheckBox('Solute')
          saccharideWidget = QtGui.QCheckBox('Saccharide')
          nucleotideWidget = QtGui.QCheckBox('Nucleotide')
          metalWidget = QtGui.QCheckBox('Metal')
          def interpretSimpleSelections():
              sel = ''
              if peptideWidget.isChecked():
                  sel += 'peptide'
              if nucleicWidget.isChecked():
                  if len(sel)>0:
                      sel += ' or nucleic'
                  else:
                      sel = 'nucleic'
              if ligandWidget.isChecked():
                  if len(sel)>0:
                      sel += ' or ligands'
                  else:
                      sel = 'ligands'
              if waterWidget.isChecked():
                  if len(sel)>0:
                      sel += ' or water'
                  else:
                      sel = 'water'
              if soluteWidget.isChecked():
                  if len(sel)>0:
                      sel += ' or solute'
                  else:
                      sel = 'solute'
              if saccharideWidget.isChecked():
                  if len(sel)>0:
                      sel += ' or saccharide'
                  else:
                      sel = 'saccharide'
              if nucleotideWidget.isChecked():
                  if len(sel)>0:
                      sel += ' or nucleotide'
                  else:
                      sel = 'nucleotide'
              if metalWidget.isChecked():
                  if len(sel)>0:
                      sel += ' or metal'
                  else:
                      sel = 'metal'


              chains = self.chainsWidget.findChildren(QtGui.QCheckBox)
              chainsel = ''
              for ch in chains:
                  #print ch.isChecked(), ch.text()
                  if ch.isChecked():
                      if len(chainsel)>0:
                          chainsel += " or " + str(ch.text())+"/"
                      else:
                          chainsel = str(ch.text())+"/"
              #print chainsel; sys.stdout.flush()

              if len(chainsel)>0 and len(sel)>0:
                 sel = "{"+sel+"} and {"+chainsel+"}"
              elif len(sel)==0 and len(chainsel)>0:
                 sel = chainsel
              val = {'selection':{'text':sel}}
              CCP4Widgets.CDataFileView.setValue(self,val)
              self.widgets['selection'].setValue(val)
              self.widgets['selection'].applySelection()
              # Surely should not have to do this? But applySelection0 seems not to work...
              self.widgets['selection'].connectUpdateViewFromModel(False)
              self.widgets['selection'].model.text.set(sel)
              self.widgets['selection'].connectUpdateViewFromModel(True)
    
          peptideWidget.stateChanged.connect(interpretSimpleSelections)
          nucleicWidget.stateChanged.connect(interpretSimpleSelections)
          ligandWidget.stateChanged.connect(interpretSimpleSelections)
          waterWidget.stateChanged.connect(interpretSimpleSelections)
          soluteWidget.stateChanged.connect(interpretSimpleSelections)
          saccharideWidget.stateChanged.connect(interpretSimpleSelections)
          nucleotideWidget.stateChanged.connect(interpretSimpleSelections)
          metalWidget.stateChanged.connect(interpretSimpleSelections)
          self.selectionTypes.layout().addWidget(peptideWidget,1,0)
          self.selectionTypes.layout().addWidget(nucleicWidget,2,0)
          self.selectionTypes.layout().addWidget(ligandWidget,3,0)
          self.selectionTypes.layout().addWidget(waterWidget,4,0)
          self.selectionTypes.layout().addWidget(soluteWidget,1,1)
          self.selectionTypes.layout().addWidget(saccharideWidget,2,1)
          self.selectionTypes.layout().addWidget(metalWidget,3,1)
          self.selectionTypes.layout().addWidget(nucleotideWidget,4,1)

          def fillChainsWidget():
              #print "fillChainsWidget"; sys.stdout.flush()
              if hasattr(self,"model") and hasattr(self.model,"fileContent") and hasattr(self.model.fileContent,"composition") and hasattr(self.model.fileContent.composition,"chains"):
                  if len(self.model.fileContent.composition.chains)<30: # and len(self.model.fileContent.composition.chains)>1:
                      self.chainsWidget.layout().addWidget(QtGui.QLabel('Chains'),0,0)
                      for i in range(len(self.model.fileContent.composition.chains)):
                          col = i / 8
                          row = i % 8 + 1
                          chainWidget = QtGui.QCheckBox(self.model.fileContent.composition.chains[i])
                          self.chainsWidget.layout().addWidget(chainWidget,row,col)
                          chainWidget.stateChanged.connect(interpretSimpleSelections)

          fillChainsWidget()

          def clearLayout(layout):
              #print "clearLayout"; sys.stdout.flush()
              #print self.model.fileContent.molHnd
              while layout.count():
                  child = layout.takeAt(0)
                  if child.widget():
                      child.widget().deleteLater()

          def dataIsChanged():
              #print "dataIsChanged"; sys.stdout.flush()
              if not self.model.fileContent.molHnd is self._old_molHnd:
                  clearLayout(self.chainsWidget.layout())
                  fillChainsWidget()
                  self._old_molHnd = self.model.fileContent.molHnd

          if hasattr(self,"model") and hasattr(self.model,"fileContent") and hasattr(self.model.fileContent,"composition") and hasattr(self.model.fileContent.composition,"chains"):
              self._old_molHnd = self.model.fileContent.molHnd
          else:
              self._old_molHnd = None

          self.connect(self.model,QtCore.SIGNAL('dataChanged'),dataIsChanged)
 
    elif not self.widgets['selection'].isVisible() and simpleSelectionsCheck.isChecked():
      self.selectionLine.show()
      self.selectionTypes.show()
      self.chainsWidget.show()
    else:
      self.selectionLine.hide()
      self.selectionTypes.hide()
      self.chainsWidget.hide()
      simpleSelectionsCheck.hide()

    simpleSelectionsCheck.clicked.connect(self.selectionTypes.setVisible)
    simpleSelectionsCheck.clicked.connect(self.chainsWidget.setVisible)
    self.selectionTypes.hide()
    self.chainsWidget.hide()

  def viewContents(self):
    if not hasattr(self,'contentsViewer'):
      self.contentsViewer = CPdbContentsViewer(self)
      try:
        fileContent = self.model.fileContent
      except:
        pass
      self.connect(self.model.fileContent,QtCore.SIGNAL('dataChanged'),self.contentsViewer.load)
      self.contentsViewer.load()
    self.contentsViewer.show()
    
  def setFocus1(self,reason,index=None):
    #print 'CPdbDataFileView.setFocus1',index
    if index is None:
      CCP4Widgets.CDataFileView.setFocus(self,reason)
    elif index==1 and self.ifAtomSelection:
      self.showAtomSelection()
      self.selectionLine.setFocus(reason)
    else:
      self.jobCombo.setFocus(reason)

  def handleBrowserOpenFile(self,filename,downloadInfo,**kw):
    kw['validate'] = False
    kw['updateView'] = False
    CCP4Widgets.CDataFileView.handleBrowserOpenFile(self,filename,downloadInfo,**kw)
    err = self.model.importFile(jobId=self.parentTaskWidget().jobId(),jobNumber=self.parentTaskWidget().jobNumber())
    if err is not None and len(err)>0:
      if err.maxSeverity()>SEVERITY_WARNING:
        message = 'File failed validation test'
        err.warningMessage(windowTitle='Importing coordinate file',parent=self,message=message)
        self.model.unSet()
    self.updateViewFromModel()
    self.validate()
    


class CPdbContentsViewer(QtGui.QDialog):
  def __init__(self,parent):
    QtGui.QDialog.__init__(self,parent)
    self.setLayout(QtGui.QVBoxLayout())
    self.textEdit = QtGui.QTextEdit(self)
    self.textEdit.setReadOnly(True)
    self.layout().addWidget(self.textEdit)
    butBox = QtGui.QDialogButtonBox(self)
    self.layout().addWidget(butBox)
    but = butBox.addButton(QtGui.QDialogButtonBox.Close)
    self.connect(but,QtCore.SIGNAL('released()'),self.close)

  def load(self):
    #print 'CPdbContentsViewer.load', self.parent().model.fileContent.composition.chains
    self.textEdit.setReadOnly(False)
    self.textEdit.clear()
    if self.parent().model.annotation.isSet():
      self.setWindowTitle(self.parent().model.annotation.__str__())
    else:
      self.setWindowTitle(self.parent().model.__str__())
    text = '\nSpace group: ' + str(self.parent().model.fileContent.mmdbManager.GetSpaceGroup())
    try:
      cell = self.parent().model.fileContent.mmdbManager.GetCell()
      text = text + '\nCell:'
      for ii in range(1,7):
        text = text + '  ' + str(cell[ii])
    except:
      pass
     
    if len( self.parent().model.fileContent.composition.chains)>0:
      text = text + '\n\nChains in model:\n'
      comp = self.parent().model.fileContent.composition
      for i in range(len(comp.chains)):
        text = text + str(comp.chains[i])+ '  ' + comp.chainInfo[i][1].split('/')[3] + ' - ' +  comp.chainInfo[i][2].split('/')[3] + \
           '  ('+str(comp.chainInfo[i][0]) +' residues)\n'
    
    if len( self.parent().model.fileContent.composition.monomers)>0:
      text = text + '\n\nMonomers in model:\n'+self.parent().model.fileContent.composition.monomers[0]
      for mon in self.parent().model.fileContent.composition.monomers[1:]:
        text = text + ', ' + str(mon)
    self.textEdit.document().setPlainText(text)
    self.textEdit.setReadOnly(True)
  
class CPdbDataFileListView(CCP4Widgets.CListView):
  MODEL_CLASS = CCP4ModelData.CPdbDataFileList
  def __init__(self,parent=None,model=None,qualifiers={}):

    #print 'CPdbDataFileListView'
    qualis = { 'mode' : 'table',
               'tableItems' : ['fullPath','selection'] ,
               'columnHeaders':['Coordinate file','Selection'],
               }
    qualis.update(qualifiers)
    CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis)
    # Tis broke! self.editor.showAtomSelection()

class CAtomSelectionView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4ModelData.CAtomSelection
  ERROR_CODES = { 101 : { 'description' : 'No CPdbDataFile when applying atom selection' },
                  102 :  { 'description' : 'Error applying atom selection' }
                  }

  def __init__(self,parent=None,model=None,qualifiers={}):
    super(CAtomSelectionView,self).__init__(parent=parent,qualifiers=qualifiers)
    
    if self.editable:
      self.widgets['text'] = CCP4Widgets.CLineEdit(self)
      self.widgets['text'].setToolTip("Enter selection command e.g. 'A/ or B/11-21'")
      self.setFocusProxy(self.widgets['text'])
    else:
      self.widgets['text'] = CCP4Widgets.CLabel(self)
    self.layout().addWidget(self.widgets['text'])
    self.label = QtGui.QLabel(' ( 0 atoms)',self)
    self.layout().addWidget(self.label)
    self.help = QtGui.QPushButton('Help',self)
    self.help.setToolTip('Details of atom selection syntax')
    self.layout().addWidget(self.help)
    self.connect(self.widgets['text'],QtCore.SIGNAL('textEdited(const QString &)'),self.applySelection0)
    self.connect(self.help,QtCore.SIGNAL('released()'),self.showHelp)
    if self.parent().model is not None: self.connect(self.parent().model,QtCore.SIGNAL('dataLoaded'),self.applySelection)
    if model is not None:
      self.setModel(model)
      self.updateViewFromModel()

  def validate(self,isValid=None,reportMessage=True):
    pass

  def showHelp(self):
    import CCP4Modules
    CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='general/atom_selection.html')


  def setValue(self,value={}):
    CCP4Widgets.CComplexLineWidget.setValue(self,value=value)
    try:
      self.applySelection()
    except:
      self.label.setText('No atoms')


  def updateViewFromModel(self):
    #print 'CAtomSelectionView.updateViewFromModel'
    #import traceback
    #traceback.print_stack(limit=10)
    self.widgets['text'].blockSignals(True)
    self.widgets['text'].setText(self.model.__str__())
    self.widgets['text'].blockSignals(False)
    try:
      self.applySelection()
    except:
      self.label.setText('No atoms')
      
  def clear(self):
    CCP4Widgets.CComplexLineWidget.clear(self)
    self.widgets['text'].clear()
    self.label.setText('No atoms')

  def  applySelection0(self,text=None):    
    try:
      self.applySelection(text=text)
    except:
      pass
    else:
      self.connectUpdateViewFromModel(False)
      self.model.text.set(text)
      self.connectUpdateViewFromModel(True)
    
  def applySelection(self,text=None):
    #print 'CSelectionLine.applySelection',text
    if text is None: text = self.widgets['text'].text()
    if isinstance(self.model.parent(),CCP4ModelData.CPdbDataFile):
      pdbDataObj = self.model.parent()
    else:
      pdbDataObj = self.model.getDataByKey('pdbFileKey')
    #print 'CSelectionLine.applySelection',text,pdbDataObj
    if pdbDataObj is None:
      try:
        pdbDataObj = self.model.parent()
      except:
        self.label.setText('No atoms')
        raise CException(self.__class__,101,name=self.modelObjectPath())
    try:
      # Call to loadFile removed from here - CPdbDataFile.updateData() unsets fileContent
      # so this accessing fileContent should cause a loadFile() if necessasy
      nSelAtoms,selHnd = pdbDataObj.fileContent.interpretSelection(str(text))
      if selHnd is not None: pdbDataObj.fileContent.molHnd.DeleteSelection(selHnd)
    except Exception as e:
      #print 'CSelectionLine.applySelection loadFile fail\n',e
      self.label.setText('No atoms')
      raise CException(self.__class__,102,name=self.modelObjectPath())
      
    self.label.setText(' ( '+str(nSelAtoms)+' atoms)')
    
    if nSelAtoms == 0:
      self.setProperty("isValid",False)
      self.setProperty("hasWarning",True)
      self.isValid = False
      self.validityMessage = 'No atoms selected'
    else:
      self.setProperty("isValid",True)
      self.setProperty("hasWarning",False)
      self.isValid = True
      self.validityMessage = None
    self.updateValidityIndicator()



class CDictDataDialog(QtGui.QDialog):
  def __init__(self,parent=None,model=None,projectId=None):
    import CCP4Modules
    if parent is None:
      parent = CCP4Modules.QTAPPLICATION()
    QtGui.QDialog. __init__(self,parent)
    self.setLayout(QtGui.QHBoxLayout())
    self.setModal(False)
    if projectId is not None:
      pName = ' for '+CCP4Modules.PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectname')
    else:
      pName=''
    self.setWindowTitle('Manage project dictionary'+pName)
    
      
    self.frame = CDictDataView(self,model=model,projectId=projectId)
    self.layout().addWidget(self.frame)

  def closeEvent(self,event):
    import CCP4Utils,CCP4Modules
    #print 'CDictDataDialog.closeEvent',self.frame.showWidget
    if self.frame.showWidget is not  None and CCP4Utils.isAlive(self.frame.showWidget):
      CCP4Modules.WEBBROWSER().tab().deleteTabWidget(widget=self.frame.showWidget)
    event.accept()


class CDictDataView(CCP4Widgets.CViewWidget):

  def __init__(self,parent=None,model=None,projectId=None):
    CCP4Widgets.CViewWidget.__init__(self,parent=parent)
    self.showWidget = None
    if model is None:
      self.dictDataFile = CCP4ModelData.CDictDataFile()
      #self.dictDataFile.set(self.dictDataFile.defaultProjectDict(projectId=projectId))
      self.dictDataFile.loadFile()
      self.model = self.dictDataFile.fileContent
      import CCP4ProjectViewer
      import os
      CCP4ProjectViewer.FILEWATCHER().addJobPath(os.path.split( os.path.split(str(self.dictDataFile))[0])[1],self.dictDataFile.__str__())
      self.connect(CCP4ProjectViewer.FILEWATCHER(),QtCore.SIGNAL('fileChanged(const QString &)'),self.handleFileChanged)
    else:
      self.model = model
      self.dictDataFile = model.parent()
    #print 'CDictDataView',self.dictDataFile,self.model,self.model.monomerList
    QtGui.QFrame.__init__(self,parent)
    self.setLayout(QtGui.QHBoxLayout())
    self.layout().setContentsMargins(1,1,1,1)
    self.layout().setSpacing(1)
    self.monomerListWidget = CDictDataList(self)   
    self.layout().addWidget(self.monomerListWidget )
    butLayout = QtGui.QVBoxLayout()
    for label,connect in [['Show',self.handleShow],['Import geometry file',self.handleMerge],['Delete',self.handleDelete]]:
      but = QtGui.QPushButton(label,self)
      self.connect(but,QtCore.SIGNAL('released()'),connect)
      butLayout.addWidget(but)
    self.layout().addLayout(butLayout)
    self.updateViewFromModel()
    self.connect(self.model,QtCore.SIGNAL('dataChanged'),self.updateViewFromModel)
    self.connect(self.monomerListWidget,QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem *,int)'),self.handlDoubleClick)

  def handleFileChanged(self,path):
    #print 'CDictDataView.handleFileChanged',path
    if str(path) == str(self.dictDataFile):
      #print 'CDictDataView.handleFileChanged updating'
      self.dictDataFile.fileContent.loadFile(str(self.dictDataFile))
      self.updateViewFromModel()


  def handlDoubleClick(self,item,col):
    #print 'CDictDataView.handlDoubleClick',item,col
    idd = item.data(0).toStrint().__str__()
    self.handleShow(idd)

    
  def updateViewFromModel(self):
    import CCP4Utils
    self.monomerListWidget.load(self.model.monomerList)
    if self.showWidget is not  None and CCP4Utils.isAlive(self.showWidget):
      self.showWidget.reload()
      
  def updateModelFromView(self):
    # All updates should be handled by the edit methods
    pass

  def handleShow(self,idd=None):
    if idd is None:
      idd = self.monomerListWidget.currentSelection()
    import CCP4Modules,CCP4Utils
    #print 'CDictDataView.handleShow',idd,self.model,self.model.parent()
    if self.showWidget is not None: print 'alive?',CCP4Utils.isAlive(self.showWidget)
    if self.showWidget is None or (not CCP4Utils.isAlive(self.showWidget)):
      self.showWidget = CCP4Modules.WEBBROWSER().openFile(self.model.parent().__str__())
    else:
      self.showWidget.browserWindow().show()
      self.showWidget.browserWindow().raise_()
    if idd is not None:
      self.showWidget.findText(subString='data_comp_'+idd)


  def handleMerge(self):
    import CCP4FileBrowser,CCP4Modules
    self.mergeFileBrowser = CCP4FileBrowser.CFileDialog(parent=self,title='Select geometry file to merge into project geometry file',
          defaultSuffix=CCP4Modules.MIMETYPESHANDLER().getMimeTypeInfo(name='application/refmac-dictionary',info='fileExtensions'),
          filters = ['Geometry file for refinement(*.cif)'], fileMode=QtGui.QFileDialog.ExistingFiles,saveButtonText='Merge these files')
    self.mergeFileBrowser.show()
    self.connect(self.mergeFileBrowser,QtCore.SIGNAL('selectFiles'),self.mergeFiles)

  def mergeFiles(self,selectedFiles):
    if hasattr(self,'mergeFileBrowser'):
      self.mergeFileBrowser.close()
      self.mergeFileBrowser.deleteLater()
    #print 'CDictDataFileView.mergeFiles',selectedFiles
    
    for fileName in selectedFiles:
      err = self.model.mergeFile(fileName=fileName,overwrite=True)
      #print 'CDictDataFileView.mergeFiles',err.report(),err.maxSeverity()
      if err.maxSeverity()>SEVERITY_WARNING:
        err.warningMessage(windowTitle='Error merging geometry files',parent=self,message='Error attempting to merge geometry files')
      return

  def handleDelete(self):
    idd = self.monomerListWidget.currentSelection()
    #print 'CDictDataView.handleDelete',idd
    if idd is None: return
    err = self.model.delete(idd)
    if err.maxSeverity()>SEVERITY_WARNING:
      err.warningMessage(windowTitle='Error deleting item in geometry file',parent=self,message='Error attempting to edit geometry file')
    return


class CDictDataList(QtGui.QTreeWidget):
  def __init__(self,parent):
    QtGui.QTreeWidget.__init__(self,parent)
    self.setMinimumWidth(400)
    self.setColumnCount(3)
    self.setHeaderLabels(['Id','Code','Name'])
    self.setColumnWidth(0,80)
    self.setColumnWidth(1,80)
    self.setColumnWidth(2,200)
    self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
    self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
                          
  def load(self,monomerList):
    # monomerList is a Clist of CChemComp
    #print 'CDictDataList.load',monomerList
    self.clear()
    for monomer in monomerList:
      #print 'CDictDataList.load',monomer.get('id')
      qList = QtCore.QStringList()
      for item in ['id','three_letter_code','name']: qList.append(str(monomer.get(item)))
      item = QtGui.QTreeWidgetItem(qList)
      treeId=self.addTopLevelItem(item)

  def handleMonomerDeleted(self,id):
    #print 'CDictDataList.handleMonomerDeleted',id
    if self.model().rowCount()==0: return
    modInxList = self.model().match(id)
    if len(modInxList)==0:
      #print 'CDictDataList.handleMonomerDeleted no match to id',id
      pass
    else:
      self.model().removeRow(modInxList[0].row())

  def handleMonomerAdded(self,id):
    pass
      
  def currentSelection(self):
    indices = self.selectionModel().selectedRows()
    if len(indices) == 0: return None
    idd = str(indices[0].data().toString())
    return idd
    
