"""
     qtgui/CCP4XtalWidgets.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 CCP4XtalWidgets (QtGui) Collection of widgets for crystallographic data types

from PyQt4 import QtGui,QtCore
import CCP4XtalData
import  CCP4Widgets
from CCP4Utils import safeFloat
from CCP4ErrorHandling import *
import CCP4Modules


class CSpaceGroupsAbstractItemModel(QtCore.QAbstractItemModel):

  def __init__(self,parent):
     QtCore.QAbstractItemModel.__init__(self,parent)
     chiralSpaceGroups= CCP4XtalData.SYMMETRYMANAGER().chiralSpaceGroups
     crystalSystems = CCP4XtalData.SYMMETRYMANAGER().crystalSystems
     model = []
     for key in crystalSystems:
       model.append((key,chiralSpaceGroups[key]))
     self.rootItem = CTreeItem(self,'root',model)     

  def columnCount(self,parent):
    return 1

  def childCount(self):
    return self.rootItem.childCount()

  def rowCount(self,index):
    if not index.isValid():
      return self.childCount()
    else:
      return index.internalPointer().childCount()

  def child(self,row):
    return self.rootItem.child(row)

  def data(self, index, role):
    if not index.isValid():
      return None    
    item = index.internalPointer()
    #return item.data(index.column(),role)
    return item.data(role)

  def index(self, row, column, parent):
    if row < 0 or column < 0 or row >= self.rowCount(parent) or column >= self.columnCount(parent):
      return QtCore.QModelIndex()
    if not parent.isValid():
      parentItem = self.rootItem
    else:
      parentItem = parent.internalPointer()
    childItem = parentItem.child(row)
    #print 'CProjectModel.index',row, column, parent,childItem.getName()
    if childItem:
      return self.createIndex(row, column, childItem)
    else:
      return QtCore.QModelIndex()

  def parent(self,index):
    if not index.isValid():
      return QtCore.QModelIndex()
    childItem = index.internalPointer()
    parentItem = childItem.parent
    if parentItem == self.rootItem:
      return QtCore.QModelIndex()
    return self.createIndex(parentItem.row(), 0, parentItem)

class CSpaceGroupTreeView(QtGui.QTreeView):
  
    def mousePressEvent(self,event):
      if event.button() == QtCore.Qt.LeftButton:
        index = self.indexAt(event.pos())
        #print 'CSpaceGroupTreeView.mousePressEvent',index.row(),index.parent().isValid(),self.parent().parent()
        if index.isValid() and not index.parent().isValid():
          #print 'CSpaceGroupTreeView.mousePressEvent expanding'
          self.setExpanded(index,(not self.isExpanded(index)))
          # Put temporary block on cling the popup menu
          self.parent().parent().blockClose = True
          event.accept()
          return
        
class CSpaceGroupCombo(QtGui.QComboBox):

  def __init__(self,parent):
    QtGui.QComboBox.__init__(self,parent)
    self.blockClose = False

  def hidePopup(self):
    #print 'CSpaceGroupCombo.hidePopup'
    if self.blockClose:
      self.blockClose = False
      return
    QtGui.QComboBox.hidePopup(self)


class CSpaceGroupView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4XtalData.CSpaceGroup
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = {}
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis)
    wQualis = { }
    wQualis.update(qualis)
    wQualis['charWidth'] = 12
    self.layout().addWidget(QtGui.QLabel(qualifiers.get('label','Space group'),self))
    if self.editable:
      self.widget = CCP4Widgets.CSearchBox()
      self.widget.setHitList( CCP4XtalData.SYMMETRYMANAGER().nonEnantiogenicList())
      self.connect(self.widget,QtCore.SIGNAL('changed'),self.updateModelFromView)
    else:
      self.widget = QtGui.QLabel(self)
    self.layout().addWidget(self.widget)
    self.setModel(model)
    #print 'CSpaceGroupView.__init__',model
    self.connect(self.widget,QtCore.SIGNAL('dataSelected'),self.updateModelFromView)
  


class CCellView(CCP4Widgets.CComplexLineWidget):

    MODEL_CLASS = CCP4XtalData.CCell
    ERROR_CODES = { }

    STRETCH = 5

    def __init__(self,parent=None,model=None,qualifiers={}):
      qualis = {'gridLayout':True}
      qualis.update(qualifiers)
      #print 'CCellView.__init__',qualifiers,qualis; import sys; sys.stdout.flush()
      CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualifiers=qualis)
      #print 'CCellView.__init__',self.layout()

      for row,rowItems in [ [ 0, ['a','b','c']],[1,['alpha','beta','gamma']]]:
        col = 0
        for item in rowItems:
          if item in ['alpha','beta','gamma']:
            label = QtGui.QLabel('&'+item+';')
          else:
            label = QtGui.QLabel(item)
          label.setTextFormat(QtCore.Qt.RichText)
          col = col+1
          self.layout().addWidget(label,row,col)
          if self.editable:
            if model is not None:
              wQualis = model.get(item).qualifiers()
            else:
              wQualis = {}
            if model is not None:
              wQualis['toolTip']=model.getDataObjects(item).qualifiers('toolTip')
            self.widgets[item] = CCP4Widgets.CFloatView(self,qualifiers=wQualis)            
            self.connect(self.widgets[item],QtCore.SIGNAL('editingFinished()'),self.updateModelFromView)
            self.connect(self.widgets[item],QtCore.SIGNAL('acceptDropData'),self.acceptDropData)
            #w.setReadOnly(1-self.editable)
          else:
            self.widgets[item] = CCP4Widgets.CLabel(self,qualis,uneditable=True)
          col = col+1
          self.layout().addWidget(self.widgets[item],row,col)
          #self.layout().setStretchFactor(self.widgets[item],2)

      if self.editable and model is not None:
        self.setModel(model)
  

    def getMenuDef(self):
      return ['copy','paste','help']
  
    def updateModelFromText(self):
      self.updateModelFromView()

    

      
class CSpaceGroupCellView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4XtalData.CSpaceGroupCell
  def __init__(self,parent=None,model=None,qualifiers={}):
      qualis = { 'gridLayout' : True,
                 'iconName' : 'cell' }
      qualis.update(qualifiers)
      CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualifiers=qualis)
      wQualis = { 'charWidth':12 }
      if qualis.get('editable',True):
        self.widgets['spaceGroup'] = CCP4Widgets.CSearchBox()      
        self.widgets['spaceGroup'].setHitList( CCP4XtalData.SYMMETRYMANAGER().nonEnantiogenicList())
        self.connect(self.widgets['spaceGroup'],QtCore.SIGNAL('changed'),self.updateModelFromView)
      else:
        self.widgets['spaceGroup'] = CCP4Widgets.CLabel(self)
      #self.widgets['spaceGroup'] = CSpaceGroupView(self,qualifiers=wQualis)
      layout = QtGui.QHBoxLayout()
      layout.addWidget(QtGui.QLabel('SpaceGroup',self))
      layout.addWidget(self.widgets['spaceGroup'])
      self.layout().addLayout(layout,1,1)
      if self.editable:
        self.loadButton = QtGui.QPushButton('Load from another file',self)
        self.loadButton.setToolTip('Take cell parameters from a different PDB or MTZ file')
        self.layout().addWidget(self.loadButton,0,1)
        self.connect(self.loadButton,QtCore.SIGNAL('clicked()'),self.handleLoadButton)
      self.widgets['cell'] = CCellView(parent=self.parent(),qualifiers=qualis)
      self.widgets['cell'].iconButton.deleteLater()
      self.layout().addWidget(self.widgets['cell'],0,2,2,1)
      #print 'CSpaceGroupCellView widgets',self.widgets
          
      self.setModel(model)


  def handleLoadButton(self):
    #print 'CSpaceGroupCellView.handleLoadButton'
    import CCP4Data
    filterText = [ 'Coordinate file (*.pdb *.cif *.ent)' ,'Experimental data file (*.mtz *.cif *.ent)']
    import CCP4FileBrowser
    self.fileBrowser = CCP4FileBrowser.CFileDialog(self,
         title='Select experimental data file or coordinate file',
         filters= filterText,
         defaultFileName='')
    self.fileBrowser.setStyleSheet("")
    self.fileBrowser.setDownloadMode(modeList=['ebiPdb','ebiSFs'],projectId=self.parentTaskWidget().projectId())
    self.connect(self.fileBrowser,QtCore.SIGNAL('selectFile'),self.loadFromFile)
    self.fileBrowser.show()
        
    
  def loadFromFile(self,fileName):
    #print 'CSpaceGroupCellView.loadFromFile',fileName,type(fileName)
    import os
    ext = os.path.splitext(fileName)[1]
    self.model.blockSignals(True)
    #print 'CSpaceGroupCellView.loadFromFile',ext
    if ext == '.pdb':
      import CCP4ModelData
      obj = CCP4ModelData.CPdbDataFile(fileName)
      #print 'CSpaceGroupCellView.loadFromFile',obj.fileContent.mmdbManager.GetCell(),obj.fileContent.mmdbManager.GetSpaceGroup()
      self.model.unSet()
      try:
        self.model.cell.set(obj.fileContent.mmdbManager.GetCell()[1:7] )
      except:
        pass
      try:
        self.model.spaceGroup.set(obj.fileContent.mmdbManager.GetSpaceGroup() )
      except:
        pass
    elif ext == '.mtz':
      obj = CCP4XtalData.CMtzDataFile(fileName)
      self.model.unSet()
      try:
        self.model.spaceGroup.set(obj.fileContent.spaceGroup)
      except:
        pass
      try:
        self.model.cell.set(obj.fileContent.cell)
      except:
        pass
    self.model.blockSignals(False)
    self.updateViewFromModel()
    self.validate()
      
    
  def dropTypes(self):
    return ['MtzDataFile','MiniMtzDataFile','ObsDataFile','PhsDataFile','FreeRDataFile','MapCoeffsDataFile','PdbDataFile']
    
  def acceptDropData(self,textData):
    #print 'CSpaceGroupCellView.acceptDropData',textData
    from lxml import etree
    tree = etree.fromstring(textData)
    import os
    try:
      path = os.path.join(tree.xpath('//relPath')[0].text.__str__(),tree.xpath('//baseName')[0].text.__str__())
      #print 'CSpaceGroupCellView.acceptDropData path',path
    except:
      pass
    else:
      self.loadFromFile(path)

class CTreeItem:
  def __init__(self,parent=None,data={},children=[]):
    #if len(data)>0:
    #  print 'CTreeItem.__init__',self,parent,str(data[QtCore.Qt.DisplayRole].toString())
    self.parent = parent
    self.myData = {}
    if isinstance(data,dict):
      self.myData.update(data)
    else:
      self.myData[QtCore.Qt.DisplayRole] = data
    self.children = []
    for c in children:
      if isinstance(c,(list,tuple)):
        self.appendChild(CTreeItem(self,c[0],c[1]))
      else:
        self.appendChild(CTreeItem(self,c))

  def childCount(self):
    return len(self.children)

  def child(self,row):
    if row>=0 and row<len(self.children):
      return self.children[row]
    else:
      return None

  def appendChild(self,item):
     self.children.append(item)

  def row(self):
    if self.parent is not None:
      return self.parent.children.index(self)
    else:
      return 0
    
  def data(self,role):
    return self.myData.get(role,QtCore.QVariant())

  def findChild(self,role,value):
    if not isinstance(value,QtCore.QVariant): value = QtCore.QVariant(value)
    for child in self.children:
      if child.myData.has_key(role) and child.myData[role] == value:
        return child
    return None

class CProgramColumnGroupModel(QtCore.QAbstractItemModel):

  def __init__(self,parent=None,mtzData=None):
    QtCore.QAbstractItemModel.__init__(self,parent)
    self.clear()

  def clear(self):
    self.rootItem = CTreeItem()

  def setMtzData(self,mtzData=None,programGroupTypes=None,programColumnGroup=None):
    #print 'CProgramColumnGroupModel.setMtzData',repr(mtzData)
    self.clear()
    #ds = CTreeItem (self.rootItem,{ QtCore.Qt.DisplayRole : QtCore.QVariant(str('')) } )
    #self.rootItem.appendChild( ds )
    datasetIndex = -1
    for dataset in mtzData.datasets:
      datasetIndex = datasetIndex + 1
      #print 'CProgramColumnGroupModel.setMtzData',dataset.name,datasetIndex
      columnGroupIndex = -1
      datasetListed = False
      for columnGroup in dataset.columnGroups:
        #print 'CProgramColumnGroupModel.setMtzData columnGroup',columnGroup
        columnGroupIndex = columnGroupIndex + 1
        if self.isRightType(columnGroup,programGroupTypes,programColumnGroup):
          if not datasetListed:
            ds = CTreeItem (self.rootItem,{ QtCore.Qt.DisplayRole : QtCore.QVariant(str(dataset.name)) } )
            self.rootItem.appendChild( ds )
            datasetListed = True
          ds.appendChild(CTreeItem(ds,{ QtCore.Qt.DisplayRole :  QtCore.QVariant(columnGroup.guiLabel()),
                                        QtCore.Qt.UserRole :  QtCore.QVariant(str(datasetIndex)+'.'+str(columnGroupIndex) ) }) )
    #print 'CProgramColumnGroupModel.setMtzData', self.rootItem.childCount(), self.nRows
    
  def isRightType(self,columnGroup,programGroupTypes,programColumnGroup):
    if len(programGroupTypes)>0:
      return (str(columnGroup.groupType) in programGroupTypes)
    else:
      if len(columnGroup.columns) != len(programColumnGroup): return False
      for i in range(len(columnGroup.columns)):
        if columnGroup.columns[i].columnType not in programColumnGroup[i].columnType:
          #print 'mismatch',columnGroup.columns[i].columnType,programColumnGroup[i].columnType
          return False
      return True

    
  def flags(self,modelIndex):
    #if not modelIndex.parent().isValid() and not modelIndex.row() == 0:
    if not modelIndex.parent().isValid():
      return QtCore.Qt.ItemIsEnabled
    else:
      return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable

  def data(self,modelIndex,role):
    if not modelIndex.isValid(): return None
    item = modelIndex.internalPointer()
    return item.data(role)


  def headerData(self,section,orientation,role):
    return QtCore.QVariant()

  def rowCount(self,modelIndex):
    if modelIndex.column() > 0:
      return 0
    if not modelIndex.isValid():
      parentItem = self.rootItem
    else:
      parentItem = modelIndex.internalPointer()
    return parentItem.childCount()

  def columnCount(self,modelIndex):   
    return 1


  def index(self, row, column, parent):
    if not self.hasIndex(row, column, parent):
      return QtCore.QModelIndex()
    if not parent.isValid():
      parentItem = self.rootItem
    else:
      parentItem = parent.internalPointer()
    childItem = parentItem.child(row)
    if childItem is not None:
      return self.createIndex(row, column, childItem)
    else:
      return QtCore.QModelIndex()	

  def parent(self, modelIndex):
    if not modelIndex.isValid():
      return QtCore.QModelIndex()
    childItem = modelIndex.internalPointer()
    parentItem = childItem.parent
    if parentItem is None:
      return None
    elif parentItem == self.rootItem:
      return QtCore.QModelIndex()
    else:
      #print 'CProgramColumnGroupModel.parent',parentItem.row()
      return self.createIndex(parentItem.row(), 0, parentItem)
    

class CProgramColumnGroupQtView(QtGui.QTreeView):

  def __init__(self,parent=None):
    QtGui.QTreeView.__init__(self,parent)
    self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
    self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
    self.header().setVisible(False)

class  CProgramColumnGroupCombo( QtGui.QComboBox ):
  def __init__(self,parent):
    QtGui.QComboBox.__init__(self,parent)
    self.setModel(CProgramColumnGroupModel(self))
    self.setView(CProgramColumnGroupQtView(self))
    #self.view().setSelectionModel(QtGui.QItemSelectionModel(self.model()))
    #self.connect(self.view().selectionModel(),QtCore.SIGNAL('selectionChanged(const QItemSelection&,const QItemSelection&)'),self.beep)
    self.setEditable(False)

  def beep(self):
    print 'CProgramColumnGroupCombo.beep',self.currentIndex(),self.view().selectionModel().currentIndex().row()
    
  def currentColumnGroupIndices(self):
    modelIndex = self.view().selectionModel().currentIndex()
    if modelIndex.isValid():
      data = modelIndex.internalPointer().data(QtCore.Qt.UserRole)
      if data is None: return -1,-1
      val = data.toString().__str__().split('.')
      #print 'currentColumnGroupIndices',val,self.currentIndex()
      return int(val[0]),int(val[1])
    else:
      return -1,-1

  def setCurrentColumnGroup(self,dataset,columnGroupText):
    #print 'CProgramColumnGroupCombo.setCurrentColumnGroup',dataset,columnGroupText
    ds = self.model().rootItem.findChild(QtCore.Qt.DisplayRole,dataset)
    #print 'CProgramColumnGroupCombo.setCurrentColumnGroup ds',ds
    if ds is None: return
    cg = ds.findChild(QtCore.Qt.DisplayRole,QtCore.QVariant(columnGroupText))
    #print 'CProgramColumnGroupCombo.setCurrentColumnGroup cg',cg
    if cg is not None:
      dsi = self.model().index(ds.row(),0, QtCore.QModelIndex())
      cgi = self.model().index(cg.row(),0,dsi)
      #print 'setCurrentColumnGroup',dsi.row(),cgi.row()
      #self.view().selectionModel().setCurrentIndex(cgi,QtGui.QItemSelectionModel.ClearAndSelect)
      #self.view().selectionModel().select(cgi,QtGui.QItemSelectionModel.ClearAndSelect)
      #self.view().selectionModel().emitSelectionChanged(QtGui.QItemSelection(cgi,cgi),QtGui.QItemSelection())
      #indx = self.findText(cg.data(QtCore.Qt.DisplayRole).toString().__str__())
      #print 'CProgramColumnGroupCombo.setCurrentColumnGroup combo indx',cg.data(QtCore.Qt.DisplayRole).toString(),indx
      self.setRootModelIndex( QtCore.QModelIndex())
      self.setCurrentIndex(cgi.row())
                      
  
class CProgramColumnGroupView(CCP4Widgets.CComplexLineWidget):

    MODEL_CLASS = CCP4XtalData.CProgramColumnGroup
    MARGIN = 0
    STRETCH = 5
    ERROR_CODES = { 101 : { 'description' : 'CProgramColumnGroup model not attached to a CMtzDataFile' },
                    102 : { 'description' : 'No CProgramColumnGroup model assigned for displaying MTZ column selection' },
                    103 : { 'description' : 'Error attempting to update display of MTZ columns' },
                    104 : { 'description' : 'Error number of MTZ columns in GUI does not match number in model' },
                    105 : { 'description' : 'MTZ file contents not available' } 
                    }

    def __init__(self,parent=None,model=None,qualifiers={},**kw):
      qualis = {}
      qualis.update(model.qualifiers())
      qualis.update(qualifiers)
      qualis.update(kw)
      qualis['gridLayout'] = True
      CCP4Widgets.CComplexLineWidget.__init__(self,parent,qualis)
      iconWidgetItem = self.layout().takeAt(0)
      iconWidgetItem.widget().deleteLater()
      self.widgets = []

      self.setModel(model)
        
    def setModel(self,model=None):
      #print 'CProgramColumnGroupView.setModel',model
      if model == self.model: return
      
      if model is None or isinstance(model,self.MODEL_CLASS):
        if self.model is not None: CCP4Widgets.CViewWidget.unsetModel(self)
        self.model = model

      '''
      if model is not None:
        self.connectUpdateViewFromModel(True)
        self.drawColumns()
        try:
          mtzDataObject = self.model.getDataByKey('mtzFileKey')
        except:
          mtzDataObject = None
        #print 'CProgramColumnGroupView.setModel mtzDataObject',mtzDataObject.objectName(),mtzDataObject.fileContent
        if mtzDataObject is not None:
          self.connect(mtzDataObject,QtCore.SIGNAL('dataChanged'),self.populateColumns)
          if mtzDataObject.fileContent is not None:
            if self.editable:
              self.populateColumns()
              #elf.updateViewFromModel()
              #self.updateFromFirstColumn()
            else:
              self.updateViewFromModel()
       '''
          

    def drawColumns(self,editable=None):
      if self.model is None: raise CException(self.__class__,102,name=self.modelObjectPath())
      n = -1
      toolTip = self.model.qualifiers('toolTip')
      if editable is not None: self.editable = editable
      #print 'CProgramColumnGroupView.drawColumns columnGroupNames', self.model.columnGroupNames()
      for name in self.model.columnGroupNames():
          n = n + 1
          label = QtGui.QLabel(name,self)
          if self.editable:
            self.widgets.append( CCP4Widgets.CComboBox(self,qualifiers=  { 'dragType' : self.dragType() }) )
            self.widgets[-1].setMinimumContentsLength(25)
            self.widgets[-1].setEditable(False)
            if toolTip is not NotImplemented: self.widgets[-1].setToolTip(toolTip)
          else:
            self.widgets.append(CCP4Widgets.CLabel(self , { 'charWidth' : 25, 'editable' :False,  'dragType' : self.dragType()  } ))
          row = n/2
          column = (n%2) * 2
          self.layout().addWidget(label,row,column+1)
          self.layout().addWidget(self.widgets[n],row,column+2)

          if self.editable:
            self.connect(self.widgets[-1],QtCore.SIGNAL('acceptDropData'),self.acceptDropData)
            # Just attempt to update all columns in sync with the first column
            if n == 0:
              self.connect(self.widgets[-1],QtCore.SIGNAL('currentIndexChanged(int)'),self.updateFromFirstColumn)
              #self.connect(self.widgets[-1],QtCore.SIGNAL('dataChanged'),self.updateFromFirstColumn)
            else:
              self.connect(self.widgets[-1],QtCore.SIGNAL('currentIndexChanged(int)'),self.updateModelFromView)
              #self.connect(self.widgets[-1],QtCore.SIGNAL('dataChanged'),self.updateModelFromView)
      
      if n%2 == 1:
        pass
     

    def populateColumns(self,listOfColumnGroups=[]):
      for widget in self.widgets:
        widget.blockSignals(True)
        widget.clear()
      if self.model is None:
        for widget in self.widgets: widget.blockSignals(False)
        return

      #for item in listOfColumnGroups: print 'populateColumns listOfColumnGroups',item.get()
      if len(listOfColumnGroups)==1:
        #load into a label widget
        for n in range(0,self.model.nColumns()):
          label = str(listOfColumnGroups[0].columnList[n].dataset)+'/'+str(listOfColumnGroups[0].columnList[n].columnLabel)
          self.widgets[n].setText(label)
      else:
        for n in range(0,self.model.nColumns()):
          for item in listOfColumnGroups:
            label = str(item.columnList[n].dataset)+'/'+str(item.columnList[n].columnLabel)
            self.widgets[n].addItem(label,QtCore.QVariant(label))

      for widget in self.widgets: widget.blockSignals(False)
      self.updateFromFirstColumn()

    #def updateFromFirstColumn(self,firstColumnIndex):
    def updateFromFirstColumn(self,indx=None):
      #print 'CProgramColumnGroupView.updateFromFirstColumn',indx
      if self.model is None:  raise CException(self.__class__,102,name=self.modelObjectPath())
      data = {}
      nameList = self.model.columnGroupNames()
      #print 'updateFromFirstColumn',self.model,type(self.model),nameList
      if self.editable:
        data[nameList[0]] = str(self.widgets[0].currentText())
      else:
        data[nameList[0]] = str(self.widgets[0].text())
      for name in nameList[1:]:
        data[name] = None
        #self.model.__setattr__(name,columnLabel)
      #print 'CProgramColumnGroupView.updateFromFirstColumn',self.model.objectName(),data
      self.model.set(data,fix=True)
      self.updateViewFromModel()

    def updateViewFromModel(self):
      if self.model is None: raise CException(self.__class__,102,name=self.modelObjectPath())
      #print 'CProgramColumnGroupView.updateViewFromModel',self.model.objectName(),self.model
      if len(self.widgets)<self.model.nColumns(): raise CException(self.__class__,103,name=self.model.objectName())
        
      columnGroupItems = self.model.columnGroupNames()
      data = self.model.get('guiLabels')
      #print 'CProgramColumnGroupView.updateViewFromModel',self.model.objectName(),data
      n = -1
      for name in columnGroupItems:
        n = n + 1
        self.widgets[n].blockSignals(True)
        #print 'CProgramColumnGroupView.updateViewFromModel',name,n, data.get(name,None),'widget:',self.widgets[n]
        if data.get(name,None) is not None:
          if self.editable:
            indx = self.widgets[n].findText(data[name])
            if indx>=0: self.widgets[n].setCurrentIndex(indx)
          else:
            #print 'CProgramColumnGroupView.updateViewFromModel',n,name,data[name]
            self.widgets[n].setValue(data[name])
        else:
          if self.editable:
            self.widgets[n].setCurrentIndex(self.widgets[n].count()-1)
          else:
             self.widgets[n].setValue('')
        self.widgets[n].blockSignals(False)

    def updateModelFromView(self,**kw):
      if self.model is None:  raise CException(self.__class__,102,name=self.modelObjectPath())
      if len(self.widgets)<self.model.nColumns():
        raise CException(self.__class__,104,name=self.modelObjectPath())
      n = -1
      data = {}
      for name in self.model.columnGroupNames():
        n = n + 1
        if self.editable:
          columnLabel = str(self.widgets[n].currentText())
        else:
          columnLabel = str(self.widgets[n].text())
        if columnLabel.count('/'):
          data[name] = columnLabel.split('/')[-1]
        else:
          data[name] = columnLabel
        #self.model.__setattr__(name,columnLabel)
      #print 'CProgramColumnGroupView.updateModelFromView',self.model.objectName(),data
      self.connectUpdateViewFromModel(False)
      self.model.set(data,fix=True)
      self.connectUpdateViewFromModel(True)
      self.validate()

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

    def validate(self,**kw):
      # Dummy routine for widget that should now be redundant
      return True
  
      



class CDatasetView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4XtalData.CDataset
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = { 'vboxLayout' : True,
               'iconName' : 'ObsDataFile',
               'dragType' :'ObsDataFile' }
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis)
    self.widgets = {}
    if model is None:
      selected = None
      obsDataFile = None
      crystalName = None
      datasetName = None
      formFactors = None
      formFactorSource = None
    else:
      selected = model.selected
      obsDataFile = model.obsDataFile
      crystalName = model.crystalName
      datasetName = model.datasetName
      formFactors = model.formFactors
      formFactorSource = model.formFactorSource
    self.widgets['selected']  = CCP4Widgets.CBooleanView(parent=self,model=selected)
    self.widgets['obsDataFile']  =CMiniMtzDataFileView(parent=self,model=obsDataFile,qualifiers={'jobCombo' : True,'dragType':'ObsDataFile'})
    self.widgets['crystalName'] = CCP4Widgets.CStringView(parent=self,model=crystalName)
    self.widgets['datasetName'] = CCP4Widgets.CStringView(parent=self,model=datasetName)
    self.widgets['formFactors'] = CFormFactorView(parent=self,model=formFactors)
    self.widgets['formFactorSource'] = CCP4Widgets.CStringView(parent=self,model=formFactorSource)
    self.layout().removeWidget(self.iconButton)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.iconButton)
    line.addWidget(self.widgets['obsDataFile'])
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['selected'])
    line.addWidget(QtGui.QLabel('Use this dataset.',self))
    line.addWidget(QtGui.QLabel('Crystal name',self))
    line.addWidget(self.widgets['crystalName'])
    line.addWidget(QtGui.QLabel('dataset name',self))
    line.addWidget(self.widgets['datasetName'])
    line.addStretch(1)
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['formFactors'])
    line.addWidget(QtGui.QLabel('set from (not working yet)',self))
    line.addWidget(self.widgets['formFactorSource'])
    line.addStretch(1)
    self.layout().addLayout(line)

class CDatasetListView(CCP4Widgets.CListView):
  MODEL_CLASS = CCP4XtalData.CDatasetList
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis =  { 'mode' : 'table',
                'tableItems' : ['file','crystalName','datasetName','formFactors'],
                'columnHeaders':['Observed data','Crystal','Dataset','Form factors'],
               }
    qualis.update(qualifiers)
    CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis)

    
class CAsuComponentView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4XtalData.CAsuComponent
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = { 'vboxLayout' : True,
                 'iconName' : 'SeqDataFile',
                 'dragType' : 'SeqDataFile' }
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis)
    self.widgets = {}
    #self.widgets['seqfile'] = CCP4Widgets.CSeqDataFileView(self,model=model.seqfile,qualifiers={'jobCombo' : False})
    if model is None:
      seqObj = None
      copiesObj = None
    else:
      seqObj = model.seqFile
      copiesObj = model.numberOfCopies
    self.widgets['seqFile']  = CCP4Widgets.CDataFileView(parent=self,model=seqObj,qualifiers={'jobCombo' : True,'dragType':'SeqDataFile'})
    self.widgets['numberOfCopies'] = CCP4Widgets.CIntView(parent=self,model=copiesObj,qualifiers= { 'editable' : self.editable, 'guiMode' : 'combo', 'enumerators' : [i for i in range(21)], 'menuText' : [str(i) for i in range(21)] })

    self.layout().removeWidget(self.iconButton)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.iconButton)
    #MN qualifiers dictionary provided has guiLabel efined as NotImplemented...intercept and provide value
    guiLabel = qualifiers.get('guiLabel','Number of copies in asymmetric unit:')
    if guiLabel is NotImplemented: guiLabel = 'Number of copies in asymmetric unit:'
    line.addWidget(QtGui.QLabel(guiLabel,self))
    line.addWidget(self.widgets['numberOfCopies'])
    line.addWidget(QtGui.QLabel('of sequence:',self))
    line.addStretch(2)
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['seqFile'])
    self.layout().addLayout(line)  
        
    self.setModel(model)

  def setModel(self,model):
    #print 'CAsuComponentView.setModel',model,self.model
    if self.model is not None:
      for item in ['seqFile','numberOfCopies']:
        self.widgets[item].connectUpdateViewFromModel(False)
    self.model = model
    if model is None:
      for item in ['seqFile','numberOfCopies']:
        self.widgets[item].setModel(None)
        self.widgets[item].setValue(None)
    else:
      for item in ['seqFile','numberOfCopies']:
        #print 'CAsuComponentView.setModel updating',item,type(self.widgets[item])
        self.widgets[item].setModel(self.model.get(item))
        self.widgets[item].updateViewFromModel()
        self.widgets[item].connectUpdateViewFromModel(True)
        
  def acceptDropData(self,textData):
    from lxml import etree
    tree = etree.fromstring(textData)
    #print 'CAsuComponentView.acceptDropData',textData,tree.tag
    self.widgets['seqFile'].connectUpdateViewFromModel(False)
    if tree.tag in ['SeqDataFile','seqFile']:
      self.model.seqFile.setEtree(tree)
    else:
      self.model.setEtree(tree)
    self.widgets['seqFile'].connectUpdateViewFromModel(True)
    self.updateViewFromModel()
    self.widgets['seqFile'].validate()

  '''
  def createMimeData(self):
    return self.widgets['seqFile'].createMimeData()
  '''

class CFormFactorView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4XtalData.CFormFactor
  def __init__(self,parent=None,model=None,qualifiers={}):
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualifiers)
    self.layout().takeAt(0)
    self.widgets['Fp'] = CCP4Widgets.CFloatView(self)
    self.widgets['Fpp'] = CCP4Widgets.CFloatView(self)
    if model is not None:
      print 'CFormFactorView.__init__',model
      self.widgets['Fp'].setModel(model.Fp)
      self.widgets['Fpp'].setModel(model.Fpp)
    self.layout().addWidget(QtGui.QLabel("Form factor f'",self))
    self.layout().addWidget(self.widgets['Fp'])
    self.layout().addWidget(QtGui.QLabel("f''",self))
    self.layout().addWidget(self.widgets['Fpp'])



class CImportUnmergedView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS = CCP4XtalData.CImportUnmerged
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = { 'vboxLayout' : True, 'iconName' : 'UnmergedDataFile' , 'dragType' :'UnmergedDataFile','dropTypes' : ['UnmergedDataFile','ImportUnmerged'] }
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis)
    self.widgets['crystalName'] = CCP4Widgets.CStringView(parent=self,qualifiers={})
    self.widgets['crystalName'].setToolTip('Short name for the crystal used to identify it throughout project')
    self.widgets['dataset'] = CCP4Widgets.CStringView(parent=self,qualifiers={})
    self.widgets['dataset'].setToolTip('Short name for the dataset used to identify it throughout project')
    self.widgets['file'] =CCP4Widgets.CDataFileView(parent=self,qualifiers={ 'ifInfo': True, 'mustExist' : True })
    self.widgets['cell'] = CCellView(parent=self)
    self.widgets['cell'].setToolTip('Enter the crystal cell that is not provided in the data file')
    self.widgets['wavelength'] = CCP4Widgets.CFloatView(parent=self,qualifiers={'charWidth' : 10 })
    self.widgets['wavelength'].setToolTip('Enter the wavelength that is not provided in the data file')
    self.widgets['excludeSelection'] = CCP4Widgets.CStringView(parent=self,qualifiers={})
    self.widgets['excludeSelection'].setToolTip("Enter lists and/or ranges of batches in form '7,9,12-13,21-23'")
    self.widgets['batchsInFile'] = CCP4Widgets.CLabel(self)
    self.widgets['batchsInFile'].setMinimumWidth(100)

    self.sameAsCombo = QtGui.QComboBox(self)
    self.connect(self.sameAsCombo,QtCore.SIGNAL('currentIndexChanged(int)'),self.handleSameAsCombo)
    
    line = QtGui.QHBoxLayout()
    iconWidgetItem = self.layout().takeAt(0)
    line.addWidget(iconWidgetItem.widget())
    line.addWidget(self.widgets['file'])
    self.layout().addLayout(line)

    self.cellFrame = QtGui.QFrame(self)
    self.cellFrame.setLayout(QtGui.QVBoxLayout())
    self.cellFrame.layout().setContentsMargins(0,0,0,0)
    self.cellFrame.layout().setSpacing(0)
    #self.cellFrame.setFrameShape(QtGui.QFrame.Box)
    self.cellFrame.layout().addWidget(QtGui.QLabel('Cell parameters'))
    self.cellFrame.layout().addWidget(self.widgets['cell'])
    self.layout().addWidget(self.cellFrame )
    self.cellFrame.hide()

    self.wLFrame =  QtGui.QFrame(self)
    self.wLFrame.setLayout(QtGui.QHBoxLayout())
    self.wLFrame.layout().setContentsMargins(0,0,0,0)
    self.wLFrame.layout().setSpacing(0)
    self.wLFrame.layout().addWidget(QtGui.QLabel('Wavelength'))
    self.wLFrame.layout().addWidget(self.widgets['wavelength'])
    self.wLFrame.layout().addStretch(2)
    self.layout().addWidget(self.wLFrame )
    self.wLFrame.hide()
    
    
    #line = QtGui.QHBoxLayout()
    #line.addWidget(QtGui.QLabel(
    #  'Assign crystal and dataset names which will be used to identify data in subsequent tasks',self))
    #self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Crystal name',self))
    line.addWidget(self.widgets['crystalName'])
    line.addWidget(QtGui.QLabel('dataset name',self))
    line.addWidget(self.widgets['dataset'])
    
    self.sameAsFrame = QtGui.QFrame(self)
    self.sameAsFrame.setLayout(QtGui.QHBoxLayout())
    self.sameAsFrame.layout().setContentsMargins(0,0,0,0)
    self.sameAsFrame.layout().setSpacing(0)
    self.sameAsFrame.layout().addWidget(QtGui.QLabel('OR same dataset as',self))
    self.sameAsFrame.layout().addWidget(self.sameAsCombo)
    line.addWidget(self.sameAsFrame)    
    line.addStretch(1)
    self.layout().addLayout(line)

    self.batchFrame =  QtGui.QFrame(self)
    self.batchFrame.setLayout(QtGui.QHBoxLayout())
    self.batchFrame.layout().setContentsMargins(0,0,0,0)
    self.batchFrame.layout().setSpacing(1)
    label = QtGui.QLabel('Batches in file:', self)
    italicFont = label.font()
    italicFont.setItalic(True)
    label.setFont(italicFont)
    self.batchFrame.layout().addWidget(label)
    self.batchFrame.layout().addWidget(self.widgets['batchsInFile'])
    self.layout().addWidget(self.batchFrame )
    self.batchFrame.hide()

    self.batchFrame2 =  QtGui.QFrame(self)
    self.batchFrame2.setLayout(QtGui.QHBoxLayout())
    self.batchFrame2.layout().setContentsMargins(0,0,0,0)
    self.batchFrame2.layout().setSpacing(1)
    label = QtGui.QLabel('Exclude batches from calculations and output', self)
    italicFont = label.font()
    italicFont.setItalic(True)
    label.setFont(italicFont)
    self.batchFrame2.layout().addWidget(label)
    self.batchFrame2.layout().addWidget(self.widgets['excludeSelection'])
    self.batchFrame2.layout().setStretchFactor(self.widgets['excludeSelection'],2)
    self.layout().addWidget(self.batchFrame2 )
    self.batchFrame2.hide()

    self.setModel(model)

  def setModel(self,model):
    #print 'CImportUnmergedView.setModel',repr(model),model
    itemList =  ['crystalName','dataset','file','excludeSelection','cell','wavelength']
    #itemList =  ['file','cell']
    if self.model is not None:
      for item in itemList:
        self.widgets[item].connectUpdateViewFromModel(False)
      self.disconnect(self.model,QtCore.SIGNAL('dataChanged'),self.validateNames)
      self.disconnect(self.model.file,QtCore.SIGNAL('dataChanged'),self.model.file.loadFile)
      self.disconnect(self.model.file,QtCore.SIGNAL('dataChanged'),self.model.loadDatasetName)
      self.disconnect(self.model.file.fileContent.knowncell,QtCore.SIGNAL('dataChanged'),self.toggleCellView)
      self.disconnect(self.model.file.fileContent.knownwavelength,QtCore.SIGNAL('dataChanged'),self.toggleWavelengthView)
      self.disconnect(self.model.file.fileContent.batchs,QtCore.SIGNAL('dataChanged'),self.toggleBatchView)
    if model is None:
      for item in  itemList:
        self.widgets[item].setModel(None)
        self.widgets[item].setValue(None)
    else:
      #data = model.get0()
      for item in itemList:
        self.widgets[item].setModel(model.get(item))
      #self.widgets['cell'].setModel(model.file.fileContent.cell)
      for item in itemList:
        self.widgets[item].updateViewFromModel()
        self.widgets[item].connectUpdateViewFromModel(True)
      self.sameAsFrame.setVisible(model.parent().index(model)!=0)
      
    self.model = model
    if self.model is not None:
      self.loadSameAsCombo()
      self.connect(self.model.file,QtCore.SIGNAL('dataChanged'),self.handleFileChanged)
      self.toggleCellView()
      self.toggleWavelengthView()
      self.toggleBatchView()

  def handleFileChanged(self):
    self.model.blockSignals(True)
    self.model.file.loadFile()
    self.model.loadDatasetName()
    self.toggleCellView()
    self.toggleWavelengthView()
    self.toggleBatchView()
    self.model.blockSignals(False)
    
      
  def validateNames(self):
    self.widgets['crystalName'].validate()
    self.widgets['dataset'].validate()

  def validate(self,isValid=None,reportMessage=True):
    excludeWidgets= []
    #print 'CImportUnmergedView.validate',isValid,self.model.file.fileContent.knowncell
    if self.model.file.fileContent.knowncell:
      excludeWidgets.append('cell')
      self.widgets['cell'].validate(True)
    if self.model.file.fileContent.knownwavelength:
      excludeWidgets.append('wavelength')
      self.widgets['wavelength'].validate(True)
    CCP4Widgets.CViewWidget.validate(self,isValid,excludeWidgets=excludeWidgets,reportMessage=reportMessage)

  def toggleCellView(self):
    if self.model.file.fileContent.knowncell.isSet() and not(self.model.file.fileContent.knowncell):
      self.cellFrame.show()
    else:
      self.cellFrame.hide()

  def toggleWavelengthView(self):
    if self.model.file.fileContent.knownwavelength.isSet() and not(self.model.file.fileContent.knownwavelength):
      self.wLFrame.show()
    else:
      self.wLFrame.hide()

  def hasBatchInfo(self):
    # return True if the file has batch information
    # format = ['unk','mtz' ,'xds', 'sca', 'saint', 'shelx']
    if ((self.model.file.fileContent.format == 'xds') or
        (self.model.file.fileContent.format == 'saint')):
      return True

    if ((self.model.file.fileContent.format == 'sca') or
        (self.model.file.fileContent.format == 'mtz')):
      # merged = ['unk','merged' ,'unmerged']
      if (self.model.file.fileContent.merged == 'unmerged'):
        return True
      return False

    # default no batch info
    return False
    
    #if (self.model.file.fileContent.batchs.isSet()
    #and (self.model.file.fileContent.batchs != '-1 - 1')):

  def toggleBatchView(self):
    #print "toggleBatchView", self.model.file.fileContent.batchs
    #print "toggleBatchView", self.model.file.fileContent.format
    #print "toggleBatchView", self.model.file.fileContent.merged

    if (self.hasBatchInfo()):
      self.batchFrame.show()
      self.batchFrame2.show()
    else:
      self.batchFrame.hide()
      self.batchFrame2.hide()

  def loadSameAsCombo(self):
    #print 'CImportUnmergedView.loadSameAsCombo',self.model
    if self.model is None: return
    indx = self.model.parent().index(self.model)
    self.sameAsCombo.blockSignals(True)
    self.sameAsCombo.clear()
    self.sameAsCombo.blockSignals(False)
    if indx<1: return
    self.sameAsCombo.addItem(' ')
    for item in self.model.parent()[0:len(self.model.parent())-1]:
      self.sameAsCombo.addItem(item.getTextItem())

  def handleSameAsCombo(self,indx):
    #print 'CImportUnmergedView.handleSameAsCombo',indx
    if indx==0: return
    try:
      self.model.dataset.set(self.model.parent()[indx-1].dataset.__str__())
      self.model.crystalName.set(self.model.parent()[indx-1].crystalName.__str__())
    except:
      pass
    

  def updateViewFromModel(self):
    #print 'CImportUnmergedView.updateViewFromModel'
    CCP4Widgets.CComplexLineWidget.updateViewFromModel(self)
    self.widgets['file'].updateViewFromModel()
    self.widgets['crystalName'].validate()
    self.widgets['dataset'].validate()
    self.widgets['wavelength'].validate()
    self.widgets['excludeSelection'].validate()
    if self.model is None:
      self.widgets['batchsInFile'].setValue('')
    else:
      self.widgets['batchsInFile'].setValue(self.model.file.fileContent.batchs)

  def getMenuDef(self):
    menu = self.widgets['file'].getMenuDef()
    #menu[menu.index('view')] = ['View','view_ViewHKL','view_text']
    return menu

  def openViewer(self,mode):
    if not self.model.file.exists(): return
    ext = self.model.file.getExt()
    if ext is None: return
    if ext == '.mtz' or mode == 'view_ViewHKL':
      CCP4Modules.LAUNCHER().openInViewer('viewHKL',fileName=self.model.file.__str__())
    else:      
      CCP4Modules.WEBBROWSER().openFile(self.model.file.__str__(),format='text/plain',toFront=True)

  # Just drag the file name
  def dragData(self):
    if self.model.file.isSet():
      tree = self.model.file.getEtree()
      tree.tag = 'UnmergedDataFile'
      from lxml import etree
      text = etree.tostring(tree,pretty_print=False)
      return text
    else:
      return None

  def dragType(self):
    return 'UnmergedDataFile'
  
  def acceptDropData(self,textData):
    from lxml import etree
    #print 'CImportUnmergedView.acceptDropData',textData
    tree = etree.fromstring(textData)
    self.connectUpdateViewFromModel(False)
    if tree.tag == 'UnmergedDataFile':
       self.model.file.unSet()
       self.model.file.setEtree(tree)
    elif  tree.tag == 'ImportUnmerged':
      self.model.unSet()
      self.model.setEtree(tree)
    self.connectUpdateViewFromModel(True)
    self.updateViewFromModel()
    self.validate()
    

class CImportUnmergedListView(CCP4Widgets.CListView):
  MODEL_CLASS = CCP4XtalData.CImportUnmergedList
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis =  { 'mode' : 'table',
                'tableItems' : ['file','crystalName','dataset','excludeSelection'],
                'columnHeaders':['Filename','Crystal','Dataset','Exclude batches'],
               }
    qualis.update(qualifiers)
    CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis)


class CMmcifReflDataFileView(CCP4Widgets.CDataFileView):
    MODEL_CLASS = CCP4XtalData.CMmcifReflDataFile
    def __init__(self,parent=None,model=None,qualifiers={}):
      qualis = {}
      qualis.update(qualifiers)
      CCP4Widgets.CDataFileView.__init__(self,parent=parent,model=model,qualifiers=qualis)
    
    def getMenuDef(self):
      return ['clear',['View','view_ViewHKL','view_text'],'sep','copy','paste','help']



class CMtzDataFileView(CCP4Widgets.CDataFileView):
  MODEL_CLASS = CCP4XtalData.CMtzDataFile

  def setModel(self,model=None):
    CCP4Widgets.CDataFileView.setModel(self,model=model)
    if self.model is not None:
        otherMtz = self.model.getDataByKey('sameCrystalAs')
        #print 'CMtzDataFileView.setModel connecting otherMtz',model.objectName(),repr(otherMtz)
        if otherMtz is not None:
          self.connect(otherMtz,QtCore.SIGNAL('dataChanged'),self.validate)

  
  def getMenuDef(self):
    #print 'CMtzDataFileView.getMenuDef'
    if isinstance(self.model,(CCP4XtalData.CObsDataFile,CCP4XtalData.CPhsDataFile,CCP4XtalData.CFreeRDataFile)):
      viewSubMenu = ['View','view_ViewHKL']
    else:
      viewSubMenu = ['View','view_ViewHKL','view_CCP4mg','view_Coot']
    if self.editable:
      menu = ['clear',viewSubMenu,'sep','copy','paste','help']
    else:
      if getattr(self,'role',None) is not None and self.role == 'output':
        menu = [viewSubMenu,'sep','copy','editLabel','export','help']
      else:
        menu = [viewSubMenu,'sep','copy','export','help']
    if getattr(self,'_stacked',False): menu.insert(0,'handleStack')
    return menu


class CMiniMtzDataFileView(CMtzDataFileView):
  MODEL_CLASS = CCP4XtalData.CMiniMtzDataFile

  def dropTypes(self):
    return ["MtzDataFile"]

  def jobNumber(self):
    return self.parentTaskWidget().jobNumber()
  
  def handleBrowserOpenFile(self,filename,downloadInfo={}):
    self.model.blockSignals(True)
    CMtzDataFileView.handleBrowserOpenFile(self,filename,downloadInfo=downloadInfo,autoInfoOnFileImport=False,validate=False)
    self.model.blockSignals(False)
    if self.model.getExt() in ['.cif','.ent']:
      err = self.model.importFromCif(jobId=self.parentTaskWidget().jobId())
      if err.maxSeverity()>SEVERITY_WARNING:
        err.warningMessage('Importing experimental data','Failed loading cif format file',parent=self)
        self.model.unSet()
        return
    errors = self.model.validColumns()
    #print 'CMiniMtzDataFileView.handleBrowserOpenFile',errors.report()
    #print 'CMiniMtzDataFileView.handleBrowserOpenFile sourceFileAnnotation',self.model.__dict__.get('sourceFileAnnotation','')
    if errors.maxSeverity()>SEVERITY_WARNING:
      #print errors.report()
      if errors.count(cls=self.model.__class__,code=206)>0:
        QtGui.QMessageBox.warning(self,'Error in selected MTZ file','This file contains unmerged data - use Data Reduction task to import it')
        self.model.unSet()
        return
      elif errors.count(cls=self.model.__class__,code=203)>0:
        QtGui.QMessageBox.warning(self,'Error in selected MTZ file','Selected MTZ file does not contain correct type of data')
        self.model.unSet()
        return
      elif errors.count(cls=self.model.__class__,code=204)>0 or errors.count(cls=self.model.__class__,code=205)>0:
        #applyNow = (errors.count(cls=self.model.__class__,code=205)>0)
        #print 'handleBrowserOpenFile applyNow',applyNow,filename
        try:
          self.dialog=CSelectColumnsWidget(parent=self,model=self.model,applyNow=False,filename=filename)
        except CException as e:
          mess = 'This file '+ filename + '\ndoes not contain the appropriate data: '
          for rC in self.model.requiredContent():
            mess = mess + self.model.CONTENT_ANNOTATION[rC-1]+','
          QtGui.QMessageBox.warning(self.parentTaskWidget(),'Error in selected MTZ file',mess[0:-1])
          self.model.unSet()
          return
        else:
          self.connect(self.dialog,QtCore.SIGNAL('apply'),self.handleDialogApply)
          self.connect(self.dialog,QtCore.SIGNAL('cancel'),self.handleDialogCancel)
    else:
      # Selected file has correct complement of columns but needs to be imported to same
      # target filename as used by self.model.splitMtz()
      self.model.importFile(jobId=self.parentTaskWidget().jobId(),jobNumber=self.parentTaskWidget().jobNumber())
      if CCP4Modules.PREFERENCES().AUTO_INFO_ON_FILE_IMPORT and not self.model.dbFileId.isSet():
        # If this is a downloaded file then it will have some provenance info in
        # self.model.sourceFileAnnotation put there by CDataFileView.handleBrowserOpenFile()
        self.openInfo(label=self.model.qualifiers('guiLabel').lower(),sourceFileAnnotation=self.model.__dict__.get('sourceFileAnnotation',''))

  def handleDialogApply(self):
    selectedColumns = self.dialog.getSelection()
    #print 'handleDialogApply',selectedColumns
    try:
      self.dialog.close()
    except:
      pass
    jobId = self.parentTaskWidget().jobId()
    projectId = self.parentTaskWidget().projectId()
    sourceFileName = self.model.__str__()
    error = self.model.splitMtz(jobId=jobId,projectId=projectId,contentFlag=selectedColumns[0],i2Labels=selectedColumns[1],columnLabels=selectedColumns[3])
    #print 'CMiniMtzDataFileView.handleDialogApply',error
    self.model.emitDataChanged()
    if error.maxSeverity()==SEVERITY_WARNING and error[0]['code']==212:
      mess = QtGui.QMessageBox.warning(self,self.windowTitle(),'This data is already imported as\n'+error[0]['details'])
      self.loadJobCombo()
      self.updateJobCombo()
      self.validate()
    elif error.maxSeverity()>=SEVERITY_WARNING:
      if error[0]['code']==211:
        mess = QtGui.QMessageBox.warning(self,self.windowTitle(),'No column data selected')
      else:
        error.warningMessage(windowTitle='Splitting MTZ: '+sourceFileName,jobId=jobId,parent=self)
      self.model.unSet()
      self.updateJobCombo()
      self.validate()
    else:
      self.loadJobCombo()
      self.updateJobCombo()
      self.validate()
      if CCP4Modules.PREFERENCES().AUTO_INFO_ON_FILE_IMPORT:
          #print 'CMiniMtzDaaFile.handleDialogApply sourceFileReference',self.model.__dict__.get('sourceFileReference','')
          # If this is a downloaded file then it will have some provenance info in
          # self.model.sourceFileAnnotation put there by CDataFileView.handleBrowserOpenFile()
          self.openInfo(label=self.model.qualifiers('guiLabel').lower()+' '+self.model.__dict__.get('sourceFileReference',''),
                          sourceFileAnnotation=self.model.__dict__.get('sourceFileAnnotation',''))
    
    self.dialog.deleteLater()
    
  def handleDialogCancel(self):
    self.dialog.close()
    self.dialog.deleteLater()
    self.model.unSet()



class CMergeMiniMtzView(CCP4Widgets.CComplexLineWidget):

  MODEL_CLASS =CCP4XtalData.CMergeMiniMtz

  def __init__(self,parent=None,model=None,qualifiers={},**kw):
    qualis = { 'vboxLayout' : True,
               'iconName' : 'MiniMtzDataFile',
               'dragType' : ['MiniMtzDataFile','ObsDataFile','PhsDataFile','FreeRDataFile','MapCoeffsDataFile'],
               'iconButton' : True }
    qualis.update(qualifiers)
    qualis.update(kw)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis,**kw)
    self.setFrameShape(QtGui.QFrame.Box)
    iconWidgetItem = self.layout().takeAt(0)

    qualis['iconButton'] =  False
    del qualis['vboxLayout']
    self.widgets['fileName'] = CMiniMtzDataFileView(parent=self,qualifiers=qualis)
    self.widgets['columnTag'] = CCP4Widgets.CStringView(parent=self,qualifiers=qualis)
    self.widgets['columnTag'].widget.setMaximumWidth(300)
    self.widgets['columnTag'].widget.setMinimumWidth(300)
    self.widgets['columnNames'] = CCP4Widgets.CStringView(parent=self,qualifiers=qualis)
    self.widgets['fileName'].setToolTip('Select an experimental data object')
    self.widgets['columnNames'].setToolTip('Comma-separated list of output column names - spaces will be ignored')
    
    line = QtGui.QHBoxLayout()
    line.addWidget(iconWidgetItem.widget())    
    line.addWidget( self.widgets['fileName'] )
    self.layout().addLayout( line )
    
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Tag for column names'))
    line.addWidget(self.widgets['columnTag'] )
    line.addWidget(QtGui.QLabel('(maximum 20 characters)'))
    line.addStretch(2)
    self.layout().addLayout( line )
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Output column names'))
    line.addWidget(self.widgets['columnNames'] )
    self.layout().addLayout( line )

    self.setModel(model)

  def openViewer(self,mode):
    self.widgets['fileName'].openViewer(mode)

  def setModel(self,model):
    CCP4Widgets.CComplexLineWidget.setModel(self,model)
    if model is None:
      self.widgets['fileName'].setModel(None)
      self.widgets['columnTag'].setModel(None)
      self.widgets['columnNames'].setModel(None)
    else:
      self.widgets['fileName'].setModel(model.fileName)
      self.widgets['columnTag'].setModel(model.columnTag)
      self.widgets['columnNames'].setModel(model.columnNames)
    # Beware the dataChanged signal from CDataFile may come before dbFileId is set
    #
    import functools
    self.connect(self.model.fileName.dbFileId,QtCore.SIGNAL('dataChanged'),functools.partial(self.updateColumnNames,True))
    self.connect(self.widgets['columnTag'].widget,QtCore.SIGNAL('editingFinished()'),functools.partial(self.updateColumnNames,False))
    #self.model.setColumnTag(overwrite=False)
    self.model.setColumnNames('applyTag',overwrite=False)

  def updateViewFromModel(self):
    if self.model is None: return
    for item in ['fileName','columnTag','columnNames']:
        self.widgets[item].updateViewFromModel()

  def updateModelFromView(self):
    if self.model is None: return
    for item in ['fileName','columnTag','columnNames']:
        self.widgets[item].updateModelFromView()
    

  def updateColumnNames(self,setColumnTag=False):
    #print 'CMergeMiniMtzView.updateColumnNames',setColumnTag
    if setColumnTag: self.model.setColumnTag(overwrite=True)
    self.model.setColumnNames('applyTag',overwrite=True)

  def getMenuDef(self):
    if self.editable:
      #menu = ['clear','view','annotate','sep','copy','paste','help']
      menu = ['clear','view_ViewHKL','sep','copy','paste','help']
    else:
      menu = ['view_ViewHKL','sep','copy','help']
    if self._stacked: menu.insert(0,'handleStack')
    return menu


  def acceptDropData(self,textData):
    #print 'CMergeMiniMtzView.acceptDropData',textData
    from lxml import etree
    tree = etree.fromstring(textData)
    tree.tag = self.dragType()
    #print 'CMergeMiniMtzView.acceptDropData',textData,tree.tag
    self.connectUpdateViewFromModel(False)
    self.model.fileName.unSet()
    self.model.fileName.setEtree(tree)
    #print 'CMergeMiniMtzView.acceptDropData model',self.model.fileName
    self.connectUpdateViewFromModel(True)
    self.updateViewFromModel()
    #print 'CMergeMiniMtzView.acceptDropData validity',self.model.validity(self.model.get()).report()
    self.validate()



class CMergeMiniMtzListView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS =CCP4XtalData.CMergeMiniMtzList
  def __init__(self,parent=None,model=None,qualifiers={},**kw):
    '''
    qualis =  { 'mode' : 'table',
                'tableItems' : ['fileName','columnNames'],
                'columnHeaders':['Filename','Column names']
               }
    
    qualis.update(qualifiers)
    CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis)
    self.listWidget.setColumnWidth(0,270)
    self.listWidget.setColumnWidth(1,270)
    '''
    qualis = { 'vboxLayout' : True,
               'iconButton' : False
                }
    qualis.update(qualifiers)
    qualis.update(kw)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis,**kw)
    self.widgets = []
    if self.editable:
      but = QtGui.QPushButton('Add input experimental data object',self)
      self.connect(but,QtCore.SIGNAL('released()'),self.handleAddObject)
      self.layout().addWidget(but)
    self.setModel(model)


  def setModel(self,model=None):
    self.model = model
    if model is None: return
    for item in model.__dict__['_value']:
      #print 'CMergeMiniMtzListView.setModel make widget for item',item
      self.widgets.append(CMergeMiniMtzView(self,model=item,qualifiers={'editable':self.editable}))
      self.layout().insertWidget(self.layout().count()-1,self.widgets[-1])

      
  def handleAddObject(self):
    self.model.addItem()
    self.widgets.append(CMergeMiniMtzView(self,model=self.model[-1],qualifiers={'editable':self.editable}))
    self.layout().insertWidget(self.layout().count()-1,self.widgets[-1])

  def setValue(self,value=[]):
    #print 'CMergeMiniMtzListView.setValue',value
    if len(value)>len(self.widgets):
      for mod in value[len(self.widgets):]:
        self.widgets.append(CMergeMiniMtzView(self,model=mod,qualifiers={'editable':self.editable}))
        self.layout().insertWidget(self.layout().count()-1,self.widgets[-1])
        #print 'CMergeMiniMtzListView.setValue',len(self.widgets),mod
    indx = 0
    for item in value:
      self.widgets[indx].setValue(item)
      indx += 1
    self.validate()

  def getValue(self,index=-1):
    #print 'CMergeMiniMtzListView.getValue NOT IMPLEMENTED'
    value = []
    if index<0:
      first = 0
      last = self.body.layout().count()
    else:
      first = index
      last = index + 1
    return value
    
  def updateViewFromModel(self):
    for w in self.widgets: w.updateViewFromModel()
    self.validate()

  def updateModelFromView(self):
    for w in self.widgets: w.updateModelFromView()


class CRunBatchRangeView(CCP4Widgets.CComplexLineWidget):
  MODEL_CLASS =CCP4XtalData.CRunBatchRange
  def __init__(self,parent=None,model=None,qualifiers={},**kw):
    qualis = {}
    qualis.update(qualifiers)
    qualis.update(**kw)
    #print 'CRunBatchRangeView',qualis
    CCP4Widgets.CComplexLineWidget. __init__(self,parent=parent,qualifiers=qualis,**kw)
    # Remove the icon which is not doing much for us
    icon = self.layout().takeAt(0).widget()
    icon.deleteLater()

    for item in ['runNumber','batchRange0','batchRange1','fileNumber']:
      self.widgets[item] = CCP4Widgets.CIntView(parent=self,qualifiers=qualis)
      self.connect(self.widgets[item], QtCore.SIGNAL('editingFinished()'),self.updateModelFromView)
      self.connect(self.widgets[item], QtCore.SIGNAL('enterKeyPress'),self.updateModelFromView)

    self.fileNumberLabel = QtGui.QLabel('file number',self)
    self.layout().addWidget(QtGui.QLabel('Run number',self))
    self.layout().addWidget( self.widgets['runNumber'] )
    self.layout().addWidget(self.fileNumberLabel)
    self.layout().addWidget( self.widgets['fileNumber'] )
    self.layout().addWidget(QtGui.QLabel('for batch range',self))
    self.layout().addWidget(self.widgets['batchRange0'])
    self.layout().addWidget(QtGui.QLabel('to',self))
    self.layout().addWidget(self.widgets['batchRange1'])
    self.layout().addStretch(1)

    self.showFileNumber(qualis.get('showFileNumber',False))
    if model is not None: self.setModel(model)

  def showFileNumber(self,mode):
    self.fileNumberLabel.setVisible(mode)
    self.widgets['fileNumber'].setVisible(mode)

  def setModel(self,model):
    # Reimplement to ensure this is validiated when any data item changes
    # (why is this not done in the base class?)
    if self.model is not None:
      for item in ['runNumber','batchRange0','batchRange1']:
        self.disconnect(self.model.__getattr__(item),QtCore.SIGNAL('dataChanged'),self.validate)      
    CCP4Widgets.CComplexLineWidget.setModel(self,model)
    if self.model is not None:
      for item in ['runNumber','batchRange0','batchRange1']:
        self.connect(self.model.__getattr__(item),QtCore.SIGNAL('dataChanged'),self.validate)


class CRunBatchRangeListView(CCP4Widgets.CListView):
  MODEL_CLASS =CCP4XtalData.CRunBatchRangeList
  def __init__(self,parent=None,model=None,qualifiers={},**kw):

    if qualifiers.get('showFileNumber',False):
        qualis =  { 'mode' : 'table',
                  'tableItems' : ['runNumber','fileNumber','batchRange0','batchRange1'],
                  'columnHeaders':['Run number','File number','Batch start','Batch end']
                 }
    else:  
      qualis =  { 'mode' : 'table',
                  'tableItems' : ['runNumber','batchRange0','batchRange1'],
                  'columnHeaders':['Run number','Batch start','Batch end']
                 }
    qualis.update(qualifiers)
    qualis.update(**kw)
    #print 'CRunBatchRangeListView',qualis
    
    CCP4Widgets.CListView.__init__(self,parent,model=model,qualifiers=qualis,editorQualifiers={ 'showFileNumber' :qualifiers.get('showFileNumber',False) } )

    
class CReindexOperatorView(CCP4Widgets.CComplexLineWidget):
   MODEL_CLASS = CCP4XtalData.CReindexOperator

   def __init__(self,parent=None,model=None,qualifiers={},**kw):
     CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualifiers)
     #self.layout().takeAt(0).widget().deleteLater()
     self.layout().addWidget(QtGui.QLabel('Reindex operator',self))
     for key in ['h','k','l']:
       self.widgets[key] = CCP4Widgets.CLineEdit(self)
       self.widgets[key].setToolTip('Enter operator for '+key)
       self.connect(self.widgets[key], QtCore.SIGNAL('editingFinished()'),self.updateModelFromView)
       self.connect(self.widgets[key], QtCore.SIGNAL('enterKeyPress'),self.updateModelFromView)
       self.layout().addWidget(QtGui.QLabel(key+'=',self))
       self.layout().addWidget(self.widgets[key])
       self.layout().setStretchFactor(self.widgets[key],2)

       if model is not None: self.setModel(model)

   def help(self):
     import CCP4Utils
     import os
     page = os.path.join(CCP4Utils.getCCP4Dir(),'html','reindexing.html')
     if not os.path.exists(page):
       page = os.path.join(CCP4Utils.getCCP4Dir(),'docs','reindexing.html')
     #print 'CReindexOperatorView.help',page,os.path.exists(page)
     if os.path.exists(page):
       CCP4Modules.WEBBROWSER().loadWebPage(fileName=page)


class CColumnGroupListView(CCP4Widgets.CComplexLineWidget):  
  MODEL_CLASS =CCP4XtalData.CColumnGroupList
  def __init__(self,parent=None,model=None,qualifiers={},**kw):
    import functools
    qualis = { 'vboxLayout' : True }
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.layout().takeAt(0).widget())
    line.addWidget(QtGui.QLabel('Select column groups to be imported as separate data objects',self))
    self.layout().addLayout(line)
    self.grid = QtGui.QGridLayout()
    self.layout().addLayout(self.grid)
    col=0
    for item in ['Dataset','Type','Column labels and types']:
      l = QtGui.QLabel(item,self)
      l.setObjectName('bold')
      col = col + 1
      self.grid.addWidget(l,0,col)
      
    line = QtGui.QHBoxLayout()
    #for item in ['Select all','Deselect all','Unique observed data']:
    for item in ['Select all importable','Deselect all']:
      button = QtGui.QPushButton(item,self)
      line.addWidget(button)
      self.connect(button,QtCore.SIGNAL('clicked()'),functools.partial(self.handleButton,item))
    line.addStretch(1)
    self.layout().addLayout(line)

    if model is not None: self.setModel(model)

  def handleButton(self,mode):
    #print 'CColumnGroupListView.handleButton',mode
    if mode == 'Unique observed data':
      pass
    else:
      flag = (mode == 'Select all importable')
      for row in range(1,self.grid.rowCount()):
        w = self.grid.itemAtPosition(row,0).widget()
        if not isinstance(w,CCP4Widgets.CCheckBoxUneditable):
          w.setChecked(flag)

  def clear(self):
    for row in range(1,self.grid.rowCount()):
      for col in (0,1,2,3):
        item = self.grid.itemAtPosition(row,col)
        if item is not None:
          item.widget().hide()
          item.widget().deleteLater()

  def updateViewFromModel(self):
    #print 'CColumnGroupListView.updateViewFromModel'
    import functools
    self.clear()
    
    for row in range(1,len(self.model.__dict__['_value'])+1):
      value = self.model.__dict__['_value'][row-1]
      #print 'CColumnGroupListView.updateViewFromModel',value
      if self.editable and len(value.columnGroupType)>0:
        cb = QtGui.QCheckBox(self)
        cb.setChecked(bool(value.selected))
        self.connect(cb,QtCore.SIGNAL('clicked(bool)'),functools.partial(self.handleClick,row))
      else:
        cb = CCP4Widgets.CCheckBoxUneditable(parent=self)
      self.grid.addWidget(cb,row,0)
      self.grid.addWidget(QtGui.QLabel(value.dataset.__str__(),self),row,1)
      self.grid.addWidget(QtGui.QLabel(value.columnGroupType.__str__(),self),row,2)
      self.grid.addWidget(QtGui.QLabel(value.columnListStr(),self),row,3)

  def handleClick(self,row,status):
    print 'handleClick',row,status
    self.model.blockSignals(True)
    self.model.__dict__['_value'][row-1].selected.set(status)
    self.model.blockSignals(False)
      
  def updateModelFromView(self):
    for row in range(1,self.grid.rowCount()):
      #print 'updateModelFromView', row,self.grid.itemAtPosition(row,0).widget()
      self.model.__dict__['_value'][row-1].selected.set(self.grid.itemAtPosition(row,0).widget().isChecked())

  def numberSelected(self):
    n = 0
    for row in range(1,len(self.model.__dict__['_value'])+1):
      if self.grid.itemAtPosition(row,0).widget().isChecked(): n += 1
    return n


class CGenericReflDataFileView(CMtzDataFileView):
  
  MODEL_CLASS = CCP4XtalData.CGenericReflDataFile

  def getMenuDef(self):
    viewSubMenu = ['View','view_ViewHKL','view_text']
    if self.editable:
      menu = ['clear',viewSubMenu,'sep','copy','paste','help']
    else:
      if self.role is not None and self.role == 'output':
        menu = [viewSubMenu,'sep','copy','editLabel','export','help']
      else:
        menu = [viewSubMenu,'sep','copy','export','help']
    if self._stacked: menu.insert(0,'handleStack')
    return menu



class CSelectColumnsWidget(QtGui.QDialog):

  ERROR_CODES = { 300 : { 'description' : 'There is no data of required type in MTZ file' }
                  }

  def __init__(self,parent=None,model=None,applyNow=False,filename=None):
    import os,functools
    import CCP4TaskWidget, CCP4DataManager
    QtGui.QDialog.__init__(self,parent)
    self.model = model
    baseName = str(self.model.baseName)
    if len(baseName)>0: baseName = os.path.splitext(baseName)[0]

    self.setModal(True)
    self.setWindowModality(QtCore.Qt.WindowModal)
    self.setWindowTitle('Select columns from MTZ file')
    self.setLayout(QtGui.QVBoxLayout())
    self.layout().setContentsMargins(3,3,3,3)
    self.layout().setSpacing(3)
    self.setMaximumWidth(CCP4TaskWidget.WIDTH)
    self.layout().addWidget(QtGui.QLabel('Extracting data from the file: '+filename,self))
    self.layout().addWidget(QtGui.QLabel('Select one set of columns for '+self.model.qualifiers('toolTip'),self))

    self.buttonGroup = QtGui.QButtonGroup(self)
    self.buttonGroup.setExclusive(True)
        
    columnGroupModelList = self.model.columnGroup()
    columnGroupsInFile = self.model.fileContent.getColumnGroups()
    #for item in columnGroupModelList:
    #  print 'CSelectColumnsWidget.__init__ columnGroupModelList',item.get()
    colGpType = self.model.__class__.__name__[1:-8]
    
    orLabel = 'Select '
    requiredContent = self.model.requiredContent()
    #print 'CSelectColumnsWidget.__init__ requiredContent',requiredContent
    if requiredContent is not None and 0 in requiredContent: requiredContent = None
    contentFlag = 0
    checked = True  
    nOfRequiredContent = 0
    for columnGroupModel in columnGroupModelList:
      contentFlag += 1
      columnGroupList = []
      for item in columnGroupsInFile:
        if item.columnGroupType == colGpType and item.contentFlag == contentFlag:
          columnGroupList.append(item)
      #for item in columnGroupList:
      #  print 'CSelectColumnsWidget.__init__ columnGroupList',contentFlag,item.get()
      if (requiredContent is None or contentFlag in requiredContent) and len(columnGroupList)>0:
        #print 'CSelectColumnsWidget.__init__ columnGroupModel',columnGroupModel
        nOfRequiredContent += 1
        self.layout().addWidget(QtGui.QLabel( orLabel + columnGroupModel.qualifiers('guiLabel').__str__().lower(),self))
        columnGroupWidget = CCP4DataManager.DATAMANAGER().getWidgetClass(model=columnGroupModel)(self,model=columnGroupModel)
        columnGroupWidget.setObjectName('contentFlag_'+str(contentFlag))
        radio = QtGui.QRadioButton(self)
        self.buttonGroup.addButton(radio)
        self.buttonGroup.setId(radio,contentFlag)
        radio.setChecked(checked)
        columnGroupWidget.layout().addWidget(radio,0,0)
        columnGroupWidget.drawColumns(len(columnGroupList)>1)
        columnGroupWidget.populateColumns(columnGroupList)
        self.layout().addWidget(columnGroupWidget)
        orLabel = '..or '
        checked= False
    #print 'CMiniMtzDataFileView.handleBrowserOpenFile',type(self.model),self.model.CONTENTS
    if nOfRequiredContent == 0:
        #There is no content of required type
        raise CException(self.__class__,300)
    if len(self.model.CONTENTS['subType']['qualifiers']['enumerators'])>0:
      line = QtGui.QHBoxLayout()
      subTypeWidget = CCP4DataManager.DATAMANAGER().getWidgetClass(model=self.model.subType)(self,model=self.model.subType,qualifiers=self.model.CONTENTS['subType']['qualifiers'])
      subTypeWidget.updateViewFromModel()
      line.addWidget(QtGui.QLabel('This is',self))
      line.addWidget(subTypeWidget)
      line.addStretch(1)
        
      self.layout().addLayout(line)

    line = QtGui.QHBoxLayout()
    self.annotationWidget = CCP4DataManager.DATAMANAGER().getWidgetClass(model=self.model.annotation)(self,model=self.model.annotation)
    self.annotationWidget.updateViewFromModel()
    line.addWidget(QtGui.QLabel('File label',self))
    line.addWidget(self.annotationWidget)
    self.layout().addLayout(line)

    self.setAnnotation()
    self.connect(self.buttonGroup,QtCore.SIGNAL('buttonClicked(int)'),self.setAnnotation)
    for columnGroupWidget in self.findChildren(CProgramColumnGroupView):
      self.connect(columnGroupWidget.model,QtCore.SIGNAL('dataChanged'),self.setAnnotation)
        
    buttonLine = QtGui.QHBoxLayout()
    buttonLine.addStretch(.5)
    buttonBox = QtGui.QDialogButtonBox(self)
    button = buttonBox.addButton(QtGui.QDialogButtonBox.Apply)
    self.connect(button,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('apply')))
    button = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(button,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('cancel')))
    button = buttonBox.addButton(QtGui.QDialogButtonBox.Help)
    self.connect(button,QtCore.SIGNAL('released()'),self.handleHelp)
    buttonLine.addWidget(buttonBox)
    buttonLine.addStretch(.5)
    self.layout().addLayout(buttonLine)
    if not applyNow:
      self.show()
    else:
      self.emit(QtCore.SIGNAL('apply'))
      # Correct column types - wrong column labels

  def setAnnotation(self,clickedInt=None):
    import os
    baseName = str(self.model.baseName)
    if len(baseName)>0: baseName = os.path.splitext(baseName)[0]
    
    rv = self.getSelection()
    if rv is None: return
    contentFlag,i2Names,dataset,colLabels = self.getSelection()
    label = ''
    for item in colLabels: label = label + item + ','

    #label =self.model.qualifiers('guiLabel')
      
    jN = self.parent().jobNumber()
    if jN is not None:
      jN = ' imported by job '+jN
    else:
      jN = ''
      
    #print 'setAnnotation',baseName,'*',dataset,'*',label,'*',jN
    #self.model.annotation.set(baseName + ' ' + dataset + '/'+label[0:-1] + jN)
    self.model.annotation.set(baseName + ': ' + dataset + jN)
     
    self.annotationWidget.updateViewFromModel()
    

  def getSelection(self):
    contentFlag =  self.buttonGroup.checkedId()
    columnGroupWidget = self.findChild(CProgramColumnGroupView,'contentFlag_'+str(contentFlag))
    if columnGroupWidget is None: return None

    colLabels = []
    for w in columnGroupWidget.widgets:
      #print 'CSelectColumnsWidget.getSelection currentText',w.currentIndex(),w.currentText()
      if isinstance(w,CCP4Widgets.CLabel):
        txt = w.text().__str__().split('/')
      else:
        txt = w.currentText().__str__().split('/')
      colLabels.append(txt[-1])
    if len(txt)>1:
      dataset = txt[0]
    else:
      dataset = ''
    #print 'getSelection',columnGroupWidget.model.columnGroupNames(),dataset,colLabels
    return contentFlag,columnGroupWidget.model.columnGroupNames(),dataset,colLabels
    

  def handleHelp(self):
    CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='data_files')

